diff --git a/libs/unimrcp/AUTHORS b/libs/unimrcp/AUTHORS new file mode 100644 index 0000000000..365eaf0026 --- /dev/null +++ b/libs/unimrcp/AUTHORS @@ -0,0 +1,12 @@ +Author(s): + Arsen Chaloyan + + +Contributor(s): + Kamil Shakirov + Anthony Masse + Vlad Socaciu + Garmt + Patrick + Bayram + Mahmoud Hassan \ No newline at end of file diff --git a/libs/unimrcp/INSTALL b/libs/unimrcp/INSTALL new file mode 100644 index 0000000000..50826f313a --- /dev/null +++ b/libs/unimrcp/INSTALL @@ -0,0 +1,103 @@ +BUILD REQUIREMENTS +================== +UniMRCP depends on a number of third party tools and libraries, +which must be installed prior to UniMRCP build. + +1. Apache Portable Runtime [>=1.2.x] (http://apr.apache.org/) +Whenever you want to build any part of UniMRCP, you need the +Apache Portable Runtime (APR) and the APR Utility (APR-util) +libraries. + +2. Sofia-SIP [>=1.12.6] (http://sofia-sip.sourceforge.net/) +Sofia-SIP library is used to implement MRCPv2 specification +compliant SIP signaling. Sofia-SIP is an open-source SIP User-Agent +library, compliant with the IETF RFC3261 specification. + +Use the link below to download one of known to work and +ready to use packages of APR and Sofia-SIP libraries. + http://www.unimrcp.org/dependencies/ + + +GNU BUILD +=================== +Additional requirements +- autoconf 2.57 or newer +- automake +- libtool 1.4 or newer +- gcc +- pkg-config + +Build procedure +$ ./bootstrap +$ ./configure +$ make +$ make install + +Installed directory layout +bin - binaries (unimrcpserver, unimrcpclient) +conf - configuration files +include - header files +libs - shared (convenient) libraries +plugins - run-time loadable modules + +There are a couple of options to "./configure". +To specify where to look for the APR and APR-util libraries +use the "--with-apr=" and "--with-apr-util=" options. +For example +$ ./configure --with-apr=/usr/local/apr \ + --with-apr-util=/usr/local/apr + +To specify where to look for the Sofia-SIP library +use the "--with-sofia-sip=" option. +For example +$ ./configure --with-sofia-sip=/usr/local/sofia-sip + +To install the default configuration use +$ make def-conf + +To generate doxygen documentation from the sources use +$ make dox + +To build distribution tarball use +$ make dist + + +WINDOWS BUILD +====================== +Additional requirements +- Microsoft Visual Studio 2005 + +One-time pre-build preparation +You may need to adjust the paths for 3-rd party libraries +in appropriate property sheets to match your local installation, +while below are the defaults (build/vsprops). + +apr.vsprops + + + +sofiasip.vsprops + + +Build procedure +Open unimrcp.sln solution file and build the solution (Build -> Build Solution). + +One-time pre-run output directory preparation +Build prepare.vcproj utility project (right click on tools -> prebuild in +Solution Explorer and select Build from context menu). This is a one-time +output directory preparation. It copies all the required APR and SofiaSIP +libraries and the default configuration to the output directory. + +Output directory layout +bin - binaries (unimrcpserver, unimrcpclient) and all the required dlls +conf - configuration files +plugins - run-time loadable modules \ No newline at end of file diff --git a/libs/unimrcp/LICENSE b/libs/unimrcp/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/libs/unimrcp/LICENSE @@ -0,0 +1,202 @@ + + 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. diff --git a/libs/unimrcp/Makefile.am b/libs/unimrcp/Makefile.am new file mode 100644 index 0000000000..a720714d6d --- /dev/null +++ b/libs/unimrcp/Makefile.am @@ -0,0 +1,42 @@ +macrodir = @ac_macro_dir@ +auxdir = @ac_aux_dir@ +AUX_DIST = $(auxdir)/config.guess \ + $(auxdir)/config.sub \ + $(auxdir)/install-sh \ + $(auxdir)/ltconfig \ + $(auxdir)/ltmain.sh \ + $(auxdir)/depcomp \ + $(auxdir)/missing +EXTRA_DIST = bootstrap + +AUTOMAKE_OPTIONS = foreign +MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure $(AUX_DIST) + +ACLOCAL = aclocal -I $(macrodir) + +SUBDIRS = libs modules plugins platforms build +if TEST_SUITES +SUBDIRS += tests +endif + +dox: + doxygen $(top_srcdir)/docs/doxygen.conf + +def-conf: + test -d $(confdir) || $(mkinstalldirs) $(confdir) + for conffile in `find conf -name \*.xml` ; do \ + filename=`echo $$conffile | sed -e 's|^.*/||'`; \ + $(INSTALL) -m 644 conf/$$filename $(confdir); \ + done + +def-data: + test -d $(datadir) || $(mkinstalldirs) $(datadir) + for datafile in `find data -name *.pcm -o -name *.xml` ; do \ + filename=`echo $$datafile | sed -e 's|^.*/||'`; \ + $(INSTALL) -m 644 data/$$filename $(datadir); \ + done + +install-data-local: + test -d $(confdir) || $(MAKE) def-conf + test -d $(datadir) || $(MAKE) def-data + test -d $(logdir) || $(mkinstalldirs) $(logdir) diff --git a/libs/unimrcp/README b/libs/unimrcp/README new file mode 100644 index 0000000000..bcd2daedd8 --- /dev/null +++ b/libs/unimrcp/README @@ -0,0 +1,51 @@ +INTRODUCTION +============ +UniMRCP - Open Source Media Resource Control Protocol Stack. + + +INSTALLATION +============ +See the file "INSTALLATION" for installation tips. + + +DOCUMENTATION +============= +See the directory "docs/dox" for doxygen generated documentation. +See the directory "docs/ea" for UML based design concepts + (Enterpise Architect generated HTML pages). + + +REFERENCES +========== +Website: + http://www.unimrcp.org + +Project Home: + http://code.google.com/p/unimrcp/ + +Wiki: + http://code.google.com/p/unimrcp/w/list + +Issue Tracker: + http://code.google.com/p/unimrcp/issues/list + +Discussion Group: + http://groups.google.com/group/unimrcp + +Version Control Repository (SVN): + http://unimrcp.googlecode.com/svn/trunk/ + +UML Documentation File (Enterpise Architect): + http://unimrcp.googlecode.com/svn/misc/unimrcp.eap + +Commit Monitor: + http://code.google.com/p/unimrcp/source/list + http://groups.google.com/group/unimrcp-svn-commits + + +LICENSING +========= +UniMRCP is licensed under terms of the Apache 2.0 license. +See the file "LICENSE" for more information. + +Copyright 2008 Arsen Chaloyan diff --git a/libs/unimrcp/bootstrap b/libs/unimrcp/bootstrap new file mode 100755 index 0000000000..e037f546a3 --- /dev/null +++ b/libs/unimrcp/bootstrap @@ -0,0 +1,14 @@ +#! /bin/sh + +case `uname` in + Darwin) libtoolize=glibtoolize ;; + *) libtoolize=libtoolize ;; +esac + +set -x +$libtoolize --force --automake --copy +aclocal -I build/acmacros +automake --foreign --add-missing --copy +autoconf + +rm -rf autom4te.cache diff --git a/libs/unimrcp/build/Makefile.am b/libs/unimrcp/build/Makefile.am new file mode 100644 index 0000000000..76d10e564a --- /dev/null +++ b/libs/unimrcp/build/Makefile.am @@ -0,0 +1,3 @@ +MAINTAINERCLEANFILES = Makefile.in + +SUBDIRS = pkgconfig diff --git a/libs/unimrcp/build/acmacros/apr.m4 b/libs/unimrcp/build/acmacros/apr.m4 new file mode 100644 index 0000000000..dc786c27e5 --- /dev/null +++ b/libs/unimrcp/build/acmacros/apr.m4 @@ -0,0 +1,44 @@ +dnl UNIMRCP_CHECK_APR + +AC_DEFUN([UNIMRCP_CHECK_APR], +[ + AC_MSG_NOTICE([Apache Portable Runtime (APR) library configuration]) + + APR_FIND_APR("", "", 1, 1) + + if test $apr_found = "no"; then + AC_MSG_WARN([APR not found]) + UNIMRCP_DOWNLOAD_APR + fi + + if test $apr_found = "reconfig"; then + AC_MSG_WARN([APR reconfig]) + fi + + dnl check APR version number + + apr_version="`$apr_config --version`" + AC_MSG_RESULT([$apr_version]) + + dnl Get build information from APR + + CPPFLAGS="$CPPFLAGS `$apr_config --cppflags`" + CFLAGS="$CFLAGS `$apr_config --cflags`" + LDFLAGS="$LDFLAGS `$apr_config --ldflags`" + + UNIMRCP_APR_INCLUDES="`$apr_config --includes`" + UNIMRCP_APR_LIBS="`$apr_config --link-libtool --libs`" + + AC_SUBST(UNIMRCP_APR_INCLUDES) + AC_SUBST(UNIMRCP_APR_LIBS) +]) + +dnl UNIMRCP_DOWNLOAD_APR +dnl no apr found, print out a message telling the user what to do +AC_DEFUN([UNIMRCP_DOWNLOAD_APR], +[ + echo "The Apache Portable Runtime (APR) library cannot be found." + echo "Please install APR on this system and supply the appropriate" + echo "--with-apr option to 'configure'" + AC_MSG_ERROR([no suitable APR found]) +]) diff --git a/libs/unimrcp/build/acmacros/apu.m4 b/libs/unimrcp/build/acmacros/apu.m4 new file mode 100644 index 0000000000..4d47f095f6 --- /dev/null +++ b/libs/unimrcp/build/acmacros/apu.m4 @@ -0,0 +1,42 @@ +dnl UNIMRCP_CHECK_APU + +AC_DEFUN([UNIMRCP_CHECK_APU], +[ + AC_MSG_NOTICE([Apache Portable Runtime Utility (APU) library configuration]) + + APR_FIND_APU("", "", 1, 1) + + if test $apu_found = "no"; then + AC_MSG_WARN([APU not found]) + UNIMRCP_DOWNLOAD_APU + fi + + if test $apu_found = "reconfig"; then + AC_MSG_WARN([APU reconfig]) + fi + + dnl check APU version number + + apu_version="`$apu_config --version`" + AC_MSG_RESULT([$apu_version]) + + dnl Get build information from APU + + LDFLAGS="$LDFLAGS `$apu_config --ldflags`" + + UNIMRCP_APU_INCLUDES="`$apu_config --includes`" + UNIMRCP_APU_LIBS="`$apu_config --link-libtool --libs`" + + AC_SUBST(UNIMRCP_APU_INCLUDES) + AC_SUBST(UNIMRCP_APU_LIBS) +]) + +dnl UNIMRCP_DOWNLOAD_APU +dnl no apr-util found, print out a message telling the user what to do +AC_DEFUN([UNIMRCP_DOWNLOAD_APU], +[ + echo "The Apache Portable Runtime Utility (APU) library cannot be found." + echo "Please install APRUTIL on this system and supply the appropriate" + echo "--with-apr-util option to 'configure'" + AC_MSG_ERROR([no suitable APU found]) +]) diff --git a/libs/unimrcp/build/acmacros/find_apr.m4 b/libs/unimrcp/build/acmacros/find_apr.m4 new file mode 100644 index 0000000000..048cb7bfc8 --- /dev/null +++ b/libs/unimrcp/build/acmacros/find_apr.m4 @@ -0,0 +1,167 @@ +dnl -------------------------------------------------------- -*- autoconf -*- +dnl Licensed to the Apache Software Foundation (ASF) under one or more +dnl contributor license agreements. See the NOTICE file distributed with +dnl this work for additional information regarding copyright ownership. +dnl The ASF licenses this file to You under the Apache License, Version 2.0 +dnl (the "License"); you may not use this file except in compliance with +dnl the License. You may obtain a copy of the License at +dnl +dnl http://www.apache.org/licenses/LICENSE-2.0 +dnl +dnl Unless required by applicable law or agreed to in writing, software +dnl distributed under the License is distributed on an "AS IS" BASIS, +dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +dnl See the License for the specific language governing permissions and +dnl limitations under the License. + +dnl +dnl find_apr.m4 : locate the APR include files and libraries +dnl +dnl This macro file can be used by applications to find and use the APR +dnl library. It provides a standardized mechanism for using APR. It supports +dnl embedding APR into the application source, or locating an installed +dnl copy of APR. +dnl +dnl APR_FIND_APR(srcdir, builddir, implicit-install-check, acceptable-majors) +dnl +dnl where srcdir is the location of the bundled APR source directory, or +dnl empty if source is not bundled. +dnl +dnl where builddir is the location where the bundled APR will will be built, +dnl or empty if the build will occur in the srcdir. +dnl +dnl where implicit-install-check set to 1 indicates if there is no +dnl --with-apr option specified, we will look for installed copies. +dnl +dnl where acceptable-majors is a space separated list of acceptable major +dnl version numbers. Often only a single major version will be acceptable. +dnl If multiple versions are specified, and --with-apr=PREFIX or the +dnl implicit installed search are used, then the first (leftmost) version +dnl in the list that is found will be used. Currently defaults to [0 1]. +dnl +dnl Sets the following variables on exit: +dnl +dnl apr_found : "yes", "no", "reconfig" +dnl +dnl apr_config : If the apr-config tool exists, this refers to it. If +dnl apr_found is "reconfig", then the bundled directory +dnl should be reconfigured *before* using apr_config. +dnl +dnl Note: this macro file assumes that apr-config has been installed; it +dnl is normally considered a required part of an APR installation. +dnl +dnl If a bundled source directory is available and needs to be (re)configured, +dnl then apr_found is set to "reconfig". The caller should reconfigure the +dnl (passed-in) source directory, placing the result in the build directory, +dnl as appropriate. +dnl +dnl If apr_found is "yes" or "reconfig", then the caller should use the +dnl value of apr_config to fetch any necessary build/link information. +dnl + +AC_DEFUN([APR_FIND_APR], [ + apr_found="no" + + if test "$target_os" = "os2-emx"; then + # Scripts don't pass test -x on OS/2 + TEST_X="test -f" + else + TEST_X="test -x" + fi + + ifelse([$4], [], [ + ifdef(AC_WARNING,AC_WARNING([$0: missing argument 4 (acceptable-majors): Defaulting to APR 0.x then APR 1.x])) + acceptable_majors="0 1"], + [acceptable_majors="$4"]) + + apr_temp_acceptable_apr_config="" + for apr_temp_major in $acceptable_majors + do + case $apr_temp_major in + 0) + apr_temp_acceptable_apr_config="$apr_temp_acceptable_apr_config apr-config" + ;; + *) + apr_temp_acceptable_apr_config="$apr_temp_acceptable_apr_config apr-$apr_temp_major-config" + ;; + esac + done + + AC_MSG_CHECKING(for APR) + AC_ARG_WITH(apr, + [ --with-apr=PATH prefix for installed APR, path to APR build tree, + or the full path to apr-config], + [ + if test "$withval" = "no" || test "$withval" = "yes"; then + AC_MSG_ERROR([--with-apr requires a directory or file to be provided]) + fi + + for apr_temp_apr_config_file in $apr_temp_acceptable_apr_config + do + for lookdir in "$withval/bin" "$withval" + do + if $TEST_X "$lookdir/$apr_temp_apr_config_file"; then + apr_found="yes" + apr_config="$lookdir/$apr_temp_apr_config_file" + break 2 + fi + done + done + + if test "$apr_found" != "yes" && $TEST_X "$withval" && $withval --help > /dev/null 2>&1 ; then + apr_found="yes" + apr_config="$withval" + fi + + dnl if --with-apr is used, it is a fatal error for its argument + dnl to be invalid + if test "$apr_found" != "yes"; then + AC_MSG_ERROR([the --with-apr parameter is incorrect. It must specify an install prefix, a build directory, or an apr-config file.]) + fi + ],[ + dnl If we allow installed copies, check those before using bundled copy. + if test -n "$3" && test "$3" = "1"; then + for apr_temp_apr_config_file in $apr_temp_acceptable_apr_config + do + if $apr_temp_apr_config_file --help > /dev/null 2>&1 ; then + apr_found="yes" + apr_config="$apr_temp_apr_config_file" + break + else + dnl look in some standard places + for lookdir in /usr /usr/local /usr/local/apr /opt/apr; do + if $TEST_X "$lookdir/bin/$apr_temp_apr_config_file"; then + apr_found="yes" + apr_config="$lookdir/bin/$apr_temp_apr_config_file" + break 2 + fi + done + fi + done + fi + dnl if we have not found anything yet and have bundled source, use that + if test "$apr_found" = "no" && test -d "$1"; then + apr_temp_abs_srcdir="`cd $1 && pwd`" + apr_found="reconfig" + apr_bundled_major="`sed -n '/#define.*APR_MAJOR_VERSION/s/^[^0-9]*\([0-9]*\).*$/\1/p' \"$1/include/apr_version.h\"`" + case $apr_bundled_major in + "") + AC_MSG_ERROR([failed to find major version of bundled APR]) + ;; + 0) + apr_temp_apr_config_file="apr-config" + ;; + *) + apr_temp_apr_config_file="apr-$apr_bundled_major-config" + ;; + esac + if test -n "$2"; then + apr_config="$2/$apr_temp_apr_config_file" + else + apr_config="$1/$apr_temp_apr_config_file" + fi + fi + ]) + + AC_MSG_RESULT($apr_found) +]) diff --git a/libs/unimrcp/build/acmacros/find_apu.m4 b/libs/unimrcp/build/acmacros/find_apu.m4 new file mode 100644 index 0000000000..e29bc60923 --- /dev/null +++ b/libs/unimrcp/build/acmacros/find_apu.m4 @@ -0,0 +1,176 @@ +dnl -------------------------------------------------------- -*- autoconf -*- +dnl Copyright 2002-2005 The Apache Software Foundation or its licensors, as +dnl applicable. +dnl +dnl Licensed under the Apache License, Version 2.0 (the "License"); +dnl you may not use this file except in compliance with the License. +dnl You may obtain a copy of the License at +dnl +dnl http://www.apache.org/licenses/LICENSE-2.0 +dnl +dnl Unless required by applicable law or agreed to in writing, software +dnl distributed under the License is distributed on an "AS IS" BASIS, +dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +dnl See the License for the specific language governing permissions and +dnl limitations under the License. + +dnl +dnl find_apu.m4 : locate the APR-util (APU) include files and libraries +dnl +dnl This macro file can be used by applications to find and use the APU +dnl library. It provides a standardized mechanism for using APU. It supports +dnl embedding APU into the application source, or locating an installed +dnl copy of APU. +dnl +dnl APR_FIND_APU(srcdir, builddir, implicit-install-check, acceptable-majors) +dnl +dnl where srcdir is the location of the bundled APU source directory, or +dnl empty if source is not bundled. +dnl +dnl where builddir is the location where the bundled APU will be built, +dnl or empty if the build will occur in the srcdir. +dnl +dnl where implicit-install-check set to 1 indicates if there is no +dnl --with-apr-util option specified, we will look for installed copies. +dnl +dnl where acceptable-majors is a space separated list of acceptable major +dnl version numbers. Often only a single major version will be acceptable. +dnl If multiple versions are specified, and --with-apr-util=PREFIX or the +dnl implicit installed search are used, then the first (leftmost) version +dnl in the list that is found will be used. Currently defaults to [0 1]. +dnl +dnl Sets the following variables on exit: +dnl +dnl apu_found : "yes", "no", "reconfig" +dnl +dnl apu_config : If the apu-config tool exists, this refers to it. If +dnl apu_found is "reconfig", then the bundled directory +dnl should be reconfigured *before* using apu_config. +dnl +dnl Note: this macro file assumes that apr-config has been installed; it +dnl is normally considered a required part of an APR installation. +dnl +dnl Note: At this time, we cannot find *both* a source dir and a build dir. +dnl If both are available, the build directory should be passed to +dnl the --with-apr-util switch. +dnl +dnl Note: the installation layout is presumed to follow the standard +dnl PREFIX/lib and PREFIX/include pattern. If the APU config file +dnl is available (and can be found), then non-standard layouts are +dnl possible, since it will be described in the config file. +dnl +dnl If a bundled source directory is available and needs to be (re)configured, +dnl then apu_found is set to "reconfig". The caller should reconfigure the +dnl (passed-in) source directory, placing the result in the build directory, +dnl as appropriate. +dnl +dnl If apu_found is "yes" or "reconfig", then the caller should use the +dnl value of apu_config to fetch any necessary build/link information. +dnl + +AC_DEFUN([APR_FIND_APU], [ + apu_found="no" + + if test "$target_os" = "os2-emx"; then + # Scripts don't pass test -x on OS/2 + TEST_X="test -f" + else + TEST_X="test -x" + fi + + ifelse([$4], [], + [ + ifdef(AC_WARNING,([$0: missing argument 4 (acceptable-majors): Defaulting to APU 0.x then APU 1.x])) + acceptable_majors="0 1" + ], [acceptable_majors="$4"]) + + apu_temp_acceptable_apu_config="" + for apu_temp_major in $acceptable_majors + do + case $apu_temp_major in + 0) + apu_temp_acceptable_apu_config="$apu_temp_acceptable_apu_config apu-config" + ;; + *) + apu_temp_acceptable_apu_config="$apu_temp_acceptable_apu_config apu-$apu_temp_major-config" + ;; + esac + done + + AC_MSG_CHECKING(for APR-util) + AC_ARG_WITH(apr-util, + [ --with-apr-util=PATH prefix for installed APU, path to APU build tree, + or the full path to apu-config], + [ + if test "$withval" = "no" || test "$withval" = "yes"; then + AC_MSG_ERROR([--with-apr-util requires a directory or file to be provided]) + fi + + for apu_temp_apu_config_file in $apu_temp_acceptable_apu_config + do + for lookdir in "$withval/bin" "$withval" + do + if $TEST_X "$lookdir/$apu_temp_apu_config_file"; then + apu_found="yes" + apu_config="$lookdir/$apu_temp_apu_config_file" + break 2 + fi + done + done + + if test "$apu_found" != "yes" && $TEST_X "$withval" && $withval --help > /dev/null 2>&1 ; then + apu_found="yes" + apu_config="$withval" + fi + + dnl if --with-apr-util is used, it is a fatal error for its argument + dnl to be invalid + if test "$apu_found" != "yes"; then + AC_MSG_ERROR([the --with-apr-util parameter is incorrect. It must specify an install prefix, a build directory, or an apu-config file.]) + fi + ],[ + if test -n "$3" && test "$3" = "1"; then + for apu_temp_apu_config_file in $apu_temp_acceptable_apu_config + do + if $apu_temp_apu_config_file --help > /dev/null 2>&1 ; then + apu_found="yes" + apu_config="$apu_temp_apu_config_file" + break + else + dnl look in some standard places (apparently not in builtin/default) + for lookdir in /usr /usr/local /usr/local/apr /opt/apr /usr/local/apache2 ; do + if $TEST_X "$lookdir/bin/$apu_temp_apu_config_file"; then + apu_found="yes" + apu_config="$lookdir/bin/$apu_temp_apu_config_file" + break 2 + fi + done + fi + done + fi + dnl if we have not found anything yet and have bundled source, use that + if test "$apu_found" = "no" && test -d "$1"; then + apu_temp_abs_srcdir="`cd $1 && pwd`" + apu_found="reconfig" + apu_bundled_major="`sed -n '/#define.*APU_MAJOR_VERSION/s/^[^0-9]*\([0-9]*\).*$/\1/p' \"$1/include/apu_version.h\"`" + case $apu_bundled_major in + "") + AC_MSG_ERROR([failed to find major version of bundled APU]) + ;; + 0) + apu_temp_apu_config_file="apu-config" + ;; + *) + apu_temp_apu_config_file="apu-$apu_bundled_major-config" + ;; + esac + if test -n "$2"; then + apu_config="$2/$apu_temp_apu_config_file" + else + apu_config="$1/$apu_temp_apu_config_file" + fi + fi + ]) + + AC_MSG_RESULT($apu_found) +]) diff --git a/libs/unimrcp/build/acmacros/sofia-sip.m4 b/libs/unimrcp/build/acmacros/sofia-sip.m4 new file mode 100644 index 0000000000..aefc06251f --- /dev/null +++ b/libs/unimrcp/build/acmacros/sofia-sip.m4 @@ -0,0 +1,49 @@ +dnl UNIMRCP_CHECK_SOFIA + +AC_DEFUN([UNIMRCP_CHECK_SOFIA], +[ + AC_MSG_NOTICE([Sofia SIP library configuration]) + + AC_MSG_CHECKING([for Sofia-SIP]) + AC_ARG_WITH(sofia-sip, + [ --with-sofia-sip=PATH prefix for installed Sofia-SIP or + path to Sofia-SIP build tree], + [sofia_path=$withval], + [sofia_path="/usr/local"] + ) + + sofiaconfig="lib/pkgconfig/sofia-sip-ua.pc" + sofiasrcdir="libsofia-sip-ua" + for dir in $sofia_path ; do + cd $dir && sofiadir=`pwd` && cd - > /dev/null + if test -f "$dir/$sofiaconfig"; then + found_sofia="yes" + UNIMRCP_SOFIA_INCLUDES="`pkg-config --cflags $dir/$sofiaconfig`" + UNIMRCP_SOFIA_LIBS="`pkg-config --libs $dir/$sofiaconfig`" + sofia_version="`pkg-config --modversion $dir/$sofiaconfig`" + break + fi + if test -d "$dir/$sofiasrcdir"; then + found_sofia="yes" + UNIMRCP_SOFIA_INCLUDES="-I$sofiadir/$sofiasrcdir -I$sofiadir/$sofiasrcdir/bnf -I$sofiadir/$sofiasrcdir/features -I$sofiadir/$sofiasrcdir/http -I$sofiadir/$sofiasrcdir/ipt -I$sofiadir/$sofiasrcdir/iptsec -I$sofiadir/$sofiasrcdir/msg -I$sofiadir/$sofiasrcdir/nea -I$sofiadir/$sofiasrcdir/nta -I$sofiadir/$sofiasrcdir/nth -I$sofiadir/$sofiasrcdir/nua -I$sofiadir/$sofiasrcdir/sdp -I$sofiadir/$sofiasrcdir/sip -I$sofiadir/$sofiasrcdir/soa -I$sofiadir/$sofiasrcdir/sresolv -I$sofiadir/$sofiasrcdir/stun -I$sofiadir/$sofiasrcdir/su -I$sofiadir/$sofiasrcdir/tport -I$sofiadir/$sofiasrcdir/url" + UNIMRCP_SOFIA_LIBS="$sofiadir/$sofiasrcdir/libsofia-sip-ua.la" + sofia_version="`pkg-config --modversion $sofiadir/packages/sofia-sip-ua.pc`" + break + fi + done + + if test x_$found_sofia != x_yes; then + AC_MSG_ERROR(Cannot find Sofia-SIP - looked for sofia-config:$sofiaconfig and srcdir:$sofiasrcdir in $sofia_path) + else + AC_MSG_RESULT([$found_sofia]) + AC_MSG_RESULT([$sofia_version]) + +case "$host" in + *darwin*) + UNIMRCP_SOFIA_LIBS="$UNIMRCP_SOFIA_LIBS -framework CoreFoundation -framework SystemConfiguration" ;; +esac + + AC_SUBST(UNIMRCP_SOFIA_INCLUDES) + AC_SUBST(UNIMRCP_SOFIA_LIBS) + fi +]) diff --git a/libs/unimrcp/build/acmacros/swift.m4 b/libs/unimrcp/build/acmacros/swift.m4 new file mode 100644 index 0000000000..c3730b9fbc --- /dev/null +++ b/libs/unimrcp/build/acmacros/swift.m4 @@ -0,0 +1,30 @@ +dnl UNIMRCP_CHECK_SWIFT + +AC_DEFUN([UNIMRCP_CHECK_SWIFT], +[ + AC_MSG_NOTICE([Cepstral Swift library configuration]) + + AC_MSG_CHECKING([for Swift]) + AC_ARG_WITH(swift, + [ --with-swift=PATH prefix for installed Swift], + [swift_path=$withval], + [swift_path="/opt/swift"] + ) + + if test -d "$swift_path"; then + found_swift="yes" + UNIMRCP_SWIFT_INCLUDES="-I$swift_path/include" + UNIMRCP_SWIFT_LIBS="-lswift -lceplex_us -lceplang_en -lm" + UNIMRCP_SWIFT_LDFLAGS="-L$swift_path/lib/ -R$swift_path/lib/" + + AC_SUBST(UNIMRCP_SWIFT_INCLUDES) + AC_SUBST(UNIMRCP_SWIFT_LIBS) + AC_SUBST(UNIMRCP_SWIFT_LDFLAGS) + + AC_MSG_RESULT($swift_path) + else + AC_MSG_WARN([not found - looked for $swift_path]) + fi + + AM_CONDITIONAL([CEPSTRAL_PLUGIN], [test x_$found_swift = x_yes]) +]) diff --git a/libs/unimrcp/build/get-version.sh b/libs/unimrcp/build/get-version.sh new file mode 100755 index 0000000000..fd685b22a8 --- /dev/null +++ b/libs/unimrcp/build/get-version.sh @@ -0,0 +1,37 @@ +#!/bin/sh +# +# extract version numbers from a header file +# +# USAGE: get-version.sh CMD VERSION_HEADER PREFIX +# where CMD is one of: all, major, libtool +# where PREFIX is the prefix to {MAJOR|MINOR|PATCH}_VERSION defines +# +# get-version.sh all returns a dotted version number +# get-version.sh major returns just the major version number +# get-version.sh libtool returns a version "libtool -version-info" format +# + +if test $# != 3; then + echo "USAGE: $0 CMD VERSION_HEADER PREFIX" + echo " where CMD is one of: all, major, libtool" + exit 1 +fi + +major_sed="/#define.*$3_MAJOR_VERSION/s/^[^0-9]*\([0-9]*\).*$/\1/p" +minor_sed="/#define.*$3_MINOR_VERSION/s/^[^0-9]*\([0-9]*\).*$/\1/p" +patch_sed="/#define.*$3_PATCH_VERSION/s/^[^0-9]*\([0-9]*\).*$/\1/p" +major="`sed -n $major_sed $2`" +minor="`sed -n $minor_sed $2`" +patch="`sed -n $patch_sed $2`" + +if test "$1" = "all"; then + echo ${major}.${minor}.${patch} +elif test "$1" = "major"; then + echo ${major} +elif test "$1" = "libtool"; then + # Yes, ${minor}:${patch}:${minor} is correct due to libtool idiocy. + echo ${minor}:${patch}:${minor} +else + echo "ERROR: unknown version CMD ($1)" + exit 1 +fi diff --git a/libs/unimrcp/build/pkgconfig/Makefile.am b/libs/unimrcp/build/pkgconfig/Makefile.am new file mode 100644 index 0000000000..ce3a063c05 --- /dev/null +++ b/libs/unimrcp/build/pkgconfig/Makefile.am @@ -0,0 +1,4 @@ +MAINTAINERCLEANFILES = Makefile.in + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = unimrcpclient.pc unimrcpserver.pc unimrcpplugin.pc diff --git a/libs/unimrcp/build/pkgconfig/unimrcpclient.pc.in b/libs/unimrcp/build/pkgconfig/unimrcpclient.pc.in new file mode 100644 index 0000000000..7fe3f1c130 --- /dev/null +++ b/libs/unimrcp/build/pkgconfig/unimrcpclient.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ @UNIMRCP_APR_INCLUDES@ + +Name: unimrcpclient +Description: UniMRCP Client Stack +Requires: +Version: @UNI_DOTTED_VERSION@ +Libs: -L${libdir} -lunimrcpclient +Cflags: @CPPFLAGS@ @CFLAGS@ -I${includedir} diff --git a/libs/unimrcp/build/pkgconfig/unimrcpplugin.pc.in b/libs/unimrcp/build/pkgconfig/unimrcpplugin.pc.in new file mode 100644 index 0000000000..df78b8ea7f --- /dev/null +++ b/libs/unimrcp/build/pkgconfig/unimrcpplugin.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ @UNIMRCP_APR_INCLUDES@ + +Name: unimrcpplugin +Description: UniMRCP Server Plugin +Requires: +Version: @UNI_DOTTED_VERSION@ +Libs: -L${libdir} -lunimrcpserver +Cflags: @CPPFLAGS@ @CFLAGS@ -I${includedir} diff --git a/libs/unimrcp/build/pkgconfig/unimrcpserver.pc.in b/libs/unimrcp/build/pkgconfig/unimrcpserver.pc.in new file mode 100644 index 0000000000..3ed91ecd02 --- /dev/null +++ b/libs/unimrcp/build/pkgconfig/unimrcpserver.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ @UNIMRCP_APR_INCLUDES@ + +Name: unimrcpserver +Description: UniMRCP Server Stack +Requires: +Version: @UNI_DOTTED_VERSION@ +Libs: -L${libdir} -lunimrcpserver +Cflags: @CPPFLAGS@ @CFLAGS@ -I${includedir} diff --git a/libs/unimrcp/build/tools/prepare.vcproj b/libs/unimrcp/build/tools/prepare.vcproj new file mode 100644 index 0000000000..c296a0efcd --- /dev/null +++ b/libs/unimrcp/build/tools/prepare.vcproj @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/build/tools/unimrcp_service.c b/libs/unimrcp/build/tools/unimrcp_service.c new file mode 100644 index 0000000000..b8a1bd7a2d --- /dev/null +++ b/libs/unimrcp/build/tools/unimrcp_service.c @@ -0,0 +1,235 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include +#include "apt.h" +#include "apt_pool.h" + +#define WIN_SERVICE_NAME "unimrcp" + + +/** Register/install service in SCM */ +static apt_bool_t uni_service_register(const char *root_dir_path, apr_pool_t *pool) +{ + char *bin_path; + SERVICE_DESCRIPTION desc; + SC_HANDLE sch_service; + SC_HANDLE sch_manager = OpenSCManager(0,0,SC_MANAGER_ALL_ACCESS); + if(!sch_manager) { + printf("Failed to Open SCManager %d\n", GetLastError()); + return FALSE; + } + + bin_path = apr_psprintf(pool,"%s\\bin\\unimrcpserver.exe --service --root-dir \"%s\" -o 2", + root_dir_path, + root_dir_path); + sch_service = CreateService( + sch_manager, + WIN_SERVICE_NAME, + "UniMRCP Server", + GENERIC_EXECUTE | SERVICE_CHANGE_CONFIG, + SERVICE_WIN32_OWN_PROCESS, + SERVICE_DEMAND_START, + SERVICE_ERROR_NORMAL, + bin_path,0,0,0,0,0); + if(!sch_service) { + printf("Failed to Create Service %d\n", GetLastError()); + CloseServiceHandle(sch_manager); + return FALSE; + } + + desc.lpDescription = "Launches UniMRCP Server"; + if(!ChangeServiceConfig2(sch_service,SERVICE_CONFIG_DESCRIPTION,&desc)) { + printf("Failed to Set Service Description %d\n", GetLastError()); + } + + CloseServiceHandle(sch_service); + CloseServiceHandle(sch_manager); + return TRUE; +} + +/** Unregister/uninstall service from SCM */ +static apt_bool_t uni_service_unregister() +{ + apt_bool_t status = TRUE; + SERVICE_STATUS ss_status; + SC_HANDLE sch_service; + SC_HANDLE sch_manager = OpenSCManager(0,0,SC_MANAGER_ALL_ACCESS); + if(!sch_manager) { + printf("Failed to Open SCManager %d\n", GetLastError()); + return FALSE; + } + + sch_service = OpenService(sch_manager,WIN_SERVICE_NAME,DELETE|SERVICE_STOP); + if(!sch_service) { + printf("Failed to Open Service %d\n", GetLastError()); + CloseServiceHandle(sch_manager); + return FALSE; + } + + ControlService(sch_service,SERVICE_CONTROL_STOP,&ss_status); + if(!DeleteService(sch_service)) { + printf("Failed to Delete Service %d\n", GetLastError()); + status = FALSE; + } + CloseServiceHandle(sch_service); + CloseServiceHandle(sch_manager); + return status; +} + +/** Start service */ +static apt_bool_t uni_service_start() +{ + apt_bool_t status = TRUE; + SC_HANDLE sch_service; + SC_HANDLE sch_manager = OpenSCManager(0,0,SC_MANAGER_ALL_ACCESS); + if(!sch_manager) { + printf("Failed to Open SCManager %d\n", GetLastError()); + return FALSE; + } + + sch_service = OpenService(sch_manager,WIN_SERVICE_NAME,SERVICE_START); + if(!sch_service) { + printf("Failed to Open Service %d\n", GetLastError()); + CloseServiceHandle(sch_manager); + return FALSE; + } + + if(!StartService(sch_service,0,NULL)) { + printf("Failed to Start Service %d\n", GetLastError()); + status = FALSE; + } + CloseServiceHandle(sch_service); + CloseServiceHandle(sch_manager); + return status; +} + +/** Stop service */ +static apt_bool_t uni_service_stop() +{ + apt_bool_t status = TRUE; + SERVICE_STATUS ss_status; + SC_HANDLE sch_service; + SC_HANDLE sch_manager = OpenSCManager(0,0,SC_MANAGER_ALL_ACCESS); + if(!sch_manager) { + printf("Failed to Open SCManager %d\n", GetLastError()); + return FALSE; + } + + sch_service = OpenService(sch_manager,WIN_SERVICE_NAME,SERVICE_STOP); + if(!sch_service) { + printf("Failed to Open Service %d\n", GetLastError()); + CloseServiceHandle(sch_manager); + return FALSE; + } + + if(!ControlService(sch_service,SERVICE_CONTROL_STOP,&ss_status)) { + printf("Failed to Stop Service %d\n", GetLastError()); + status = FALSE; + } + + CloseServiceHandle(sch_service); + CloseServiceHandle(sch_manager); + return status; +} + + +static void usage() +{ + printf( + "\n" + "Usage:\n" + "\n" + " unimrcpservice [options]\n" + "\n" + " Available options:\n" + "\n" + " -r [--register] rootdir : Register the Windows service.\n" + "\n" + " -u [--unregister] : Unregister the Windows service.\n" + "\n" + " -s [--start] : Start the Windows service.\n" + "\n" + " -t [--stop] : Stop the Windows service.\n" + "\n" + " -h [--help] : Show the help.\n" + "\n"); +} + +int main(int argc, const char * const *argv) +{ + apr_pool_t *pool; + apr_status_t rv; + apr_getopt_t *opt; + + static const apr_getopt_option_t opt_option[] = { + /* long-option, short-option, has-arg flag, description */ + { "register", 'r', TRUE, "register service" }, /* -r or --register arg */ + { "unregister", 'u', FALSE, "unregister service" },/* -u or --unregister */ + { "start", 's', FALSE, "start service" }, /* -s or --start */ + { "stop", 't', FALSE, "stop service" }, /* -t or --stop */ + { "help", 'h', FALSE, "show help" }, /* -h or --help */ + { NULL, 0, 0, NULL }, /* end */ + }; + + /* APR global initialization */ + if(apr_initialize() != APR_SUCCESS) { + apr_terminate(); + return 0; + } + + /* create APR pool */ + pool = apt_pool_create(); + if(!pool) { + apr_terminate(); + return 0; + } + + rv = apr_getopt_init(&opt, pool , argc, argv); + if(rv == APR_SUCCESS) { + int optch; + const char *optarg; + while((rv = apr_getopt_long(opt, opt_option, &optch, &optarg)) == APR_SUCCESS) { + switch(optch) { + case 'r': + uni_service_register(optarg,pool); + break; + case 'u': + uni_service_unregister(); + break; + case 's': + uni_service_start(); + break; + case 't': + uni_service_stop(); + break; + case 'h': + usage(); + break; + } + } + if(rv != APR_EOF) { + usage(); + } + } + + /* destroy APR pool */ + apr_pool_destroy(pool); + /* APR global termination */ + apr_terminate(); + return 0; +} diff --git a/libs/unimrcp/build/tools/unimrcpservice.exe.manifest b/libs/unimrcp/build/tools/unimrcpservice.exe.manifest new file mode 100644 index 0000000000..3a14ebfb75 --- /dev/null +++ b/libs/unimrcp/build/tools/unimrcpservice.exe.manifest @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/libs/unimrcp/build/tools/unimrcpservice.vcproj b/libs/unimrcp/build/tools/unimrcpservice.vcproj new file mode 100644 index 0000000000..abddf90538 --- /dev/null +++ b/libs/unimrcp/build/tools/unimrcpservice.vcproj @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/build/uni_version.h b/libs/unimrcp/build/uni_version.h new file mode 100644 index 0000000000..b453927d09 --- /dev/null +++ b/libs/unimrcp/build/uni_version.h @@ -0,0 +1,66 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __UNI_VERSION_H__ +#define __UNI_VERSION_H__ + +/** + * @file uni_version.h + * @brief UniMRCP Version Numbering + * + * UniMRCP version numbering is derived from APR project specified in: + * + * http://apr.apache.org/versioning.html + */ + +#include + +/** major version + * Major API changes that could cause compatibility problems for older + * programs such as structure size changes. No binary compatibility is + * possible across a change in the major version. + */ +#define UNI_MAJOR_VERSION 0 + +/** minor version + * Minor API changes that do not cause binary compatibility problems. + * Reset to 0 when upgrading UNI_MAJOR_VERSION + */ +#define UNI_MINOR_VERSION 6 + +/** patch level + * The Patch Level never includes API changes, simply bug fixes. + * Reset to 0 when upgrading UNI_MINOR_VERSION + */ +#define UNI_PATCH_VERSION 0 + + +/** + * Check at compile time if the UNI version is at least a certain + * level. + */ +#define UNI_VERSION_AT_LEAST(major,minor,patch) \ +(((major) < UNI_MAJOR_VERSION) \ + || ((major) == UNI_MAJOR_VERSION && (minor) < UNI_MINOR_VERSION) \ + || ((major) == UNI_MAJOR_VERSION && (minor) == UNI_MINOR_VERSION && (patch) <= UNI_PATCH_VERSION)) + +/** The formatted string of UniMRCP's version */ +#define UNI_VERSION_STRING \ + APR_STRINGIFY(UNI_MAJOR_VERSION) "." \ + APR_STRINGIFY(UNI_MINOR_VERSION) "." \ + APR_STRINGIFY(UNI_PATCH_VERSION) + +#endif /* __UNI_VERSION_H__ */ diff --git a/libs/unimrcp/build/vsprops/apr.vsprops b/libs/unimrcp/build/vsprops/apr.vsprops new file mode 100644 index 0000000000..3cc1c96995 --- /dev/null +++ b/libs/unimrcp/build/vsprops/apr.vsprops @@ -0,0 +1,28 @@ + + + + + + + + diff --git a/libs/unimrcp/build/vsprops/apt.vsprops b/libs/unimrcp/build/vsprops/apt.vsprops new file mode 100644 index 0000000000..263579c8c5 --- /dev/null +++ b/libs/unimrcp/build/vsprops/apt.vsprops @@ -0,0 +1,17 @@ + + + + + diff --git a/libs/unimrcp/build/vsprops/cepstral.vsprops b/libs/unimrcp/build/vsprops/cepstral.vsprops new file mode 100644 index 0000000000..8df44eba0f --- /dev/null +++ b/libs/unimrcp/build/vsprops/cepstral.vsprops @@ -0,0 +1,20 @@ + + + + + + diff --git a/libs/unimrcp/build/vsprops/mpf.vsprops b/libs/unimrcp/build/vsprops/mpf.vsprops new file mode 100644 index 0000000000..f03cdcc3d9 --- /dev/null +++ b/libs/unimrcp/build/vsprops/mpf.vsprops @@ -0,0 +1,17 @@ + + + + + diff --git a/libs/unimrcp/build/vsprops/mrcp.vsprops b/libs/unimrcp/build/vsprops/mrcp.vsprops new file mode 100644 index 0000000000..bbb759e793 --- /dev/null +++ b/libs/unimrcp/build/vsprops/mrcp.vsprops @@ -0,0 +1,17 @@ + + + + + diff --git a/libs/unimrcp/build/vsprops/mrcpclient.vsprops b/libs/unimrcp/build/vsprops/mrcpclient.vsprops new file mode 100644 index 0000000000..5a51b5923d --- /dev/null +++ b/libs/unimrcp/build/vsprops/mrcpclient.vsprops @@ -0,0 +1,16 @@ + + + + + diff --git a/libs/unimrcp/build/vsprops/mrcpengine.vsprops b/libs/unimrcp/build/vsprops/mrcpengine.vsprops new file mode 100644 index 0000000000..e57efb7ae4 --- /dev/null +++ b/libs/unimrcp/build/vsprops/mrcpengine.vsprops @@ -0,0 +1,16 @@ + + + + + diff --git a/libs/unimrcp/build/vsprops/mrcpserver.vsprops b/libs/unimrcp/build/vsprops/mrcpserver.vsprops new file mode 100644 index 0000000000..1965a90880 --- /dev/null +++ b/libs/unimrcp/build/vsprops/mrcpserver.vsprops @@ -0,0 +1,16 @@ + + + + + diff --git a/libs/unimrcp/build/vsprops/mrcpsignaling.vsprops b/libs/unimrcp/build/vsprops/mrcpsignaling.vsprops new file mode 100644 index 0000000000..64d7e14ced --- /dev/null +++ b/libs/unimrcp/build/vsprops/mrcpsignaling.vsprops @@ -0,0 +1,16 @@ + + + + + diff --git a/libs/unimrcp/build/vsprops/mrcpv2transport.vsprops b/libs/unimrcp/build/vsprops/mrcpv2transport.vsprops new file mode 100644 index 0000000000..481d85b047 --- /dev/null +++ b/libs/unimrcp/build/vsprops/mrcpv2transport.vsprops @@ -0,0 +1,16 @@ + + + + + diff --git a/libs/unimrcp/build/vsprops/sdk/unimrcpclient.vsprops b/libs/unimrcp/build/vsprops/sdk/unimrcpclient.vsprops new file mode 100644 index 0000000000..09ca2cd194 --- /dev/null +++ b/libs/unimrcp/build/vsprops/sdk/unimrcpclient.vsprops @@ -0,0 +1,12 @@ + + + + diff --git a/libs/unimrcp/build/vsprops/sdk/unimrcpplugin.vsprops b/libs/unimrcp/build/vsprops/sdk/unimrcpplugin.vsprops new file mode 100644 index 0000000000..96fb0cfed1 --- /dev/null +++ b/libs/unimrcp/build/vsprops/sdk/unimrcpplugin.vsprops @@ -0,0 +1,12 @@ + + + + diff --git a/libs/unimrcp/build/vsprops/sdk/unimrcpsdk.vsprops b/libs/unimrcp/build/vsprops/sdk/unimrcpsdk.vsprops new file mode 100644 index 0000000000..f5cd0d311a --- /dev/null +++ b/libs/unimrcp/build/vsprops/sdk/unimrcpsdk.vsprops @@ -0,0 +1,21 @@ + + + + + + diff --git a/libs/unimrcp/build/vsprops/sdk/unimrcpserver.vsprops b/libs/unimrcp/build/vsprops/sdk/unimrcpserver.vsprops new file mode 100644 index 0000000000..df5df74f97 --- /dev/null +++ b/libs/unimrcp/build/vsprops/sdk/unimrcpserver.vsprops @@ -0,0 +1,12 @@ + + + + diff --git a/libs/unimrcp/build/vsprops/sofiasip.vsprops b/libs/unimrcp/build/vsprops/sofiasip.vsprops new file mode 100644 index 0000000000..905d3ea296 --- /dev/null +++ b/libs/unimrcp/build/vsprops/sofiasip.vsprops @@ -0,0 +1,20 @@ + + + + + + diff --git a/libs/unimrcp/build/vsprops/unibase.vsprops b/libs/unimrcp/build/vsprops/unibase.vsprops new file mode 100644 index 0000000000..843d0fcbff --- /dev/null +++ b/libs/unimrcp/build/vsprops/unibase.vsprops @@ -0,0 +1,24 @@ + + + + + + diff --git a/libs/unimrcp/build/vsprops/unidebug.vsprops b/libs/unimrcp/build/vsprops/unidebug.vsprops new file mode 100644 index 0000000000..d3abbff710 --- /dev/null +++ b/libs/unimrcp/build/vsprops/unidebug.vsprops @@ -0,0 +1,24 @@ + + + + + diff --git a/libs/unimrcp/build/vsprops/unimrcpclient.vsprops b/libs/unimrcp/build/vsprops/unimrcpclient.vsprops new file mode 100644 index 0000000000..de020ae1b9 --- /dev/null +++ b/libs/unimrcp/build/vsprops/unimrcpclient.vsprops @@ -0,0 +1,20 @@ + + + + + + diff --git a/libs/unimrcp/build/vsprops/unimrcpplugin.vsprops b/libs/unimrcp/build/vsprops/unimrcpplugin.vsprops new file mode 100644 index 0000000000..00dcc9ed43 --- /dev/null +++ b/libs/unimrcp/build/vsprops/unimrcpplugin.vsprops @@ -0,0 +1,17 @@ + + + + + diff --git a/libs/unimrcp/build/vsprops/unimrcpserver.vsprops b/libs/unimrcp/build/vsprops/unimrcpserver.vsprops new file mode 100644 index 0000000000..966d2b794e --- /dev/null +++ b/libs/unimrcp/build/vsprops/unimrcpserver.vsprops @@ -0,0 +1,20 @@ + + + + + + diff --git a/libs/unimrcp/build/vsprops/unirelease.vsprops b/libs/unimrcp/build/vsprops/unirelease.vsprops new file mode 100644 index 0000000000..2d2f4ea400 --- /dev/null +++ b/libs/unimrcp/build/vsprops/unirelease.vsprops @@ -0,0 +1,15 @@ + + + + diff --git a/libs/unimrcp/build/vsprops/unirtsp.vsprops b/libs/unimrcp/build/vsprops/unirtsp.vsprops new file mode 100644 index 0000000000..b5dede2b33 --- /dev/null +++ b/libs/unimrcp/build/vsprops/unirtsp.vsprops @@ -0,0 +1,17 @@ + + + + + diff --git a/libs/unimrcp/conf/unimrcpclient.xml b/libs/unimrcp/conf/unimrcpclient.xml new file mode 100644 index 0000000000..fc903ffbd8 --- /dev/null +++ b/libs/unimrcp/conf/unimrcpclient.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/conf/unimrcpserver.xml b/libs/unimrcp/conf/unimrcpserver.xml new file mode 100644 index 0000000000..6cf213d53b --- /dev/null +++ b/libs/unimrcp/conf/unimrcpserver.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/configure.ac b/libs/unimrcp/configure.ac new file mode 100644 index 0000000000..7a569fddee --- /dev/null +++ b/libs/unimrcp/configure.ac @@ -0,0 +1,149 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.57) + +AC_INIT([unimrcp],[0.6.0]) + +AC_CONFIG_AUX_DIR([build]) +AC_CONFIG_MACRO_DIR([build/acmacros]) +AC_PREFIX_DEFAULT(/usr/local/unimrcp) + +plugindir='${prefix}/plugin' +confdir='${prefix}/conf' +logdir='${prefix}/log' +datadir='${prefix}/data' + +AM_INIT_AUTOMAKE(foreign) + +#Set default language +AC_LANG_C + +AC_PROG_CC +AC_PROG_CXX +AC_PROG_INSTALL + +#skip detection of Fortran +m4_undefine([AC_PROG_F77]) +m4_defun([AC_PROG_F77],[]) +AC_PROG_LIBTOOL + +# get version information +get_version="build/get-version.sh" +version_hdr="build/uni_version.h" +plugin_version_hdr="libs/mrcp-engine/include/mrcp_resource_plugin.h" +UNI_DOTTED_VERSION="`$get_version all $version_hdr UNI`" +UNI_LT_VERSION="-version-info `$get_version libtool $version_hdr UNI`" +PLUGIN_LT_VERSION="-version-info `$get_version libtool $plugin_version_hdr PLUGIN`" + +AC_SUBST(UNI_DOTTED_VERSION) +AC_SUBST(UNI_LT_VERSION) +AC_SUBST(PLUGIN_LT_VERSION) + +echo "UniMRCP Version: ${UNI_DOTTED_VERSION}" + +UNIMRCP_CHECK_APR +UNIMRCP_CHECK_APU + +UNIMRCP_CHECK_SOFIA + +UNIMRCP_CHECK_SWIFT + + +AC_SUBST(ac_aux_dir) +AC_SUBST(ac_macro_dir) +AC_SUBST(plugindir) +AC_SUBST(confdir) +AC_SUBST(logdir) + +AC_DEFUN([AX_COMPILER_VENDOR], +[ +AC_CACHE_CHECK([for _AC_LANG compiler vendor], ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor, + [ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor=unknown + # note: don't check for gcc first since some other compilers define __GNUC__ + for ventest in intel:__ICC,__ECC,__INTEL_COMPILER ibm:__xlc__,__xlC__,__IBMC__,__IBMCPP__ gnu:__GNUC__ sun:__SUNPRO_C,__SUNPRO_CC hp:__HP_cc,__HP_aCC dec:__DECC,__DECCXX,__DECC_VER,__DECCXX_VER borland:__BORLANDC__,__TURBOC__ comeau:__COMO__ cray:_CRAYC kai:__KCC lcc:__LCC__ metrowerks:__MWERKS__ sgi:__sgi,sgi microsoft:_MSC_VER watcom:__WATCOMC__ portland:__PGI; do + vencpp="defined("`echo $ventest | cut -d: -f2 | sed 's/,/) || defined(/g'`")" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,[ +#if !($vencpp) + thisisanerror; +#endif +])], [ax_cv_]_AC_LANG_ABBREV[_compiler_vendor=`echo $ventest | cut -d: -f1`; break]) + done + ]) +]) + +AX_COMPILER_VENDOR + + +AC_ARG_ENABLE(maintainer-mode, + [AC_HELP_STRING([--enable-maintainer-mode ],[turn on debugging and compile time warnings])], + [enable_maintainer_mode="$enableval"], + [enable_maintainer_mode="no"]) + +if test "${enable_maintainer_mode}" != "no"; then + CFLAGS="$CFLAGS -g" + if test "x${ax_cv_c_compiler_vendor}" = "xgnu" ; then + CFLAGS="$CFLAGS -Wall -Werror" + fi +fi + +AC_ARG_ENABLE(test-suites, + [AC_HELP_STRING([--enable-test-suites ],[build test suites])], + [enable_test_suites="$enableval"], + [enable_test_suites="no"]) + +AM_CONDITIONAL([TEST_SUITES],[test "${enable_test_suites}" != "no"]) + +AC_ARG_ENABLE(demosynth-plugin, + [AC_HELP_STRING([--disable-demosynth-plugin ],[exclude demo synthesizer plugin from build])], + [enable_demosynth_plugin="$enableval"], + [enable_demosynth_plugin="yes"]) + +AM_CONDITIONAL([DEMOSYNTH_PLUGIN],[test "${enable_demosynth_plugin}" = "yes"]) + +AC_ARG_ENABLE(demorecog-plugin, + [AC_HELP_STRING([--disable-demorecog-plugin ],[exclude demo recognizer plugin from build])], + [enable_demorecog_plugin="$enableval"], + [enable_demorecog_plugin="yes"]) + +AM_CONDITIONAL([DEMORECOG_PLUGIN],[test "${enable_demorecog_plugin}" = "yes"]) + + +AC_CONFIG_FILES([ + Makefile + libs/Makefile + libs/apr-toolkit/Makefile + libs/mpf/Makefile + libs/mrcp/Makefile + libs/mrcp-signaling/Makefile + libs/mrcpv2-transport/Makefile + libs/mrcp-engine/Makefile + libs/mrcp-server/Makefile + libs/mrcp-client/Makefile + libs/uni-rtsp/Makefile + modules/Makefile + modules/mrcp-sofiasip/Makefile + modules/mrcp-unirtsp/Makefile + plugins/Makefile + plugins/mrcp-cepstral/Makefile + plugins/demo-synth/Makefile + plugins/demo-recog/Makefile + platforms/Makefile + platforms/libunimrcp-server/Makefile + platforms/libunimrcp-client/Makefile + platforms/unimrcp-server/Makefile + platforms/unimrcp-client/Makefile + tests/Makefile + tests/apttest/Makefile + tests/mpftest/Makefile + tests/mrcptest/Makefile + tests/rtsptest/Makefile + tests/strtablegen/Makefile + build/Makefile + build/pkgconfig/Makefile + build/pkgconfig/unimrcpclient.pc + build/pkgconfig/unimrcpserver.pc + build/pkgconfig/unimrcpplugin.pc +]) + +AC_OUTPUT diff --git a/libs/unimrcp/data/demo.pcm b/libs/unimrcp/data/demo.pcm new file mode 100644 index 0000000000..7c5bf2720b Binary files /dev/null and b/libs/unimrcp/data/demo.pcm differ diff --git a/libs/unimrcp/data/grammar.xml b/libs/unimrcp/data/grammar.xml new file mode 100644 index 0000000000..3e84ebc38a --- /dev/null +++ b/libs/unimrcp/data/grammar.xml @@ -0,0 +1,10 @@ + + + + + one + two + three + + + \ No newline at end of file diff --git a/libs/unimrcp/data/one.pcm b/libs/unimrcp/data/one.pcm new file mode 100644 index 0000000000..92c1e92cf0 Binary files /dev/null and b/libs/unimrcp/data/one.pcm differ diff --git a/libs/unimrcp/data/result.xml b/libs/unimrcp/data/result.xml new file mode 100644 index 0000000000..e75c6e4b5f --- /dev/null +++ b/libs/unimrcp/data/result.xml @@ -0,0 +1,7 @@ + + + + one + one + + \ No newline at end of file diff --git a/libs/unimrcp/data/speak.xml b/libs/unimrcp/data/speak.xml new file mode 100644 index 0000000000..e9a9ec3ebd --- /dev/null +++ b/libs/unimrcp/data/speak.xml @@ -0,0 +1,6 @@ + + + + Hello World. + + \ No newline at end of file diff --git a/libs/unimrcp/docs/doxygen.conf b/libs/unimrcp/docs/doxygen.conf new file mode 100644 index 0000000000..32b0b23eaf --- /dev/null +++ b/libs/unimrcp/docs/doxygen.conf @@ -0,0 +1,32 @@ +PROJECT_NAME="UniMRCP" + +INPUT=. +QUIET=YES +RECURSIVE=YES +FILE_PATTERNS=*.h + +OUTPUT_DIRECTORY=docs/dox + +MACRO_EXPANSION=YES +EXPAND_ONLY_PREDEF=YES + +PREDEFINED="APT_DECLARE(x)=x" \ + "MPF_DECLARE(x)=x" \ + "MRCP_DECLARE(x)=x" \ + "RTSP_DECLARE(x)=x" + +OPTIMIZE_OUTPUT_FOR_C=YES +STRIP_CODE_COMMENTS=NO + +FULL_PATH_NAMES=NO +CASE_SENSE_NAMES=NO + +EXCLUDE_PATTERNS="*/acconfig.h" \ + "*/test/*" \ + "*/arch/*" + +GENERATE_HTML=YES +GENERATE_LATEX=NO +GENERATE_RTF=NO +GENERATE_MAN=NO +GENERATE_XML=NO diff --git a/libs/unimrcp/libs/Makefile.am b/libs/unimrcp/libs/Makefile.am new file mode 100644 index 0000000000..ce33be3016 --- /dev/null +++ b/libs/unimrcp/libs/Makefile.am @@ -0,0 +1,3 @@ +MAINTAINERCLEANFILES = Makefile.in + +SUBDIRS = apr-toolkit mpf mrcp mrcp-signaling mrcpv2-transport mrcp-engine mrcp-server mrcp-client uni-rtsp diff --git a/libs/unimrcp/libs/apr-toolkit/Makefile.am b/libs/unimrcp/libs/apr-toolkit/Makefile.am new file mode 100644 index 0000000000..e2a065beec --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/Makefile.am @@ -0,0 +1,43 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_srcdir)/libs/apr-toolkit/include \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) + +noinst_LTLIBRARIES = libaprtoolkit.la + +include_HEADERS = include/apt.h \ + include/apt_obj_list.h \ + include/apt_cyclic_queue.h \ + include/apt_task.h \ + include/apt_task_msg.h \ + include/apt_consumer_task.h \ + include/apt_net_server_task.h \ + include/apt_net_client_task.h \ + include/apt_pollset.h \ + include/apt_pool.h \ + include/apt_log.h \ + include/apt_pair.h \ + include/apt_string.h \ + include/apt_string_table.h \ + include/apt_text_stream.h \ + include/apt_net.h \ + include/apt_dir_layout.h \ + include/apt_test_suite.h + +libaprtoolkit_la_SOURCES = src/apt_obj_list.c \ + src/apt_cyclic_queue.c \ + src/apt_task.c \ + src/apt_task_msg.c \ + src/apt_consumer_task.c \ + src/apt_net_server_task.c \ + src/apt_net_client_task.c \ + src/apt_pollset.c \ + src/apt_pool.c \ + src/apt_log.c \ + src/apt_pair.c \ + src/apt_string_table.c \ + src/apt_text_stream.c \ + src/apt_net.c \ + src/apt_dir_layout.c \ + src/apt_test_suite.c + diff --git a/libs/unimrcp/libs/apr-toolkit/aprtoolkit.vcproj b/libs/unimrcp/libs/apr-toolkit/aprtoolkit.vcproj new file mode 100644 index 0000000000..9d39f55100 --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/aprtoolkit.vcproj @@ -0,0 +1,277 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt.h b/libs/unimrcp/libs/apr-toolkit/include/apt.h new file mode 100644 index 0000000000..2c0353161e --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/include/apt.h @@ -0,0 +1,59 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __APT_H__ +#define __APT_H__ + +/** + * @file apt.h + * @brief APR Toolkit Definitions + */ + +#include +#include +#include + +#ifdef __cplusplus +/** Begin of extern "C" block */ +#define APT_BEGIN_EXTERN_C extern "C" { +/** End of extern "C" block */ +#define APT_END_EXTERN_C } +#else +/** Begin of extern "C" block */ +#define APT_BEGIN_EXTERN_C +/** End of extern "C" block */ +#define APT_END_EXTERN_C +#endif + +/** Lib export/import defines (win32) */ +#ifdef WIN32 +#ifdef APT_STATIC_LIB +#define APT_DECLARE(type) type __stdcall +#else +#ifdef APT_LIB_EXPORT +#define APT_DECLARE(type) __declspec(dllexport) type __stdcall +#else +#define APT_DECLARE(type) __declspec(dllimport) type __stdcall +#endif +#endif +#else +#define APT_DECLARE(type) type +#endif + +/** Boolean value */ +typedef int apt_bool_t; + +#endif /*__APT_H__*/ diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_consumer_task.h b/libs/unimrcp/libs/apr-toolkit/include/apt_consumer_task.h new file mode 100644 index 0000000000..59eff4015f --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_consumer_task.h @@ -0,0 +1,63 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __APT_CONSUMER_TASK_H__ +#define __APT_CONSUMER_TASK_H__ + +/** + * @file apt_consumer_task.h + * @brief Consumer Task Definition + */ + +#include "apt_task.h" + +APT_BEGIN_EXTERN_C + +/** Opaque consumer task declaration */ +typedef struct apt_consumer_task_t apt_consumer_task_t; + +/** + * Create consumer task. + * @param obj the external object to associate with the task + * @param msg_pool the pool of task messages + * @param pool the pool to allocate memory from + */ +APT_DECLARE(apt_consumer_task_t*) apt_consumer_task_create( + void *obj, + apt_task_msg_pool_t *msg_pool, + apr_pool_t *pool); + +/** + * Get task base. + * @param task the consumer task to get base for + */ +APT_DECLARE(apt_task_t*) apt_consumer_task_base_get(apt_consumer_task_t *task); + +/** + * Get task vtable. + * @param task the consumer task to get vtable for + */ +APT_DECLARE(apt_task_vtable_t*) apt_consumer_task_vtable_get(apt_consumer_task_t *task); + +/** + * Get consumer task object. + * @param task the consumer task to get object from + */ +APT_DECLARE(void*) apt_consumer_task_object_get(apt_consumer_task_t *task); + +APT_END_EXTERN_C + +#endif /*__APT_CONSUMER_TASK_H__*/ diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_cyclic_queue.h b/libs/unimrcp/libs/apr-toolkit/include/apt_cyclic_queue.h new file mode 100644 index 0000000000..c36fcd21c8 --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_cyclic_queue.h @@ -0,0 +1,76 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __APT_CYCLIC_QUEUE_H__ +#define __APT_CYCLIC_QUEUE_H__ + +/** + * @file apt_cyclic_queue.h + * @brief Cyclic FIFO Queue of Opaque void* Objects + */ + +#include "apt.h" + +APT_BEGIN_EXTERN_C + +#define CYCLIC_QUEUE_DEFAULT_SIZE 100 + +/** Opaque cyclic queue declaration */ +typedef struct apt_cyclic_queue_t apt_cyclic_queue_t; + +/** + * Create cyclic queue. + * @param size the initial size of the queue + * @return the created queue + */ +APT_DECLARE(apt_cyclic_queue_t*) apt_cyclic_queue_create(apr_size_t size); + +/** + * Destroy cyclic queue. + * @param queue the queue to destroy + */ +APT_DECLARE(void) apt_cyclic_queue_destroy(apt_cyclic_queue_t *queue); + +/** + * Push object to the queue. + * @param queue the queue to push object to + * @param obj the object to push + */ +APT_DECLARE(apt_bool_t) apt_cyclic_queue_push(apt_cyclic_queue_t *queue, void *obj); + +/** + * Pop object from the queue. + * @param queue the queue to pop message from + */ +APT_DECLARE(void*) apt_cyclic_queue_pop(apt_cyclic_queue_t *queue); + +/** + * Clear the queue (remove all the elements from the queue). + * @param queue the queue to clear + */ +APT_DECLARE(void) apt_cyclic_queue_clear(apt_cyclic_queue_t *queue); + +/** + * Query whether the queue is empty. + * @param queue the queue to query + * @return TRUE if empty, otherwise FALSE + */ +APT_DECLARE(apt_bool_t) apt_cyclic_queue_is_empty(apt_cyclic_queue_t *queue); + + +APT_END_EXTERN_C + +#endif /*__APT_CYCLIC_QUEUE_H__*/ diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_dir_layout.h b/libs/unimrcp/libs/apr-toolkit/include/apt_dir_layout.h new file mode 100644 index 0000000000..497265d381 --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_dir_layout.h @@ -0,0 +1,67 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __APT_DIR_LAYOUT_H__ +#define __APT_DIR_LAYOUT_H__ + +/** + * @file apt_dir_layout.h + * @brief Directory Layout + */ + +#include "apt.h" + +APT_BEGIN_EXTERN_C + +/** Directory layout declaration */ +typedef struct apt_dir_layout_t apt_dir_layout_t; + +/** Directory layout */ +struct apt_dir_layout_t { + /** Path to config dir */ + char *conf_dir_path; + /** Path to plugin dir */ + char *plugin_dir_path; + /** Path to log dir */ + char *log_dir_path; + /** Path to data dir */ + char *data_dir_path; +}; + +/** + * Create (allocate) the structure of default directories layout. + */ +APT_DECLARE(apt_dir_layout_t*) apt_default_dir_layout_create(const char *root_dir_path, apr_pool_t *pool); + +/** + * Create (allocate) the structure of custom directories layout. + */ +APT_DECLARE(apt_dir_layout_t*) apt_custom_dir_layout_create( + const char *conf_dir_path, + const char *plugin_dir_path, + const char *log_dir_path, + const char *data_dir_path, + apr_pool_t *pool); + +/** + * Construct file path with the given file name relative to data dir. + */ +APT_DECLARE(char*) apt_datadir_filepath_get(const apt_dir_layout_t *dir_layout, const char *file_name, apr_pool_t *pool); + + +APT_END_EXTERN_C + +#endif /*__APT_DIR_LAYOUT_H__*/ diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_log.h b/libs/unimrcp/libs/apr-toolkit/include/apt_log.h new file mode 100644 index 0000000000..d3b38abf83 --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_log.h @@ -0,0 +1,170 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __APT_LOG_H__ +#define __APT_LOG_H__ + +/** + * @file apt_log.h + * @brief Basic Logger + */ + +#include +#include +#include "apt.h" + +APT_BEGIN_EXTERN_C + +/** Default max size of the log file (8Mb) */ +#define MAX_LOG_FILE_SIZE (8 * 1024 * 1024) +/** Default max number of rotated log files */ +#define MAX_LOG_FILE_COUNT 10 + +/** File:line mark */ +#define APT_LOG_MARK __FILE__,__LINE__ + +/** Format to log pointer values */ +#define APT_PTR_FMT "0x%x" +/** Format to log string identifiers */ +#define APT_SID_FMT "<%s>" +/** Format to log string identifiers and resources */ +#define APT_SIDRES_FMT "<%s@%s>" +/** Format to log pointers and identifiers */ +#define APT_PTRSID_FMT APT_PTR_FMT" "APT_SID_FMT +/** Format to log pointers, identifiers and resources */ +#define APT_PTRSIDRES_FMT APT_PTR_FMT" "APT_SIDRES_FMT + + +/** Priority of log messages ordered from highest priority to lowest (rfc3164) */ +typedef enum { + APT_PRIO_EMERGENCY, /**< system is unusable */ + APT_PRIO_ALERT, /**< action must be taken immediately */ + APT_PRIO_CRITICAL, /**< critical condition */ + APT_PRIO_ERROR, /**< error condition */ + APT_PRIO_WARNING, /**< warning condition */ + APT_PRIO_NOTICE, /**< normal, but significant condition */ + APT_PRIO_INFO, /**< informational message */ + APT_PRIO_DEBUG, /**< debug-level message */ + + APT_PRIO_COUNT /**< number of priorities */ +} apt_log_priority_e; + +/** Header (format) of log messages */ +typedef enum { + APT_LOG_HEADER_NONE = 0x00, /**< disable optional headers output */ + APT_LOG_HEADER_DATE = 0x01, /**< enable date output */ + APT_LOG_HEADER_TIME = 0x02, /**< enable time output */ + APT_LOG_HEADER_PRIORITY = 0x04, /**< enable priority name output */ + APT_LOG_HEADER_MARK = 0x08, /**< enable file:line mark output */ + + APT_LOG_HEADER_DEFAULT = APT_LOG_HEADER_DATE | APT_LOG_HEADER_TIME | APT_LOG_HEADER_PRIORITY +} apt_log_header_e; + +/** Log output modes */ +typedef enum { + APT_LOG_OUTPUT_NONE = 0x00, /**< disable logging */ + APT_LOG_OUTPUT_CONSOLE = 0x01, /**< enable console output */ + APT_LOG_OUTPUT_FILE = 0x02, /**< enable log file output */ +} apt_log_output_e; + +/** Opaque logger declaration */ +typedef struct apt_logger_t apt_logger_t; + +/** Prototype of extended log handler function */ +typedef apt_bool_t (*apt_log_ext_handler_f)(const char *file, int line, const char *id, + apt_log_priority_e priority, const char *format, va_list arg_ptr); + +/** + * Create the singleton instance of the logger. + * @param mode the log output mode + * @param priority the log priority level + * @param pool the memory pool to use + */ +APT_DECLARE(apt_bool_t) apt_log_instance_create(apt_log_output_e mode, apt_log_priority_e priority, apr_pool_t *pool); + +/** + * Destroy the singleton instance of the logger. + */ +APT_DECLARE(apt_bool_t) apt_log_instance_destroy(); + +/** + * Get the singleton instance of the logger. + */ +APT_DECLARE(apt_logger_t*) apt_log_instance_get(); + +/** + * Set the singleton instance of the logger. + */ +APT_DECLARE(apt_bool_t) apt_log_instance_set(apt_logger_t *logger); + +/** + * Open the log file. + * @param dir_path the path to the log directory + * @param file_name the name of the log file + * @param max_file_size the max size of the log file + * @param max_file_count the max number of files used in log rotation + * @param pool the memory pool to use + */ +APT_DECLARE(apt_bool_t) apt_log_file_open( + const char *dir_path, + const char *file_name, + apr_size_t max_file_size, + apr_size_t max_file_count, + apr_pool_t *pool); + +/** + * Close the log file. + */ +APT_DECLARE(apt_bool_t) apt_log_file_close(); + +/** + * Set the logging output. + * @param mode the mode to set + */ +APT_DECLARE(apt_bool_t) apt_log_output_mode_set(apt_log_output_e mode); + +/** + * Set the logging priority (log level). + * @param priority the priority to set + */ +APT_DECLARE(apt_bool_t) apt_log_priority_set(apt_log_priority_e priority); + +/** + * Set the header (format) for log messages. + * @param header the header to set (used as bitmask) + */ +APT_DECLARE(apt_bool_t) apt_log_header_set(int header); + +/** + * Set the extended external log handler. + * @param handler the handler to pass log events to + * @remark default logger is used to output the logs to stdout and/or log file, + * if external log handler isn't set + */ +APT_DECLARE(apt_bool_t) apt_log_ext_handler_set(apt_log_ext_handler_f handler); + +/** + * Do logging. + * @param file the file name log entry is generated from + * @param line the line number log entry is generated from + * @param priority the priority of the entire log entry + * @param format the format of the entire log entry + */ +APT_DECLARE(apt_bool_t) apt_log(const char *file, int line, apt_log_priority_e priority, const char *format, ...); + +APT_END_EXTERN_C + +#endif /*__APT_LOG_H__*/ diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_net.h b/libs/unimrcp/libs/apr-toolkit/include/apt_net.h new file mode 100644 index 0000000000..d3792f2dd1 --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_net.h @@ -0,0 +1,38 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __APT_NET_H__ +#define __APT_NET_H__ + +/** + * @file apt_net.h + * @brief Network Utilities + */ + +#include "apt.h" + +APT_BEGIN_EXTERN_C + +/** + * Get the IP address (in numeric address string format) by hostname. + * @param addr the IP address + * @param pool the pool to allocate memory from + */ +apt_bool_t apt_ip_get(char **addr, apr_pool_t *pool); + +APT_END_EXTERN_C + +#endif /*__APT_NET_H__*/ diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_net_client_task.h b/libs/unimrcp/libs/apr-toolkit/include/apt_net_client_task.h new file mode 100644 index 0000000000..0cd9466f6a --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_net_client_task.h @@ -0,0 +1,127 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __APT_NET_CLIENT_TASK_H__ +#define __APT_NET_CLIENT_TASK_H__ + +/** + * @file apt_net_client_task.h + * @brief Network Client Task Base + */ + +#include +#include "apt_task.h" + +APT_BEGIN_EXTERN_C + +/** Opaque network client task declaration */ +typedef struct apt_net_client_task_t apt_net_client_task_t; +/** Network client connection declaration */ +typedef struct apt_net_client_connection_t apt_net_client_connection_t; +/** Virtual table of network client events */ +typedef struct apt_net_client_vtable_t apt_net_client_vtable_t; + +/** Network client connection */ +struct apt_net_client_connection_t { + /** Memory pool */ + apr_pool_t *pool; + /** External object */ + void *obj; + /** Connected socket */ + apr_socket_t *sock; + /** Socket poll descriptor */ + apr_pollfd_t sock_pfd; + /** String identifier used for traces */ + const char *id; +}; + +/** Virtual table of network client events */ +struct apt_net_client_vtable_t { + /** Message receive handler */ + apt_bool_t (*on_receive)(apt_net_client_task_t *task, apt_net_client_connection_t *connection); +}; + + +/** + * Create network client task. + * @param max_connection_count the number of max connections + * @param obj the external object + * @param client_vtable the table of virtual methods of the net client task + * @param msg_pool the pool of task messages + * @param pool the pool to allocate memory from + */ +APT_DECLARE(apt_net_client_task_t*) apt_net_client_task_create( + apr_size_t max_connection_count, + void *obj, + const apt_net_client_vtable_t *client_vtable, + apt_task_msg_pool_t *msg_pool, + apr_pool_t *pool); + +/** + * Destroy network client task. + * @param task the task to destroy + */ +APT_DECLARE(apt_bool_t) apt_net_client_task_destroy(apt_net_client_task_t *task); + +/** + * Start network client task and wait for incoming requests. + * @param task the task to start + */ +APT_DECLARE(apt_bool_t) apt_net_client_task_start(apt_net_client_task_t *task); + +/** + * Terminate connection task. + * @param task the task to terminate + */ +APT_DECLARE(apt_bool_t) apt_net_client_task_terminate(apt_net_client_task_t *task); + +/** + * Get task base. + * @param task the network client task to get task base from + */ +APT_DECLARE(apt_task_t*) apt_net_client_task_base_get(apt_net_client_task_t *task); + +/** + * Get task vtable. + * @param task the network client task to get vtable from + */ +APT_DECLARE(apt_task_vtable_t*) apt_net_client_task_vtable_get(apt_net_client_task_t *task); + +/** + * Get external object. + * @param task the task to get object from + */ +APT_DECLARE(void*) apt_net_client_task_object_get(apt_net_client_task_t *task); + +/** + * Create connection. + */ +APT_DECLARE(apt_net_client_connection_t*) apt_net_client_connect(apt_net_client_task_t *task, const char *ip, apr_port_t port); + +/** + * Close connection. + */ +APT_DECLARE(apt_bool_t) apt_net_client_connection_close(apt_net_client_task_t *task, apt_net_client_connection_t *connection); + +/** + * Close and destroy connection. + */ +APT_DECLARE(apt_bool_t) apt_net_client_disconnect(apt_net_client_task_t *task, apt_net_client_connection_t *connection); + + +APT_END_EXTERN_C + +#endif /*__APT_NET_CLIENT_TASK_H__*/ diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_net_server_task.h b/libs/unimrcp/libs/apr-toolkit/include/apt_net_server_task.h new file mode 100644 index 0000000000..fa97836463 --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_net_server_task.h @@ -0,0 +1,132 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __APT_NET_SERVER_TASK_H__ +#define __APT_NET_SERVER_TASK_H__ + +/** + * @file apt_net_server_task.h + * @brief Network Server Task Base + */ + +#include +#include "apt_task.h" + +APT_BEGIN_EXTERN_C + +/** Opaque network server task declaration */ +typedef struct apt_net_server_task_t apt_net_server_task_t; +/** Network server connection declaration */ +typedef struct apt_net_server_connection_t apt_net_server_connection_t; +/** Virtual table of network server events */ +typedef struct apt_net_server_vtable_t apt_net_server_vtable_t; + +/** Network server connection */ +struct apt_net_server_connection_t { + /** Memory pool */ + apr_pool_t *pool; + /** External object */ + void *obj; + /** Client IP address */ + char *client_ip; + /** Accepted socket */ + apr_socket_t *sock; + /** Socket poll descriptor */ + apr_pollfd_t sock_pfd; + /** String identifier used for traces */ + const char *id; +}; + +/** Virtual table of network server events */ +struct apt_net_server_vtable_t { + /** Connect event handler */ + apt_bool_t (*on_connect)(apt_net_server_task_t *task, apt_net_server_connection_t *connection); + /** Disconnect event handler */ + apt_bool_t (*on_disconnect)(apt_net_server_task_t *task, apt_net_server_connection_t *connection); + /** Message receive handler */ + apt_bool_t (*on_receive)(apt_net_server_task_t *task, apt_net_server_connection_t *connection); +}; + + +/** + * Create network server task. + * @param listen_ip the listen IP address + * @param listen_port the listen port + * @param max_connection_count the number of max connections to accept + * @param obj the external object + * @param server_vtable the table of virtual methods of the net server task + * @param msg_pool the pool of task messages + * @param pool the pool to allocate memory from + */ +APT_DECLARE(apt_net_server_task_t*) apt_net_server_task_create( + const char *listen_ip, + apr_port_t listen_port, + apr_size_t max_connection_count, + void *obj, + const apt_net_server_vtable_t *server_vtable, + apt_task_msg_pool_t *msg_pool, + apr_pool_t *pool); + +/** + * Destroy network server task. + * @param task the task to destroy + */ +APT_DECLARE(apt_bool_t) apt_net_server_task_destroy(apt_net_server_task_t *task); + +/** + * Start network server task and wait for incoming requests. + * @param task the task to start + */ +APT_DECLARE(apt_bool_t) apt_net_server_task_start(apt_net_server_task_t *task); + +/** + * Terminate connection task. + * @param task the task to terminate + */ +APT_DECLARE(apt_bool_t) apt_net_server_task_terminate(apt_net_server_task_t *task); + +/** + * Get task base. + * @param task the network server task to get task base from + */ +APT_DECLARE(apt_task_t*) apt_net_server_task_base_get(apt_net_server_task_t *task); + +/** + * Get task vtable. + * @param task the network server task to get vtable from + */ +APT_DECLARE(apt_task_vtable_t*) apt_net_server_task_vtable_get(apt_net_server_task_t *task); + +/** + * Get external object. + * @param task the task to get object from + */ +APT_DECLARE(void*) apt_net_server_task_object_get(apt_net_server_task_t *task); + +/** + * Close connection. + */ +APT_DECLARE(apt_bool_t) apt_net_server_connection_close(apt_net_server_task_t *task, apt_net_server_connection_t *connection); + +/** + * Destroy connection. + */ +APT_DECLARE(void) apt_net_server_connection_destroy(apt_net_server_connection_t *connection); + + +APT_END_EXTERN_C + +#endif /*__APT_NET_SERVER_TASK_H__*/ diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_obj_list.h b/libs/unimrcp/libs/apr-toolkit/include/apt_obj_list.h new file mode 100644 index 0000000000..ed3359567d --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_obj_list.h @@ -0,0 +1,137 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __APT_OBJ_LIST_H__ +#define __APT_OBJ_LIST_H__ + +/** + * @file apt_obj_list.h + * @brief List of Opaque void* Objects + */ + +#include "apt.h" + +APT_BEGIN_EXTERN_C + + +/** Opaque list declaration */ +typedef struct apt_obj_list_t apt_obj_list_t; +/** Opaque list element declaration */ +typedef struct apt_list_elem_t apt_list_elem_t; + +/** + * Create list. + * @param pool the pool to allocate list from + * @return the created list + */ +APT_DECLARE(apt_obj_list_t*) apt_list_create(apr_pool_t *pool); + +/** + * Destroy list. + * @param list the list to destroy + */ +APT_DECLARE(void) apt_list_destroy(apt_obj_list_t *list); + +/** + * Push object to the list as first in, first out. + * @param list the list to push object to + * @param obj the object to push + * @param pool the pool to allocate list element from + * @return the inserted element + */ +APT_DECLARE(apt_list_elem_t*) apt_list_push_back(apt_obj_list_t *list, void *obj, apr_pool_t *pool); + +/** + * Pop object from the list as first in, first out. + * @param list the list to pop message from + * @return the popped object (if any) + */ +APT_DECLARE(void*) apt_list_pop_front(apt_obj_list_t *list); + +/** + * Retrieve object of the first element in the list. + * @param list the list to retrieve from + */ +APT_DECLARE(void*) apt_list_head(apt_obj_list_t *list); + +/** + * Retrieve object of the last element in the list. + * @param list the list to retrieve from + */ +APT_DECLARE(void*) apt_obj_list_tail(apt_obj_list_t *list); + + +/** + * Retrieve the first element of the list. + * @param list the list to retrieve from + */ +APT_DECLARE(apt_list_elem_t*) apt_list_first_elem_get(apt_obj_list_t *list); + +/** + * Retrieve the last element of the list. + * @param list the list to retrieve from + */ +APT_DECLARE(apt_list_elem_t*) apt_list_last_elem_get(apt_obj_list_t *list); + +/** + * Retrieve the next element of the list. + * @param list the list to retrieve from + * @param elem the element to retrieve next element from + */ +APT_DECLARE(apt_list_elem_t*) apt_list_next_elem_get(apt_obj_list_t *list, apt_list_elem_t *elem); + +/** + * Retrieve the prev element of the list. + * @param list the list to retrieve from + * @param elem the element to retrieve prev element from + */ +APT_DECLARE(apt_list_elem_t*) apt_list_prev_elem_get(apt_obj_list_t *list, apt_list_elem_t *elem); + +/** + * Insert element to the list. + * @param list the list to insert element to + * @param elem the element to insert before + * @param obj the object to insert + * @param pool the pool to allocate list element from + * @return the inserted element + */ +APT_DECLARE(apt_list_elem_t*) apt_list_elem_insert(apt_obj_list_t *list, apt_list_elem_t *elem, void *obj, apr_pool_t *pool); + +/** + * Remove element from the list. + * @param list the list to remove element from + * @param elem the element to remove + * @return the next element (if any) + */ +APT_DECLARE(apt_list_elem_t*) apt_list_elem_remove(apt_obj_list_t *list, apt_list_elem_t *elem); + +/** + * Query whether the list is empty. + * @param list the list to query + * @return TRUE if empty, otherwise FALSE + */ +APT_DECLARE(apt_bool_t) apt_list_is_empty(apt_obj_list_t *list); + +/** + * Retrieve the object associated with element. + * @param elem the element to retrieve object from + */ +APT_DECLARE(void*) apt_list_elem_object_get(apt_list_elem_t *elem); + + +APT_END_EXTERN_C + +#endif /*__APT_OBJ_LIST_H__*/ diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_pair.h b/libs/unimrcp/libs/apr-toolkit/include/apt_pair.h new file mode 100644 index 0000000000..9921ae0fa7 --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_pair.h @@ -0,0 +1,73 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __APT_PAIR_H__ +#define __APT_PAIR_H__ + +/** + * @file apt_pair.h + * @brief Generic Name-Value Pair + */ + +#include "apr_tables.h" +#include "apt_string.h" + +APT_BEGIN_EXTERN_C + +/** Name-value declaration */ +typedef struct apt_pair_t apt_pair_t; + +/** Generic name-value pair definition ("name:value") */ +struct apt_pair_t { + /** The name */ + apt_str_t name; + /** The value */ + apt_str_t value; +}; + +/** Dynamic array of name-value pairs */ +typedef apr_array_header_t apt_pair_arr_t; + +/** Initialize name-value pair */ +static APR_INLINE void apt_pair_init(apt_pair_t *pair) +{ + apt_string_reset(&pair->name); + apt_string_reset(&pair->value); +} + +/** Copy name-value pair */ +static APR_INLINE void apt_pair_copy(apt_pair_t *pair, const apt_pair_t *src_pair, apr_pool_t *pool) +{ + apt_string_copy(&pair->name,&src_pair->name,pool); + apt_string_copy(&pair->value,&src_pair->value,pool); +} + +/** Create array of name-value pairs */ +APT_DECLARE(apt_pair_arr_t*) apt_pair_array_create(apr_size_t initial_size, apr_pool_t *pool); +/** Copy array of name-value pairs */ +APT_DECLARE(apt_pair_arr_t*) apt_pair_array_copy(const apt_pair_arr_t *src, apr_pool_t *pool); +/** Append name-value pair */ +APT_DECLARE(apt_bool_t) apt_pair_array_append(apt_pair_arr_t *arr, const apt_str_t *name, const apt_str_t *value, apr_pool_t *pool); +/** Find name-value pair by name */ +APT_DECLARE(const apt_pair_t*) apt_pair_array_find(const apt_pair_arr_t *arr, const apt_str_t *name); +/** Get size of pair array */ +APT_DECLARE(int) apt_pair_array_size_get(const apt_pair_arr_t *arr); +/** Get name-value pair by id */ +APT_DECLARE(const apt_pair_t*) apt_pair_array_get(const apt_pair_arr_t *arr, int id); + +APT_END_EXTERN_C + +#endif /*__APT_PAIR_H__*/ diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_pollset.h b/libs/unimrcp/libs/apr-toolkit/include/apt_pollset.h new file mode 100644 index 0000000000..05ba559bd3 --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_pollset.h @@ -0,0 +1,96 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __APT_POLLSET_H__ +#define __APT_POLLSET_H__ + +/** + * @file apt_pollset.h + * @brief Interruptable APR-Pollset + */ + +/** + * Wakeup builtin API of the pollset is introduced only in APR-1.4 + * and it is not available for APR-1.2 and APR-1.3 versions. Thus + * apt_pollset_t is an extension of apr_pollset_t and provides + * pollset wakeup capabilities the similar way as it's implemented + * in APR-1.4 trunk + */ + +#include +#include "apt.h" + +APT_BEGIN_EXTERN_C + +/** Opaque pollset declaration */ +typedef struct apt_pollset_t apt_pollset_t; + +/** + * Create interruptable pollset on top of APR pollset + * @param size the maximum number of descriptors pollset can hold + * @param pool the pool to allocate memory from + */ +APT_DECLARE(apt_pollset_t*) apt_pollset_create(apr_uint32_t size, apr_pool_t *pool); + +/** + * Destroy pollset. + * @param pollset the pollset to destroy + */ +APT_DECLARE(apt_bool_t) apt_pollset_destroy(apt_pollset_t *pollset); + +/** + * Add pollset descriptor to a pollset. + * @param pollset the pollset to add the descriptor to + * @param descriptor the descriptor to add + */ +APT_DECLARE(apt_bool_t) apt_pollset_add(apt_pollset_t *pollset, const apr_pollfd_t *descriptor); + +/** + * Remove pollset descriptor from a pollset. + * @param pollset the pollset to remove the descriptor from + * @param descriptor the descriptor to remove + */ +APT_DECLARE(apt_bool_t) apt_pollset_remove(apt_pollset_t *pollset, const apr_pollfd_t *descriptor); + +/** + * Block for activity on the descriptor(s) in a pollset. + * @param pollset the pollset to use + * @param timeout the timeout in microseconds + * @param num nthe umber of signalled descriptors (output parameter) + * @param descriptors the array of signalled descriptors (output parameter) + */ +APT_DECLARE(apr_status_t) apt_pollset_poll( + apt_pollset_t *pollset, + apr_interval_time_t timeout, + apr_int32_t *num, + const apr_pollfd_t **descriptors); + +/** + * Interrupt the blocked poll call. + * @param pollset the pollset to use + */ +APT_DECLARE(apt_bool_t) apt_pollset_wakeup(apt_pollset_t *pollset); + +/** + * Match against builtin wake up descriptor in a pollset. + * @param pollset the pollset to use + * @param descriptor the descriptor to match + */ +APT_DECLARE(apt_bool_t) apt_pollset_is_wakeup(apt_pollset_t *pollset, const apr_pollfd_t *descriptor); + +APT_END_EXTERN_C + +#endif /*__APT_POLLSET_H__*/ diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_pool.h b/libs/unimrcp/libs/apr-toolkit/include/apt_pool.h new file mode 100644 index 0000000000..085195bee4 --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_pool.h @@ -0,0 +1,47 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __APT_POOL_H__ +#define __APT_POOL_H__ + +/** + * @file apt_pool.h + * @brief APR pool management + */ + +/** + * Wrappers around APR pool creation + * allow to control memory allocation policy project uses + */ + +#include "apt.h" + +APT_BEGIN_EXTERN_C + +/** + * Create APR pool + */ +APT_DECLARE(apr_pool_t*) apt_pool_create(); + +/** + * Create APR subpool pool + * @param parent the parent pool + */ +APT_DECLARE(apr_pool_t*) apt_subpool_create(apr_pool_t *parent); + +APT_END_EXTERN_C + +#endif /*__APT_POOL_H__*/ diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_string.h b/libs/unimrcp/libs/apr-toolkit/include/apt_string.h new file mode 100644 index 0000000000..9d0a7c9be6 --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_string.h @@ -0,0 +1,119 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __APT_STRING_H__ +#define __APT_STRING_H__ + +/** + * @file apt_string.h + * @brief String Representation + */ + +#include "apt.h" + +APT_BEGIN_EXTERN_C + +/** String declaration */ +typedef struct apt_str_t apt_str_t; + +/** String representation */ +struct apt_str_t { + /** String buffer (might be not NULL terminated) */ + char *buf; + /** Length of the string (not counting terminating NULL character if exists) */ + apr_size_t length; +}; + +/** Reset string. */ +static APR_INLINE void apt_string_reset(apt_str_t *str) +{ + str->buf = NULL; + str->length = 0; +} + +/** + * Set NULL terminated string. + * @param str the destination string + * @param src the NULL terminated string to set + */ +static APR_INLINE void apt_string_set(apt_str_t *str, const char *src) +{ + str->buf = (char*)src; + str->length = src ? strlen(src) : 0; +} + +/** + * Assign (copy) NULL terminated string. + * @param str the destination string + * @param src the NULL terminated string to copy + * @param pool the pool to allocate memory from + */ +static APR_INLINE void apt_string_assign(apt_str_t *str, const char *src, apr_pool_t *pool) +{ + str->buf = NULL; + str->length = src ? strlen(src) : 0; + if(str->length) { + str->buf = apr_pstrmemdup(pool,src,str->length); + } +} + +/** + * Assign (copy) n characters from the src string. + * @param str the destination string + * @param src the NULL terminated string to copy + * @param pool the pool to allocate memory from + */ +static APR_INLINE void apt_string_assign_n(apt_str_t *str, const char *src, apr_size_t length, apr_pool_t *pool) +{ + str->buf = NULL; + str->length = length; + if(str->length) { + str->buf = apr_pstrmemdup(pool,src,str->length); + } +} + +/** + * Copy string. + * @param dest_str the destination string + * @param src_str the source string + * @param pool the pool to allocate memory from + */ +static APR_INLINE void apt_string_copy(apt_str_t *str, const apt_str_t *src_str, apr_pool_t *pool) +{ + str->buf = NULL; + str->length = src_str->length; + if(str->length) { + str->buf = apr_pstrmemdup(pool,src_str->buf,src_str->length); + } +} + +/** + * Compare two strings (case insensitive). + * @param str1 the string to compare + * @param str2 the string to compare + * @return TRUE if equal, FALSE otherwise + */ +static APR_INLINE apt_bool_t apt_string_compare(const apt_str_t *str1, const apt_str_t *str2) +{ + if(str1->length != str2->length || !str1->length) { + return FALSE; + } + return (strncasecmp(str1->buf,str2->buf,str1->length) == 0) ? TRUE : FALSE; +} + +APT_END_EXTERN_C + +#endif /*__APT_STRING_H__*/ diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_string_table.h b/libs/unimrcp/libs/apr-toolkit/include/apt_string_table.h new file mode 100644 index 0000000000..112bd6bffc --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_string_table.h @@ -0,0 +1,63 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __APT_STRING_TABLE_H__ +#define __APT_STRING_TABLE_H__ + +/** + * @file apt_string_table.h + * @brief Generic String Table + */ + +#include "apt_string.h" + +APT_BEGIN_EXTERN_C + + +/** String table item declaration */ +typedef struct apt_str_table_item_t apt_str_table_item_t; + +/** String table item definition */ +struct apt_str_table_item_t { + /** String value associated with id */ + apt_str_t value; + /** Index of the unique (key) character to compare */ + apr_size_t key; +}; + + +/** + * Get the string by a given id. + * @param table the table to get string from + * @param size the size of the table + * @param id the id to get string by + * @return the string associated with the id, or NULL if the id is invalid + */ +APT_DECLARE(const apt_str_t*) apt_string_table_str_get(const apt_str_table_item_t table[], apr_size_t size, apr_size_t id); + +/** + * Find the id associated with a given string. + * @param table the table to search for the id + * @param size the size of the table + * @param value the string to search for + * @return the id associated with the string, or invalid id if string cannot be matched + */ +APT_DECLARE(apr_size_t) apt_string_table_id_find(const apt_str_table_item_t table[], apr_size_t size, const apt_str_t *value); + + +APT_END_EXTERN_C + +#endif /*__APT_STRING_TABLE_H__*/ diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_task.h b/libs/unimrcp/libs/apr-toolkit/include/apt_task.h new file mode 100644 index 0000000000..a2117d72d3 --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_task.h @@ -0,0 +1,209 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __APT_TASK_H__ +#define __APT_TASK_H__ + +/** + * @file apt_task.h + * @brief Thread Execution Abstraction + */ + +#include "apt.h" +#include "apt_task_msg.h" + +APT_BEGIN_EXTERN_C + +/** Opaque task declaration */ +typedef struct apt_task_t apt_task_t; +/** Opaque task virtual table declaration */ +typedef struct apt_task_vtable_t apt_task_vtable_t; +/** Opaque task method declaration */ +typedef apt_bool_t (*apt_task_method_f)(apt_task_t *task); +/** Opaque task event declaration */ +typedef void (*apt_task_event_f)(apt_task_t *task); + + +/** + * Create task. + * @param obj the external object to associate with the task + * @param msg_pool the pool of task messages + * @param pool the pool to allocate memory from + */ +APT_DECLARE(apt_task_t*) apt_task_create( + void *obj, + apt_task_msg_pool_t *msg_pool, + apr_pool_t *pool); + +/** + * Destroy task. + * @param task the task to destroy + */ +APT_DECLARE(apt_bool_t) apt_task_destroy(apt_task_t *task); + +/** + * Add slave task. + * @param task the task to add slave task to + * @param child_task the child task to add + */ +APT_DECLARE(apt_bool_t) apt_task_add(apt_task_t *task, apt_task_t *child_task); + +/** + * Start task. + * @param task the task to start + */ +APT_DECLARE(apt_bool_t) apt_task_start(apt_task_t *task); + +/** + * Terminate task. + * @param task the task to terminate + * @param wait_till_complete whether to wait for task to complete or + * process termination asynchronously + */ +APT_DECLARE(apt_bool_t) apt_task_terminate(apt_task_t *task, apt_bool_t wait_till_complete); + +/** + * Start child tasks. + * @param task the parent task + */ +APT_DECLARE(apt_bool_t) apt_task_child_start(apt_task_t *task); + +/** + * Terminate child tasks. + * @param task the parent task + */ +APT_DECLARE(apt_bool_t) apt_task_child_terminate(apt_task_t *task); + +/** + * Wait for task till complete. + * @param task the task to wait for + */ +APT_DECLARE(apt_bool_t) apt_task_wait_till_complete(apt_task_t *task); + +/** + * Get (acquire) task message. + * @param task the task to get task message from + */ +APT_DECLARE(apt_task_msg_t*) apt_task_msg_get(apt_task_t *task); + +/** + * Signal (post) message to the task. + * @param task the task to signal message to + * @param msg the message to signal + */ +APT_DECLARE(apt_bool_t) apt_task_msg_signal(apt_task_t *task, apt_task_msg_t *msg); + +/** + * Signal (post) message to the parent of the specified task. + * @param task the task to signal message to + * @param msg the message to signal + */ +APT_DECLARE(apt_bool_t) apt_task_msg_parent_signal(apt_task_t *task, apt_task_msg_t *msg); + +/** + * Process message signaled to the task. + * @param task the task to process message + * @param msg the message to process + */ +APT_DECLARE(apt_bool_t) apt_task_msg_process(apt_task_t *task, apt_task_msg_t *msg); + +/** + * Get parent (master) task. + * @param task the task to get parent from + */ +APT_DECLARE(apt_task_t*) apt_task_parent_get(apt_task_t *task); + +/** + * Get memory pool associated with task. + * @param task the task to get pool from + */ +APT_DECLARE(apr_pool_t*) apt_task_pool_get(apt_task_t *task); + +/** + * Get external object associated with the task. + * @param task the task to get object from + */ +APT_DECLARE(void*) apt_task_object_get(apt_task_t *task); + +/** + * Get task vtable. + * @param task the task to get vtable from + */ +APT_DECLARE(apt_task_vtable_t*) apt_task_vtable_get(apt_task_t *task); + +/** + * Give a name to the task. + * @param task the task to give name for + * @param name the name to set + */ +APT_DECLARE(void) apt_task_name_set(apt_task_t *task, const char *name); + +/** + * Get task name. + * @param task the task to get name from + */ +APT_DECLARE(const char*) apt_task_name_get(apt_task_t *task); + +/** + * Hold task execution. + * @param msec the time to hold + */ +APT_DECLARE(void) apt_task_delay(apr_size_t msec); + + +/** Table of task virtual methods */ +struct apt_task_vtable_t { + /** Virtual destroy method */ + apt_task_method_f destroy; + /** Virtual start method*/ + apt_task_method_f start; + /** Virtual terminate method */ + apt_task_method_f terminate; + /** Virtual run method*/ + apt_task_method_f run; + + /** Virtual signal method */ + apt_bool_t (*signal_msg)(apt_task_t *task, apt_task_msg_t *msg); + /** Virtual process method */ + apt_bool_t (*process_msg)(apt_task_t *task, apt_task_msg_t *msg); + + /** Virtual pre-run event handler */ + apt_task_event_f on_pre_run; + /** Virtual post-run event handler */ + apt_task_event_f on_post_run; + /** Virtual start-complete event handler */ + apt_task_event_f on_start_complete; + /** Virtual terminate-complete event handler */ + apt_task_event_f on_terminate_complete; +}; + +static APR_INLINE void apt_task_vtable_reset(apt_task_vtable_t *vtable) +{ + vtable->destroy = NULL; + vtable->start = NULL; + vtable->terminate = NULL; + vtable->run = NULL; + vtable->signal_msg = NULL; + vtable->process_msg = NULL; + vtable->on_pre_run = NULL; + vtable->on_post_run = NULL; + vtable->on_start_complete = NULL; + vtable->on_terminate_complete = NULL; +} + +APT_END_EXTERN_C + +#endif /*__APT_TASK_H__*/ diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_task_msg.h b/libs/unimrcp/libs/apr-toolkit/include/apt_task_msg.h new file mode 100644 index 0000000000..4d43ee5ca3 --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_task_msg.h @@ -0,0 +1,80 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __APT_TASK_MSG_H__ +#define __APT_TASK_MSG_H__ + +/** + * @file apt_task_msg.h + * @brief Task Message Base Definition + */ + +#include "apt.h" + +APT_BEGIN_EXTERN_C + +/** Enumeration of task message types */ +typedef enum { + TASK_MSG_CORE, /**< core task message type */ + TASK_MSG_USER /**< user defined task messages start from here */ +} apt_task_msg_type_e; + +/** Enumeration of core task messages */ +typedef enum { + CORE_TASK_MSG_NONE, /**< indefinite message */ + CORE_TASK_MSG_START_COMPLETE, /**< start-complete message */ + CORE_TASK_MSG_TERMINATE_REQUEST, /**< terminate-request message */ + CORE_TASK_MSG_TERMINATE_COMPLETE /**< terminate-complete message */ +} apt_core_task_msg_type_e; + +/** Opaque task message declaration */ +typedef struct apt_task_msg_t apt_task_msg_t; +/** Opaque task message pool declaration */ +typedef struct apt_task_msg_pool_t apt_task_msg_pool_t; + +/** Task message is used for inter task communication */ +struct apt_task_msg_t { + /** Message pool the task message is allocated from */ + apt_task_msg_pool_t *msg_pool; + /** Task msg type */ + apt_task_msg_type_e type; + /** Task msg sub type */ + int sub_type; + /** Context specific data */ + char data[1]; +}; + + +/** Create pool of task messages with dynamic allocation of messages (no actual pool is created) */ +APT_DECLARE(apt_task_msg_pool_t*) apt_task_msg_pool_create_dynamic(apr_size_t msg_size, apr_pool_t *pool); + +/** Create pool of task messages with static allocation of messages */ +APT_DECLARE(apt_task_msg_pool_t*) apt_task_msg_pool_create_static(apr_size_t msg_size, apr_size_t msg_pool_size, apr_pool_t *pool); + +/** Destroy pool of task messages */ +APT_DECLARE(void) apt_task_msg_pool_destroy(apt_task_msg_pool_t *msg_pool); + + +/** Acquire task message from task message pool */ +APT_DECLARE(apt_task_msg_t*) apt_task_msg_acquire(apt_task_msg_pool_t *task_msg_pool); + +/** Realese task message */ +APT_DECLARE(void) apt_task_msg_release(apt_task_msg_t *task_msg); + + +APT_END_EXTERN_C + +#endif /*__APT_TASK_MSG_H__*/ diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_test_suite.h b/libs/unimrcp/libs/apr-toolkit/include/apt_test_suite.h new file mode 100644 index 0000000000..a703de7c03 --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_test_suite.h @@ -0,0 +1,99 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __APT_TEST_SUITE_H__ +#define __APT_TEST_SUITE_H__ + +/** + * @file apt_test_suite.h + * @brief Test Suite and Framework Definitions + */ + +#include "apt_string.h" + +APT_BEGIN_EXTERN_C + + +/** Opaque test suite declaration */ +typedef struct apt_test_suite_t apt_test_suite_t; + +/** Prototype of test function */ +typedef apt_bool_t (*apt_test_f)(apt_test_suite_t *suite, int argc, const char * const *argv); + +/** Test suite as a base for all kind of tests */ +struct apt_test_suite_t { + /** Memory pool to allocate memory from */ + apr_pool_t *pool; + /** Unique name of the test suite */ + apt_str_t name; + /** External object associated with the test suite */ + void *obj; + /** Test function to execute */ + apt_test_f tester; +}; + +/** + * Create test suite. + * @param pool the pool to allocate memory from + * @param name the unique name of the test suite + * @param obj the external object associated with the test suite + * @param tester the test function to execute + */ +APT_DECLARE(apt_test_suite_t*) apt_test_suite_create(apr_pool_t *pool, const char *name, + void *obj, apt_test_f tester); + + + + + +/** Opaque test framework declaration */ +typedef struct apt_test_framework_t apt_test_framework_t; + +/** + * Create test framework. + */ +APT_DECLARE(apt_test_framework_t*) apt_test_framework_create(); + +/** + * Destroy test framework. + * @param framework the test framework to destroy + */ +APT_DECLARE(void) apt_test_framework_destroy(apt_test_framework_t *framework); + +/** + * Add test suite to framework. + * @param framework the test framework to add test suite to + * @param suite the test suite to add + */ +APT_DECLARE(apt_bool_t) apt_test_framework_suite_add(apt_test_framework_t *framework, apt_test_suite_t *suite); + +/** + * Run test suites. + * @param framework the test framework + * @param argc the number of arguments + * @param argv the array of arguments + */ +APT_DECLARE(apt_bool_t) apt_test_framework_run(apt_test_framework_t *framework, int argc, const char * const *argv); + +/** + * Retrieve the memory pool. + * @param framework the test framework to retrieve memory pool from + */ +APT_DECLARE(apr_pool_t*) apt_test_framework_pool_get(apt_test_framework_t *framework); + +APT_END_EXTERN_C + +#endif /*__APT_TEST_SUITE_H__*/ diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_text_stream.h b/libs/unimrcp/libs/apr-toolkit/include/apt_text_stream.h new file mode 100644 index 0000000000..f5fe1c30c0 --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_text_stream.h @@ -0,0 +1,215 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __APT_TEXT_STREAM_H__ +#define __APT_TEXT_STREAM_H__ + +/** + * @file apt_text_stream.h + * @brief Text Stream Parse/Generate Routine + */ + +#include +#include +#include "apt_string.h" +#include "apt_pair.h" + +APT_BEGIN_EXTERN_C + +/** Named tokens */ + +/** Space */ +#define APT_TOKEN_SP ' ' +/** Carrige return */ +#define APT_TOKEN_CR 0x0D +/** Line feed */ +#define APT_TOKEN_LF 0x0A + +/** Text stream declaration */ +typedef struct apt_text_stream_t apt_text_stream_t; + +/** Text stream is used for message parsing and generation */ +struct apt_text_stream_t { + /** Text stream */ + apt_str_t text; + /** Current position in the buffer */ + char *pos; +}; + +/** + * Navigate through the lines of the text stream (message). + * @param stream the text stream to navigate + * @param line the read line to return + * @return TRUE if the length of the line > 0, otherwise FALSE + */ +APT_DECLARE(apt_bool_t) apt_text_line_read(apt_text_stream_t *stream, apt_str_t *line); + +/** + * Navigate through the headers (name:value pairs) of the text stream (message). + * @param stream the text stream to navigate + * @param pair the read pair to return + * @return TRUE if the length of the read name > 0, otherwise FALSE + */ +APT_DECLARE(apt_bool_t) apt_text_header_read(apt_text_stream_t *stream, apt_pair_t *pair); + +/** + * Navigate through the fields of the line. + * @param stream the text stream to navigate + * @param separator the field separator + * @param skip_spaces whether to skip spaces or not + * @param field the read field to return + * @return TRUE if the length of the field > 0, otherwise FALSE + */ +APT_DECLARE(apt_bool_t) apt_text_field_read(apt_text_stream_t *stream, char separator, apt_bool_t skip_spaces, apt_str_t *field); + + + +/** Generate header */ +APT_DECLARE(apt_bool_t) apt_text_header_generate(const apt_pair_t *pair, apt_text_stream_t *text_stream); + +/** Generate only the name ("name:") of the header */ +APT_DECLARE(apt_bool_t) apt_text_header_name_generate(const apt_str_t *name, apt_text_stream_t *text_stream); + +/** Parse array of name-value pairs */ +APT_DECLARE(apt_bool_t) apt_pair_array_parse(apt_pair_arr_t *arr, const apt_str_t *value, apr_pool_t *pool); +/** Generate array of name-value pairs */ +APT_DECLARE(apt_bool_t) apt_pair_array_generate(apt_pair_arr_t *arr, apt_text_stream_t *text_stream); + + +/** Parse boolean-value */ +APT_DECLARE(apt_bool_t) apt_boolean_value_parse(const apt_str_t *str, apt_bool_t *value); + +/** Generate boolean-value */ +APT_DECLARE(apt_bool_t) apt_boolean_value_generate(apt_bool_t value, apt_text_stream_t *str); + + +/** Parse size_t value */ +static APR_INLINE apr_size_t apt_size_value_parse(const apt_str_t *str) +{ + return str->buf ? atol(str->buf) : 0; +} + +/** Generate apr_size_t value */ +static APR_INLINE apt_bool_t apt_size_value_generate(apr_size_t value, apt_text_stream_t *stream) +{ + int length = sprintf(stream->pos, "%"APR_SIZE_T_FMT, value); + if(length <= 0) { + return FALSE; + } + stream->pos += length; + return TRUE; +} + +/** Parse float value */ +static APR_INLINE float apt_float_value_parse(const apt_str_t *str) +{ + return str->buf ? (float)atof(str->buf) : 0; +} + +/** Generate float value */ +static APR_INLINE apt_bool_t apt_float_value_generate(float value, apt_text_stream_t *stream) +{ + int length = sprintf(stream->pos,"%.1f",value); + if(length <= 0) { + return FALSE; + } + stream->pos += length; + return TRUE; +} + +/** Generate string value */ +static APR_INLINE apt_bool_t apt_string_value_generate(const apt_str_t *str, apt_text_stream_t *stream) +{ + if(str->length) { + memcpy(stream->pos,str->buf,str->length); + stream->pos += str->length; + } + return TRUE; +} + +/** Initialize text stream */ +static APR_INLINE void apt_text_stream_init(apt_text_stream_t *stream, char *buffer, apr_size_t size) +{ + stream->text.buf = buffer; + stream->text.length = size; + stream->pos = stream->text.buf; +} + +/** Insert end of the line symbol(s) */ +static APR_INLINE void apt_text_eol_insert(apt_text_stream_t *stream) +{ + *stream->pos++ = APT_TOKEN_CR; + *stream->pos++ = APT_TOKEN_LF; +} + +/** Insert character */ +static APR_INLINE void apt_text_char_insert(apt_text_stream_t *stream, char ch) +{ + *stream->pos++ = ch; +} + +/** Insert space */ +static APR_INLINE void apt_text_space_insert(apt_text_stream_t *stream) +{ + *stream->pos++ = APT_TOKEN_SP; +} + +/** Skip spaces */ +static APR_INLINE void apt_text_spaces_skip(apt_text_stream_t *stream) +{ + const char *end = stream->text.buf + stream->text.length; + while(stream->pos < end && *stream->pos == APT_TOKEN_SP) stream->pos++; +} + +/** Skip specified character */ +static APR_INLINE void apt_text_char_skip(apt_text_stream_t *stream, char ch) +{ + const char *end = stream->text.buf + stream->text.length; + if(stream->pos < end && *stream->pos == ch) stream->pos++; +} + +/** Check whether end of stream is reached */ +static APR_INLINE apt_bool_t apt_text_is_eos(const apt_text_stream_t *stream) +{ + const char *end = stream->text.buf + stream->text.length; + return (stream->pos >= end) ? TRUE : FALSE; +} + +/** Scroll text stream */ +APT_DECLARE(apt_bool_t) apt_text_stream_scroll(apt_text_stream_t *stream); + +/** Parse id at resource string */ +APT_DECLARE(apt_bool_t) apt_id_resource_parse(const apt_str_t *str, char separator, apt_str_t *id, apt_str_t *resource, apr_pool_t *pool); + +/** Generate id at resource string */ +APT_DECLARE(apt_bool_t) apt_id_resource_generate(const apt_str_t *id, const apt_str_t *resource, char separator, apt_str_t *str, apr_pool_t *pool); + +/** Generate value plus the length (number of digits) of the value itself */ +APT_DECLARE(apt_bool_t) apt_var_length_value_generate(apr_size_t *value, apr_size_t max_count, apt_str_t *str); + + +/** + * Generate unique identifier (hex string) + * @param id the id to generate + * @param length the length of hex string to generate + * @param pool the pool to allocate memory from + */ +APT_DECLARE(apt_bool_t) apt_unique_id_generate(apt_str_t *id, apr_size_t length, apr_pool_t *pool); + + +APT_END_EXTERN_C + +#endif /*__APT_TEXT_STREAM_H__*/ diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_consumer_task.c b/libs/unimrcp/libs/apr-toolkit/src/apt_consumer_task.c new file mode 100644 index 0000000000..42a8754347 --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_consumer_task.c @@ -0,0 +1,99 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include "apt_consumer_task.h" + +struct apt_consumer_task_t { + void *obj; + apt_task_t *base; + apr_queue_t *msg_queue; +}; + +static apt_bool_t apt_consumer_task_msg_signal(apt_task_t *task, apt_task_msg_t *msg); +static apt_bool_t apt_consumer_task_run(apt_task_t *task); + +APT_DECLARE(apt_consumer_task_t*) apt_consumer_task_create( + void *obj, + apt_task_msg_pool_t *msg_pool, + apr_pool_t *pool) +{ + apt_task_vtable_t *vtable; + apt_consumer_task_t *consumer_task = apr_palloc(pool,sizeof(apt_consumer_task_t)); + consumer_task->obj = obj; + consumer_task->msg_queue = NULL; + if(apr_queue_create(&consumer_task->msg_queue,1024,pool) != APR_SUCCESS) { + return NULL; + } + + consumer_task->base = apt_task_create(consumer_task,msg_pool,pool); + if(!consumer_task->base) { + return NULL; + } + + vtable = apt_task_vtable_get(consumer_task->base); + if(vtable) { + vtable->run = apt_consumer_task_run; + vtable->signal_msg = apt_consumer_task_msg_signal; + } + return consumer_task; +} + +APT_DECLARE(apt_task_t*) apt_consumer_task_base_get(apt_consumer_task_t *task) +{ + return task->base; +} + +APT_DECLARE(apt_task_vtable_t*) apt_consumer_task_vtable_get(apt_consumer_task_t *task) +{ + return apt_task_vtable_get(task->base); +} + +APT_DECLARE(void*) apt_consumer_task_object_get(apt_consumer_task_t *task) +{ + return task->obj; +} + +static apt_bool_t apt_consumer_task_msg_signal(apt_task_t *task, apt_task_msg_t *msg) +{ + apt_consumer_task_t *consumer_task = apt_task_object_get(task); + return (apr_queue_push(consumer_task->msg_queue,msg) == APR_SUCCESS) ? TRUE : FALSE; +} + +static apt_bool_t apt_consumer_task_run(apt_task_t *task) +{ + apr_status_t rv; + void *msg; + apt_bool_t running = TRUE; + apt_consumer_task_t *consumer_task; + consumer_task = apt_task_object_get(task); + if(!consumer_task) { + return FALSE; + } + + while(running) { + rv = apr_queue_pop(consumer_task->msg_queue,&msg); + if(rv == APR_SUCCESS) { + if(msg) { + apt_task_msg_t *task_msg = msg; + if(apt_task_msg_process(consumer_task->base,task_msg) == FALSE) { + running = FALSE; + } + } + } + } + return TRUE; +} diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_cyclic_queue.c b/libs/unimrcp/libs/apr-toolkit/src/apt_cyclic_queue.c new file mode 100644 index 0000000000..fff7de514f --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_cyclic_queue.c @@ -0,0 +1,104 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include "apt_cyclic_queue.h" + +struct apt_cyclic_queue_t { + void **data; + apr_size_t max_size; + apr_size_t actual_size; + apr_size_t head; + apr_size_t tail; +}; + +static apt_bool_t apt_cyclic_queue_resize(apt_cyclic_queue_t *queue); + + +APT_DECLARE(apt_cyclic_queue_t*) apt_cyclic_queue_create(apr_size_t size) +{ + apt_cyclic_queue_t *queue = malloc(sizeof(apt_cyclic_queue_t)); + queue->max_size = size; + queue->actual_size = 0; + queue->data = malloc(sizeof(void*) * queue->max_size); + queue->head = queue->tail = 0; + return queue; +} + +APT_DECLARE(void) apt_cyclic_queue_destroy(apt_cyclic_queue_t *queue) +{ + if(queue->data) { + free(queue->data); + queue->data = NULL; + } + free(queue); +} + +APT_DECLARE(apt_bool_t) apt_cyclic_queue_push(apt_cyclic_queue_t *queue, void *obj) +{ + if(queue->actual_size >= queue->max_size) { + if(apt_cyclic_queue_resize(queue) != TRUE) { + return FALSE; + } + } + + queue->data[queue->head] = obj; + queue->head = (queue->head + 1) % queue->max_size; + queue->actual_size++; + return TRUE; +} + +APT_DECLARE(void*) apt_cyclic_queue_pop(apt_cyclic_queue_t *queue) +{ + void *obj = NULL; + if(queue->actual_size) { + obj = queue->data[queue->tail]; + queue->tail = (queue->tail + 1) % queue->max_size; + queue->actual_size--; + } + return obj; +} + +APT_DECLARE(void) apt_cyclic_queue_clear(apt_cyclic_queue_t *queue) +{ + queue->actual_size = 0; + queue->head = queue->tail = 0; +} + +APT_DECLARE(apt_bool_t) apt_cyclic_queue_is_empty(apt_cyclic_queue_t *queue) +{ + return queue->actual_size ? TRUE : FALSE; +} + +static apt_bool_t apt_cyclic_queue_resize(apt_cyclic_queue_t *queue) +{ + apr_size_t new_size = queue->max_size + queue->max_size/2; + void **new_data = malloc(sizeof(void*) * new_size); + apr_size_t offset; + + offset = queue->max_size - queue->head; + memcpy(new_data, queue->data + queue->head, sizeof(void*) * offset); + if(queue->head) { + memcpy(new_data + offset, queue->data, sizeof(void*) * queue->head); + } + + queue->tail = 0; + queue->head = queue->max_size; + queue->max_size = new_size; + free(queue->data); + queue->data = new_data; + return TRUE; +} diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_dir_layout.c b/libs/unimrcp/libs/apr-toolkit/src/apt_dir_layout.c new file mode 100644 index 0000000000..9a527b63b0 --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_dir_layout.c @@ -0,0 +1,74 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include "apt_dir_layout.h" + +static apt_dir_layout_t* apt_dir_layout_alloc(apr_pool_t *pool) +{ + apt_dir_layout_t *dir_layout = (apt_dir_layout_t*) apr_palloc(pool,sizeof(apt_dir_layout_t)); + dir_layout->conf_dir_path = NULL; + dir_layout->plugin_dir_path = NULL; + dir_layout->log_dir_path = NULL; + dir_layout->data_dir_path = NULL; + return dir_layout; +} + +APT_DECLARE(apt_dir_layout_t*) apt_default_dir_layout_create(const char *root_dir_path, apr_pool_t *pool) +{ + apt_dir_layout_t *dir_layout = apt_dir_layout_alloc(pool); + if(root_dir_path) { + apr_filepath_merge(&dir_layout->conf_dir_path,root_dir_path,"conf",0,pool); + apr_filepath_merge(&dir_layout->plugin_dir_path,root_dir_path,"plugin",0,pool); + apr_filepath_merge(&dir_layout->log_dir_path,root_dir_path,"log",0,pool); + apr_filepath_merge(&dir_layout->data_dir_path,root_dir_path,"data",0,pool); + } + return dir_layout; +} + +APT_DECLARE(apt_dir_layout_t*) apt_custom_dir_layout_create( + const char *conf_dir_path, + const char *plugin_dir_path, + const char *log_dir_path, + const char *data_dir_path, + apr_pool_t *pool) +{ + apt_dir_layout_t *dir_layout = apt_dir_layout_alloc(pool); + if(conf_dir_path) { + dir_layout->conf_dir_path = apr_pstrdup(pool,conf_dir_path); + } + if(plugin_dir_path) { + dir_layout->plugin_dir_path = apr_pstrdup(pool,plugin_dir_path); + } + if(log_dir_path) { + dir_layout->log_dir_path = apr_pstrdup(pool,log_dir_path); + } + if(data_dir_path) { + dir_layout->data_dir_path = apr_pstrdup(pool,data_dir_path); + } + return dir_layout; +} + +APT_DECLARE(char*) apt_datadir_filepath_get(const apt_dir_layout_t *dir_layout, const char *file_name, apr_pool_t *pool) +{ + if(dir_layout && dir_layout->data_dir_path && file_name) { + char *file_path = NULL; + if(apr_filepath_merge(&file_path,dir_layout->data_dir_path,file_name,0,pool) == APR_SUCCESS) { + return file_path; + } + } + return NULL; +} diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_log.c b/libs/unimrcp/libs/apr-toolkit/src/apt_log.c new file mode 100644 index 0000000000..25a5abf5d5 --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_log.c @@ -0,0 +1,304 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include +#include "apt_log.h" + +#define MAX_LOG_ENTRY_SIZE 4096 +#define MAX_PRIORITY_NAME_LENGTH 9 + +static const char priority_snames[APT_PRIO_COUNT][MAX_PRIORITY_NAME_LENGTH+1] = +{ + "[EMERG] ", + "[ALERT] ", + "[CRITIC] ", + "[ERROR] ", + "[WARN] ", + "[NOTICE] ", + "[INFO] ", + "[DEBUG] " +}; + +typedef struct apt_log_file_data_t apt_log_file_data_t; + +struct apt_log_file_data_t { + const char *log_dir_path; + const char *log_file_name; + FILE *file; + apr_size_t cur_size; + apr_size_t max_size; + apr_size_t cur_file_index; + apr_size_t max_file_count; + apr_thread_mutex_t *mutex; + apr_pool_t *pool; +}; + +struct apt_logger_t { + apt_log_output_e mode; + apt_log_priority_e priority; + int header; + apt_log_ext_handler_f ext_handler; + apt_log_file_data_t *file_data; +}; + +static apt_logger_t *apt_logger = NULL; + +static apt_bool_t apt_do_log(const char *file, int line, apt_log_priority_e priority, const char *format, va_list arg_ptr); + +static const char* apt_log_file_path_make(apt_log_file_data_t *file_data); +static apt_bool_t apt_log_file_dump(apt_log_file_data_t *file_data, const char *log_entry, apr_size_t size); + + +APT_DECLARE(apt_bool_t) apt_log_instance_create(apt_log_output_e mode, apt_log_priority_e priority, apr_pool_t *pool) +{ + if(apt_logger) { + return FALSE; + } + apt_logger = apr_palloc(pool,sizeof(apt_logger_t)); + apt_logger->mode = mode; + apt_logger->priority = priority; + apt_logger->header = APT_LOG_HEADER_DEFAULT; + apt_logger->ext_handler = NULL; + apt_logger->file_data = NULL; + return TRUE; +} + +APT_DECLARE(apt_bool_t) apt_log_instance_destroy() +{ + if(!apt_logger) { + return FALSE; + } + + if(apt_logger->file_data) { + apt_log_file_close(); + } + apt_logger = NULL; + return TRUE; +} + +APT_DECLARE(apt_logger_t*) apt_log_instance_get() +{ + return apt_logger; +} + +APT_DECLARE(apt_bool_t) apt_log_instance_set(apt_logger_t *logger) +{ + if(apt_logger){ + return FALSE; + } + apt_logger = logger; + return TRUE; +} + +APT_DECLARE(apt_bool_t) apt_log_file_open(const char *dir_path, const char *file_name, apr_size_t max_file_size, apr_size_t max_file_count, apr_pool_t *pool) +{ + const char *log_file_path; + apt_log_file_data_t *file_data; + if(!apt_logger || !dir_path || !file_name) { + return FALSE; + } + + if(apt_logger->file_data) { + return FALSE; + } + + file_data = apr_palloc(pool,sizeof(apt_log_file_data_t)); + file_data->log_dir_path = dir_path; + file_data->log_file_name = file_name; + file_data->cur_file_index = 0; + file_data->cur_size = 0; + file_data->max_file_count = max_file_count; + file_data->max_size = max_file_size; + file_data->mutex = NULL; + file_data->pool = pool; + + if(!file_data->max_size) { + file_data->max_file_count = MAX_LOG_FILE_SIZE; + } + if(!file_data->max_file_count) { + file_data->max_file_count = MAX_LOG_FILE_COUNT; + } + + /* create mutex */ + if(apr_thread_mutex_create(&file_data->mutex,APR_THREAD_MUTEX_DEFAULT,pool) != APR_SUCCESS) { + return FALSE; + } + /* open log file */ + log_file_path = apt_log_file_path_make(file_data); + file_data->file = fopen(log_file_path,"wb"); + if(!file_data->file) { + apr_thread_mutex_destroy(file_data->mutex); + return FALSE; + } + + apt_logger->file_data = file_data; + return TRUE; +} + +APT_DECLARE(apt_bool_t) apt_log_file_close() +{ + apt_log_file_data_t *file_data; + if(!apt_logger || !apt_logger->file_data) { + return FALSE; + } + file_data = apt_logger->file_data; + if(file_data->file) { + /* close log file */ + fclose(file_data->file); + file_data->file = NULL; + /* destroy mutex */ + apr_thread_mutex_destroy(file_data->mutex); + file_data->mutex = NULL; + } + apt_logger->file_data = NULL; + return TRUE; +} + +APT_DECLARE(apt_bool_t) apt_log_output_mode_set(apt_log_output_e mode) +{ + if(!apt_logger) { + return FALSE; + } + apt_logger->mode = mode; + return TRUE; +} + +APT_DECLARE(apt_bool_t) apt_log_priority_set(apt_log_priority_e priority) +{ + if(!apt_logger || priority >= APT_PRIO_COUNT) { + return FALSE; + } + apt_logger->priority = priority; + return TRUE; +} + +APT_DECLARE(apt_bool_t) apt_log_header_set(int header) +{ + if(!apt_logger) { + return FALSE; + } + apt_logger->header = header; + return TRUE; +} + +APT_DECLARE(apt_bool_t) apt_log_ext_handler_set(apt_log_ext_handler_f handler) +{ + if(!apt_logger) { + return FALSE; + } + apt_logger->ext_handler = handler; + return TRUE; +} + +APT_DECLARE(apt_bool_t) apt_log(const char *file, int line, apt_log_priority_e priority, const char *format, ...) +{ + apt_bool_t status = TRUE; + if(!apt_logger) { + return FALSE; + } + if(priority <= apt_logger->priority) { + va_list arg_ptr; + va_start(arg_ptr, format); + if(apt_logger->ext_handler) { + status = apt_logger->ext_handler(file,line,NULL,priority,format,arg_ptr); + } + else { + status = apt_do_log(file,line,priority,format,arg_ptr); + } + va_end(arg_ptr); + } + return status; +} + +static apt_bool_t apt_do_log(const char *file, int line, apt_log_priority_e priority, const char *format, va_list arg_ptr) +{ + char log_entry[MAX_LOG_ENTRY_SIZE]; + apr_size_t offset = 0; + apr_time_exp_t result; + apr_time_t now = apr_time_now(); + apr_time_exp_lt(&result,now); + + if(apt_logger->header & APT_LOG_HEADER_DATE) { + offset += apr_snprintf(log_entry+offset,MAX_LOG_ENTRY_SIZE-offset,"%4d-%02d-%02d ", + result.tm_year+1900, + result.tm_mon+1, + result.tm_mday); + } + if(apt_logger->header & APT_LOG_HEADER_TIME) { + offset += apr_snprintf(log_entry+offset,MAX_LOG_ENTRY_SIZE-offset,"%02d:%02d:%02d:%06d ", + result.tm_hour, + result.tm_min, + result.tm_sec, + result.tm_usec); + } + if(apt_logger->header & APT_LOG_HEADER_MARK) { + offset += apr_snprintf(log_entry+offset,MAX_LOG_ENTRY_SIZE-offset,"%s:%03d ",file,line); + } + if(apt_logger->header & APT_LOG_HEADER_PRIORITY) { + memcpy(log_entry+offset,priority_snames[priority],MAX_PRIORITY_NAME_LENGTH); + offset += MAX_PRIORITY_NAME_LENGTH; + } + + offset += apr_vsnprintf(log_entry+offset,MAX_LOG_ENTRY_SIZE-offset,format,arg_ptr); + log_entry[offset++] = '\n'; + log_entry[offset] = '\0'; + if((apt_logger->mode & APT_LOG_OUTPUT_CONSOLE) == APT_LOG_OUTPUT_CONSOLE) { + printf(log_entry); + } + + if((apt_logger->mode & APT_LOG_OUTPUT_FILE) == APT_LOG_OUTPUT_FILE && apt_logger->file_data) { + apt_log_file_dump(apt_logger->file_data,log_entry,offset); + } + return TRUE; +} + +static const char* apt_log_file_path_make(apt_log_file_data_t *file_data) +{ + char *log_file_path = NULL; + const char *log_file_name = apr_psprintf(file_data->pool,"%s-%d.log",file_data->log_file_name,file_data->cur_file_index); + apr_filepath_merge(&log_file_path,file_data->log_dir_path,log_file_name,0,file_data->pool); + return log_file_path; +} + +static apt_bool_t apt_log_file_dump(apt_log_file_data_t *file_data, const char *log_entry, apr_size_t size) +{ + apr_thread_mutex_lock(file_data->mutex); + + file_data->cur_size += size; + if(file_data->cur_size > file_data->max_size) { + const char *log_file_path; + /* close current log file */ + fclose(file_data->file); + /* roll over the next log file */ + file_data->cur_file_index++; + file_data->cur_file_index %= file_data->max_file_count; + /* open log file */ + log_file_path = apt_log_file_path_make(file_data); + file_data->file = fopen(log_file_path,"wb"); + if(!file_data->file) { + return FALSE; + } + + file_data->cur_size = size; + } + /* write to log file */ + fwrite(log_entry,1,size,file_data->file); + fflush(file_data->file); + + apr_thread_mutex_unlock(file_data->mutex); + return TRUE; +} diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_net.c b/libs/unimrcp/libs/apr-toolkit/src/apt_net.c new file mode 100644 index 0000000000..bf5b8a9f92 --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_net.c @@ -0,0 +1,35 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include "apt_net.h" + +/** Get the IP address (in numeric address string format) by hostname */ +apt_bool_t apt_ip_get(char **addr, apr_pool_t *pool) +{ + apr_sockaddr_t *sockaddr = NULL; + char *hostname = apr_palloc(pool,APRMAXHOSTLEN+1); + if(apr_gethostname(hostname,APRMAXHOSTLEN,pool) != APR_SUCCESS) { + return FALSE; + } + if(apr_sockaddr_info_get(&sockaddr,hostname,APR_INET,0,0,pool) != APR_SUCCESS) { + return FALSE; + } + if(apr_sockaddr_ip_get(addr,sockaddr) != APR_SUCCESS) { + return FALSE; + } + return TRUE; +} diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_net_client_task.c b/libs/unimrcp/libs/apr-toolkit/src/apt_net_client_task.c new file mode 100644 index 0000000000..eb7cf6393d --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_net_client_task.c @@ -0,0 +1,320 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "apt_net_client_task.h" +#include "apt_task.h" +#include "apt_pool.h" +#include "apt_pollset.h" +#include "apt_cyclic_queue.h" +#include "apt_log.h" + + +/** Network client task */ +struct apt_net_client_task_t { + apr_pool_t *pool; + apt_task_t *base; + void *obj; + + apr_size_t max_connection_count; + + apr_thread_mutex_t *guard; + apt_cyclic_queue_t *msg_queue; + apt_pollset_t *pollset; + + const apt_net_client_vtable_t *client_vtable; +}; + +static apt_bool_t apt_net_client_task_msg_signal(apt_task_t *task, apt_task_msg_t *msg); +static apt_bool_t apt_net_client_task_run(apt_task_t *task); +static apt_bool_t apt_net_client_task_on_destroy(apt_task_t *task); + +/** Create connection task */ +APT_DECLARE(apt_net_client_task_t*) apt_net_client_task_create( + apr_size_t max_connection_count, + void *obj, + const apt_net_client_vtable_t *client_vtable, + apt_task_msg_pool_t *msg_pool, + apr_pool_t *pool) +{ + apt_task_vtable_t *vtable; + apt_net_client_task_t *task; + + task = apr_palloc(pool,sizeof(apt_net_client_task_t)); + task->pool = pool; + task->obj = obj; + task->pollset = NULL; + task->max_connection_count = max_connection_count; + + if(!client_vtable || !client_vtable->on_receive) { + return NULL; + } + task->client_vtable = client_vtable; + + task->base = apt_task_create(task,msg_pool,pool); + if(!task->base) { + return NULL; + } + + vtable = apt_task_vtable_get(task->base); + if(vtable) { + vtable->run = apt_net_client_task_run; + vtable->destroy = apt_net_client_task_on_destroy; + vtable->signal_msg = apt_net_client_task_msg_signal; + } + + task->msg_queue = apt_cyclic_queue_create(CYCLIC_QUEUE_DEFAULT_SIZE); + apr_thread_mutex_create(&task->guard,APR_THREAD_MUTEX_UNNESTED,pool); + return task; +} + +/** Virtual destroy handler */ +static apt_bool_t apt_net_client_task_on_destroy(apt_task_t *base) +{ + apt_net_client_task_t *task = apt_task_object_get(base); + if(task->guard) { + apr_thread_mutex_destroy(task->guard); + task->guard = NULL; + } + if(task->msg_queue) { + apt_cyclic_queue_destroy(task->msg_queue); + task->msg_queue = NULL; + } + return TRUE; +} + +/** Destroy connection task. */ +APT_DECLARE(apt_bool_t) apt_net_client_task_destroy(apt_net_client_task_t *task) +{ + return apt_task_destroy(task->base); +} + +/** Start connection task. */ +APT_DECLARE(apt_bool_t) apt_net_client_task_start(apt_net_client_task_t *task) +{ + return apt_task_start(task->base); +} + +/** Terminate connection task. */ +APT_DECLARE(apt_bool_t) apt_net_client_task_terminate(apt_net_client_task_t *task) +{ + return apt_task_terminate(task->base,TRUE); +} + +/** Get task */ +APT_DECLARE(apt_task_t*) apt_net_client_task_base_get(apt_net_client_task_t *task) +{ + return task->base; +} + +/** Get task vtable */ +APT_DECLARE(apt_task_vtable_t*) apt_net_client_task_vtable_get(apt_net_client_task_t *task) +{ + return apt_task_vtable_get(task->base); +} + +/** Get external object */ +APT_DECLARE(void*) apt_net_client_task_object_get(apt_net_client_task_t *task) +{ + return task->obj; +} + +/** Create connection */ +APT_DECLARE(apt_net_client_connection_t*) apt_net_client_connect(apt_net_client_task_t *task, const char *ip, apr_port_t port) +{ + char *local_ip = NULL; + char *remote_ip = NULL; + apr_sockaddr_t *l_sockaddr = NULL; + apr_sockaddr_t *r_sockaddr = NULL; + apt_net_client_connection_t *connection; + apr_pool_t *pool = apt_pool_create(); + if(!pool) { + return NULL; + } + + connection = apr_palloc(pool,sizeof(apt_net_client_connection_t)); + connection->pool = pool; + connection->obj = NULL; + connection->sock = NULL; + + if(apr_sockaddr_info_get(&r_sockaddr,ip,APR_INET,port,0,connection->pool) != APR_SUCCESS) { + apr_pool_destroy(pool); + return NULL; + } + + if(apr_socket_create(&connection->sock,r_sockaddr->family,SOCK_STREAM,APR_PROTO_TCP,connection->pool) != APR_SUCCESS) { + apr_pool_destroy(pool); + return NULL; + } + + apr_socket_opt_set(connection->sock, APR_SO_NONBLOCK, 0); + apr_socket_timeout_set(connection->sock, -1); + apr_socket_opt_set(connection->sock, APR_SO_REUSEADDR, 1); + + if(apr_socket_connect(connection->sock,r_sockaddr) != APR_SUCCESS) { + apr_socket_close(connection->sock); + apr_pool_destroy(pool); + return NULL; + } + + if(apr_socket_addr_get(&l_sockaddr,APR_LOCAL,connection->sock) != APR_SUCCESS) { + apr_socket_close(connection->sock); + apr_pool_destroy(pool); + return NULL; + } + + apr_sockaddr_ip_get(&local_ip,l_sockaddr); + apr_sockaddr_ip_get(&remote_ip,r_sockaddr); + connection->id = apr_psprintf(pool,"%s:%hu <-> %s:%hu", + local_ip,l_sockaddr->port, + remote_ip,r_sockaddr->port); + + memset(&connection->sock_pfd,0,sizeof(apr_pollfd_t)); + connection->sock_pfd.desc_type = APR_POLL_SOCKET; + connection->sock_pfd.reqevents = APR_POLLIN; + connection->sock_pfd.desc.s = connection->sock; + connection->sock_pfd.client_data = connection; + if(apt_pollset_add(task->pollset,&connection->sock_pfd) != TRUE) { + apr_socket_close(connection->sock); + apr_pool_destroy(pool); + return NULL; + } + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Established TCP Connection %s",connection->id); + return connection; +} + +/** Close connection */ +APT_DECLARE(apt_bool_t) apt_net_client_connection_close(apt_net_client_task_t *task, apt_net_client_connection_t *connection) +{ + if(connection->sock) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Close TCP Connection %s",connection->id); + apt_pollset_remove(task->pollset,&connection->sock_pfd); + apr_socket_close(connection->sock); + connection->sock = NULL; + } + return TRUE; +} + +/** Close and destroy connection */ +APT_DECLARE(apt_bool_t) apt_net_client_disconnect(apt_net_client_task_t *task, apt_net_client_connection_t *connection) +{ + apt_net_client_connection_close(task,connection); + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Destroy TCP Connection %s",connection->id); + apr_pool_destroy(connection->pool); + return TRUE; +} + +/** Create the pollset */ +static apt_bool_t apt_net_client_task_pollset_create(apt_net_client_task_t *task) +{ + /* create pollset */ + task->pollset = apt_pollset_create((apr_uint32_t)task->max_connection_count, task->pool); + if(!task->pollset) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Pollset"); + return FALSE; + } + + return TRUE; +} + +/** Destroy the pollset */ +static void apt_net_client_task_pollset_destroy(apt_net_client_task_t *task) +{ + if(task->pollset) { + apt_pollset_destroy(task->pollset); + task->pollset = NULL; + } +} + +static apt_bool_t apt_net_client_task_process(apt_net_client_task_t *task) +{ + apt_bool_t status = TRUE; + apt_bool_t running = TRUE; + apt_task_msg_t *msg; + + do { + apr_thread_mutex_lock(task->guard); + msg = apt_cyclic_queue_pop(task->msg_queue); + apr_thread_mutex_unlock(task->guard); + if(msg) { + status = apt_task_msg_process(task->base,msg); + } + else { + running = FALSE; + } + } + while(running == TRUE); + return status; +} + +static apt_bool_t apt_net_client_task_run(apt_task_t *base) +{ + apt_net_client_task_t *task = apt_task_object_get(base); + apt_bool_t running = TRUE; + apr_status_t status; + apr_int32_t num; + const apr_pollfd_t *ret_pfd; + int i; + + if(!task) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Start Network Client Task"); + return FALSE; + } + + if(apt_net_client_task_pollset_create(task) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Pollset"); + return FALSE; + } + + while(running) { + status = apt_pollset_poll(task->pollset, -1, &num, &ret_pfd); + if(status != APR_SUCCESS) { + continue; + } + for(i = 0; i < num; i++) { + if(apt_pollset_is_wakeup(task->pollset,&ret_pfd[i])) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Process Control Message"); + if(apt_net_client_task_process(task) == FALSE) { + running = FALSE; + break; + } + continue; + } + + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Process Message"); + task->client_vtable->on_receive(task,ret_pfd[i].client_data); + } + } + + apt_net_client_task_pollset_destroy(task); + + apt_task_child_terminate(task->base); + return TRUE; +} + +static apt_bool_t apt_net_client_task_msg_signal(apt_task_t *base, apt_task_msg_t *msg) +{ + apt_bool_t status; + apt_net_client_task_t *task = apt_task_object_get(base); + apr_thread_mutex_lock(task->guard); + status = apt_cyclic_queue_push(task->msg_queue,msg); + apr_thread_mutex_unlock(task->guard); + if(apt_pollset_wakeup(task->pollset) != TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Signal Control Message"); + status = FALSE; + } + return status; +} diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_net_server_task.c b/libs/unimrcp/libs/apr-toolkit/src/apt_net_server_task.c new file mode 100644 index 0000000000..77c48b980a --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_net_server_task.c @@ -0,0 +1,391 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "apt_net_server_task.h" +#include "apt_task.h" +#include "apt_pool.h" +#include "apt_pollset.h" +#include "apt_cyclic_queue.h" +#include "apt_log.h" + + +/** Network server task */ +struct apt_net_server_task_t { + apr_pool_t *pool; + apt_task_t *base; + void *obj; + + apr_size_t max_connection_count; + + apr_thread_mutex_t *guard; + apt_cyclic_queue_t *msg_queue; + apt_pollset_t *pollset; + + /* Listening socket descriptor */ + apr_sockaddr_t *sockaddr; + apr_socket_t *listen_sock; + apr_pollfd_t listen_sock_pfd; + + const apt_net_server_vtable_t *server_vtable; +}; + +static apt_bool_t apt_net_server_task_msg_signal(apt_task_t *task, apt_task_msg_t *msg); +static apt_bool_t apt_net_server_task_run(apt_task_t *task); +static apt_bool_t apt_net_server_task_on_destroy(apt_task_t *task); + +/** Create connection task */ +APT_DECLARE(apt_net_server_task_t*) apt_net_server_task_create( + const char *listen_ip, + apr_port_t listen_port, + apr_size_t max_connection_count, + void *obj, + const apt_net_server_vtable_t *server_vtable, + apt_task_msg_pool_t *msg_pool, + apr_pool_t *pool) +{ + apt_task_vtable_t *vtable; + apt_net_server_task_t *task; + + task = apr_palloc(pool,sizeof(apt_net_server_task_t)); + task->pool = pool; + task->obj = obj; + task->sockaddr = NULL; + task->listen_sock = NULL; + task->pollset = NULL; + task->max_connection_count = max_connection_count; + + apr_sockaddr_info_get(&task->sockaddr,listen_ip,APR_INET,listen_port,0,task->pool); + if(!task->sockaddr) { + return NULL; + } + + if(!server_vtable || !server_vtable->on_connect || + !server_vtable->on_disconnect || !server_vtable->on_receive) { + return NULL; + } + task->server_vtable = server_vtable; + + task->base = apt_task_create(task,msg_pool,pool); + if(!task->base) { + return NULL; + } + + vtable = apt_task_vtable_get(task->base); + if(vtable) { + vtable->run = apt_net_server_task_run; + vtable->destroy = apt_net_server_task_on_destroy; + vtable->signal_msg = apt_net_server_task_msg_signal; + } + + task->msg_queue = apt_cyclic_queue_create(CYCLIC_QUEUE_DEFAULT_SIZE); + apr_thread_mutex_create(&task->guard,APR_THREAD_MUTEX_UNNESTED,pool); + return task; +} + +/** Virtual destroy handler */ +static apt_bool_t apt_net_server_task_on_destroy(apt_task_t *base) +{ + apt_net_server_task_t *task = apt_task_object_get(base); + if(task->guard) { + apr_thread_mutex_destroy(task->guard); + task->guard = NULL; + } + if(task->msg_queue) { + apt_cyclic_queue_destroy(task->msg_queue); + task->msg_queue = NULL; + } + return TRUE; +} + +/** Destroy connection task. */ +APT_DECLARE(apt_bool_t) apt_net_server_task_destroy(apt_net_server_task_t *task) +{ + return apt_task_destroy(task->base); +} + +/** Start connection task. */ +APT_DECLARE(apt_bool_t) apt_net_server_task_start(apt_net_server_task_t *task) +{ + return apt_task_start(task->base); +} + +/** Terminate connection task. */ +APT_DECLARE(apt_bool_t) apt_net_server_task_terminate(apt_net_server_task_t *task) +{ + return apt_task_terminate(task->base,TRUE); +} + +/** Get task */ +APT_DECLARE(apt_task_t*) apt_net_server_task_base_get(apt_net_server_task_t *task) +{ + return task->base; +} + +/** Get task vtable */ +APT_DECLARE(apt_task_vtable_t*) apt_net_server_task_vtable_get(apt_net_server_task_t *task) +{ + return apt_task_vtable_get(task->base); +} + +/** Get external object */ +APT_DECLARE(void*) apt_net_server_task_object_get(apt_net_server_task_t *task) +{ + return task->obj; +} + + +/** Create listening socket and add to pollset */ +static apt_bool_t apt_net_server_task_listen_socket_create(apt_net_server_task_t *task) +{ + apr_status_t status; + if(!task->sockaddr) { + return FALSE; + } + + /* create listening socket */ + status = apr_socket_create(&task->listen_sock, task->sockaddr->family, SOCK_STREAM, APR_PROTO_TCP, task->pool); + if(status != APR_SUCCESS) { + return FALSE; + } + + apr_socket_opt_set(task->listen_sock, APR_SO_NONBLOCK, 0); + apr_socket_timeout_set(task->listen_sock, -1); + apr_socket_opt_set(task->listen_sock, APR_SO_REUSEADDR, 1); + + status = apr_socket_bind(task->listen_sock, task->sockaddr); + if(status != APR_SUCCESS) { + apr_socket_close(task->listen_sock); + task->listen_sock = NULL; + return FALSE; + } + status = apr_socket_listen(task->listen_sock, SOMAXCONN); + if(status != APR_SUCCESS) { + apr_socket_close(task->listen_sock); + task->listen_sock = NULL; + return FALSE; + } + + memset(&task->listen_sock_pfd,0,sizeof(apr_pollfd_t)); + task->listen_sock_pfd.desc_type = APR_POLL_SOCKET; + task->listen_sock_pfd.reqevents = APR_POLLIN; + task->listen_sock_pfd.desc.s = task->listen_sock; + task->listen_sock_pfd.client_data = task->listen_sock; + if(apt_pollset_add(task->pollset, &task->listen_sock_pfd) != TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Add Listen Socket to Pollset"); + apr_socket_close(task->listen_sock); + task->listen_sock = NULL; + } + + return TRUE; +} + +/** Remove from pollset and destroy listening socket */ +static void apt_net_server_task_listen_socket_destroy(apt_net_server_task_t *task) +{ + apt_pollset_remove(task->pollset,&task->listen_sock_pfd); + + if(task->listen_sock) { + apr_socket_close(task->listen_sock); + task->listen_sock = NULL; + } +} + +/** Create the pollset */ +static apt_bool_t apt_net_server_task_pollset_create(apt_net_server_task_t *task) +{ + /* create pollset */ + task->pollset = apt_pollset_create((apr_uint32_t)task->max_connection_count + 1, task->pool); + if(!task->pollset) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Pollset"); + return FALSE; + } + + /* create listening socket */ + if(apt_net_server_task_listen_socket_create(task) != TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Listen Socket"); + } + + return TRUE; +} + +/** Destroy the pollset */ +static void apt_net_server_task_pollset_destroy(apt_net_server_task_t *task) +{ + apt_net_server_task_listen_socket_destroy(task); + if(task->pollset) { + apt_pollset_destroy(task->pollset); + task->pollset = NULL; + } +} + +static apt_bool_t apt_net_server_task_process(apt_net_server_task_t *task) +{ + apt_bool_t status = TRUE; + apt_bool_t running = TRUE; + apt_task_msg_t *msg; + + do { + apr_thread_mutex_lock(task->guard); + msg = apt_cyclic_queue_pop(task->msg_queue); + apr_thread_mutex_unlock(task->guard); + if(msg) { + status = apt_task_msg_process(task->base,msg); + } + else { + running = FALSE; + } + } + while(running == TRUE); + return status; +} + +static apt_bool_t apt_net_server_task_accept(apt_net_server_task_t *task) +{ + char *local_ip = NULL; + char *remote_ip = NULL; + apr_sockaddr_t *l_sockaddr = NULL; + apr_sockaddr_t *r_sockaddr = NULL; + apt_net_server_connection_t *connection; + apr_pool_t *pool = apt_pool_create(); + if(!pool) { + return FALSE; + } + + connection = apr_palloc(pool,sizeof(apt_net_server_connection_t)); + connection->pool = pool; + connection->obj = NULL; + connection->sock = NULL; + connection->client_ip = NULL; + + if(apr_socket_accept(&connection->sock,task->listen_sock,connection->pool) != APR_SUCCESS) { + apr_pool_destroy(pool); + return FALSE; + } + + if(apr_socket_addr_get(&l_sockaddr,APR_LOCAL,connection->sock) != APR_SUCCESS || + apr_socket_addr_get(&r_sockaddr,APR_REMOTE,connection->sock) != APR_SUCCESS) { + apr_pool_destroy(pool); + return FALSE; + } + + apr_sockaddr_ip_get(&local_ip,l_sockaddr); + apr_sockaddr_ip_get(&remote_ip,r_sockaddr); + connection->client_ip = remote_ip; + connection->id = apr_psprintf(pool,"%s:%hu <-> %s:%hu", + local_ip,l_sockaddr->port, + remote_ip,r_sockaddr->port); + + memset(&connection->sock_pfd,0,sizeof(apr_pollfd_t)); + connection->sock_pfd.desc_type = APR_POLL_SOCKET; + connection->sock_pfd.reqevents = APR_POLLIN; + connection->sock_pfd.desc.s = connection->sock; + connection->sock_pfd.client_data = connection; + if(apt_pollset_add(task->pollset,&connection->sock_pfd) != TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Add to Pollset"); + apr_socket_close(connection->sock); + apr_pool_destroy(pool); + return FALSE; + } + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Accepted TCP Connection %s",connection->id); + task->server_vtable->on_connect(task,connection); + return TRUE; +} + +static apt_bool_t apt_net_server_task_run(apt_task_t *base) +{ + apt_net_server_task_t *task = apt_task_object_get(base); + apt_bool_t running = TRUE; + apr_status_t status; + apr_int32_t num; + const apr_pollfd_t *ret_pfd; + int i; + + if(!task) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Start Network Server Task"); + return FALSE; + } + + if(apt_net_server_task_pollset_create(task) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Pollset"); + return FALSE; + } + + while(running) { + status = apt_pollset_poll(task->pollset, -1, &num, &ret_pfd); + if(status != APR_SUCCESS) { + continue; + } + for(i = 0; i < num; i++) { + if(ret_pfd[i].desc.s == task->listen_sock) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Accept Connection"); + apt_net_server_task_accept(task); + continue; + } + if(apt_pollset_is_wakeup(task->pollset,&ret_pfd[i])) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Process Control Message"); + if(apt_net_server_task_process(task) == FALSE) { + running = FALSE; + break; + } + continue; + } + + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Process Message"); + task->server_vtable->on_receive(task,ret_pfd[i].client_data); + } + } + + apt_net_server_task_pollset_destroy(task); + + apt_task_child_terminate(task->base); + return TRUE; +} + +static apt_bool_t apt_net_server_task_msg_signal(apt_task_t *base, apt_task_msg_t *msg) +{ + apt_bool_t status; + apt_net_server_task_t *task = apt_task_object_get(base); + apr_thread_mutex_lock(task->guard); + status = apt_cyclic_queue_push(task->msg_queue,msg); + apr_thread_mutex_unlock(task->guard); + if(apt_pollset_wakeup(task->pollset) != TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Signal Control Message"); + status = FALSE; + } + return status; +} + + +/** Close connection */ +APT_DECLARE(apt_bool_t) apt_net_server_connection_close(apt_net_server_task_t *task, apt_net_server_connection_t *connection) +{ + if(connection->sock) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Close TCP Connection %s",connection->id); + apt_pollset_remove(task->pollset,&connection->sock_pfd); + apr_socket_close(connection->sock); + connection->sock = NULL; + task->server_vtable->on_disconnect(task,connection); + } + return TRUE; +} + +/** Destroy connection */ +APT_DECLARE(void) apt_net_server_connection_destroy(apt_net_server_connection_t *connection) +{ + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Destroy TCP Connection %s",connection->id); + apr_pool_destroy(connection->pool); +} diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_obj_list.c b/libs/unimrcp/libs/apr-toolkit/src/apt_obj_list.c new file mode 100644 index 0000000000..a1d320a8aa --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_obj_list.c @@ -0,0 +1,151 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WIN32 +#pragma warning(disable: 4127) +#endif +#include +#include "apt_obj_list.h" + +struct apt_list_elem_t { + APR_RING_ENTRY(apt_list_elem_t) link; + void *obj; +}; + +struct apt_obj_list_t { + APR_RING_HEAD(apt_list_head_t, apt_list_elem_t) head; + apr_pool_t *pool; +}; + + + +APT_DECLARE(apt_obj_list_t*) apt_list_create(apr_pool_t *pool) +{ + apt_obj_list_t *list = apr_palloc(pool, sizeof(apt_obj_list_t)); + list->pool = pool; + APR_RING_INIT(&list->head, apt_list_elem_t, link); + return list; +} + +APT_DECLARE(void) apt_list_destroy(apt_obj_list_t *list) +{ + /* nothing to do, the list is allocated from the pool */ +} + +APT_DECLARE(apt_list_elem_t*) apt_list_push_back(apt_obj_list_t *list, void *obj, apr_pool_t *pool) +{ + apt_list_elem_t *elem = apr_palloc(pool,sizeof(apt_list_elem_t)); + elem->obj = obj; + + APR_RING_INSERT_TAIL(&list->head,elem,apt_list_elem_t,link); + return elem; +} + +APT_DECLARE(void*) apt_list_pop_front(apt_obj_list_t *list) +{ + apt_list_elem_t *elem; + if(APR_RING_EMPTY(&list->head,apt_list_elem_t,link)) { + return NULL; + } + elem = APR_RING_FIRST(&list->head); + APR_RING_REMOVE(elem,link); + return elem->obj; +} + +APT_DECLARE(void*) apt_list_head(apt_obj_list_t *list) +{ + apt_list_elem_t *elem; + if(APR_RING_EMPTY(&list->head,apt_list_elem_t,link)) { + return NULL; + } + elem = APR_RING_FIRST(&list->head); + return elem->obj; +} + +APT_DECLARE(void*) apt_obj_list_tail(apt_obj_list_t *list) +{ + apt_list_elem_t *elem; + if(APR_RING_EMPTY(&list->head,apt_list_elem_t,link)) { + return NULL; + } + elem = APR_RING_LAST(&list->head); + return elem->obj; +} + +APT_DECLARE(apt_list_elem_t*) apt_list_first_elem_get(apt_obj_list_t *list) +{ + if(APR_RING_EMPTY(&list->head,apt_list_elem_t,link)) { + return NULL; + } + return APR_RING_FIRST(&list->head); +} + +APT_DECLARE(apt_list_elem_t*) apt_list_last_elem_get(apt_obj_list_t *list) +{ + if(APR_RING_EMPTY(&list->head,apt_list_elem_t,link)) { + return NULL; + } + return APR_RING_LAST(&list->head); +} + +APT_DECLARE(apt_list_elem_t*) apt_list_next_elem_get(apt_obj_list_t *list, apt_list_elem_t *elem) +{ + apt_list_elem_t *next_elem = APR_RING_NEXT(elem,link); + if(next_elem == APR_RING_SENTINEL(&list->head,apt_list_elem_t,link)) { + next_elem = NULL; + } + return next_elem; +} + +APT_DECLARE(apt_list_elem_t*) apt_list_prev_elem_get(apt_obj_list_t *list, apt_list_elem_t *elem) +{ + apt_list_elem_t *prev_elem = APR_RING_PREV(elem,link); + if(prev_elem == APR_RING_SENTINEL(&list->head,apt_list_elem_t,link)) { + prev_elem = NULL; + } + return prev_elem; +} + +APT_DECLARE(apt_list_elem_t*) apt_list_elem_insert(apt_obj_list_t *list, apt_list_elem_t *elem, void *obj, apr_pool_t *pool) +{ + apt_list_elem_t *new_elem = apr_palloc(pool,sizeof(apt_list_elem_t)); + new_elem->obj = obj; + APR_RING_INSERT_BEFORE(elem,new_elem,link); + return new_elem; +} + +APT_DECLARE(apt_list_elem_t*) apt_list_elem_remove(apt_obj_list_t *list, apt_list_elem_t *elem) +{ + apt_list_elem_t *next_elem = APR_RING_NEXT(elem,link); + APR_RING_REMOVE(elem,link); + if(next_elem == APR_RING_SENTINEL(&list->head,apt_list_elem_t,link)) { + next_elem = NULL; + } + return next_elem; +} + +APT_DECLARE(apt_bool_t) apt_list_is_empty(apt_obj_list_t *list) +{ + if(APR_RING_EMPTY(&list->head,apt_list_elem_t,link)) { + return TRUE; + } + return FALSE; +} + +APT_DECLARE(void*) apt_list_elem_object_get(apt_list_elem_t *elem) +{ + return elem->obj; +} diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_pair.c b/libs/unimrcp/libs/apr-toolkit/src/apt_pair.c new file mode 100644 index 0000000000..816eea264e --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_pair.c @@ -0,0 +1,86 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "apt_pair.h" + +/** Create array of name-value pairs */ +APT_DECLARE(apt_pair_arr_t*) apt_pair_array_create(apr_size_t initial_size, apr_pool_t *pool) +{ + return apr_array_make(pool,(int)initial_size,sizeof(apt_pair_t)); +} + +/** Copy array of name-value pairs */ +APT_DECLARE(apt_pair_arr_t*) apt_pair_array_copy(const apt_pair_arr_t *src_arr, apr_pool_t *pool) +{ + int i; + const apt_pair_t *src_pair; + apt_pair_t *pair; + apt_pair_arr_t *arr; + if(!src_arr) { + return NULL; + } + arr = apr_array_copy(pool,src_arr); + for(i=0; inelts; i++) { + pair = (apt_pair_t*)arr->elts + i; + src_pair = (const apt_pair_t*)src_arr->elts + i; + apt_pair_copy(pair,src_pair,pool); + } + return arr; +} + + +/** Append name-value pair */ +APT_DECLARE(apt_bool_t) apt_pair_array_append(apt_pair_arr_t *arr, const apt_str_t *name, const apt_str_t *value, apr_pool_t *pool) +{ + apt_pair_t *pair = apr_array_push(arr); + apt_pair_init(pair); + if(name) { + apt_string_copy(&pair->name,name,pool); + } + if(value) { + apt_string_copy(&pair->value,value,pool); + } + return TRUE; +} + +/** Find name-value pair by name */ +APT_DECLARE(const apt_pair_t*) apt_pair_array_find(const apt_pair_arr_t *arr, const apt_str_t *name) +{ + int i; + apt_pair_t *pair; + for(i=0; inelts; i++) { + pair = (apt_pair_t*)arr->elts + i; + if(apt_string_compare(&pair->name,name) == TRUE) { + return pair; + } + } + return NULL; +} + +/** Get size of pair array */ +APT_DECLARE(int) apt_pair_array_size_get(const apt_pair_arr_t *arr) +{ + return arr->nelts; +} + +/** Get name-value pair by id */ +APT_DECLARE(const apt_pair_t*) apt_pair_array_get(const apt_pair_arr_t *arr, int id) +{ + if(id < arr->nelts) { + return (apt_pair_t*)arr->elts + id; + } + return NULL; +} diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_pollset.c b/libs/unimrcp/libs/apr-toolkit/src/apt_pollset.c new file mode 100644 index 0000000000..87db7f8c04 --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_pollset.c @@ -0,0 +1,323 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include "apt_pollset.h" +#include "apt_log.h" + +struct apt_pollset_t { + /** APR pollset */ + apr_pollset_t *base; +#ifdef WIN32 + /** Socket descriptors used for wakeup */ + apr_socket_t *wakeup_pipe[2]; +#else + /** Pipe descriptors used for wakeup */ + apr_file_t *wakeup_pipe[2]; +#endif + /** Builtin wakeup poll descriptor */ + apr_pollfd_t wakeup_pfd; + + /** Pool to allocate memory from */ + apr_pool_t *pool; +}; + +static apt_bool_t apt_wakeup_pipe_create(apt_pollset_t *pollset); +static apt_bool_t apt_wakeup_pipe_destroy(apt_pollset_t *pollset); + +/** Create interruptable pollset on top of APR pollset */ +APT_DECLARE(apt_pollset_t*) apt_pollset_create(apr_uint32_t size, apr_pool_t *pool) +{ + apt_pollset_t *pollset = apr_palloc(pool,sizeof(apt_pollset_t)); + pollset->pool = pool; + memset(&pollset->wakeup_pfd,0,sizeof(pollset->wakeup_pfd)); + + /* create pollset with max number of descriptors size+1, + where +1 is builtin wakeup descriptor */ + if(apr_pollset_create(&pollset->base,size+1,pool,0) != APR_SUCCESS) { + return NULL; + } + + /* create wakeup pipe */ + if(apt_wakeup_pipe_create(pollset) != TRUE) { + apr_pollset_destroy(pollset->base); + return NULL; + } + + /* add wakeup pipe to pollset */ + if(apr_pollset_add(pollset->base,&pollset->wakeup_pfd) != APR_SUCCESS) { + apt_wakeup_pipe_destroy(pollset); + apr_pollset_destroy(pollset->base); + return NULL; + } + return pollset; +} + +/** Destroy pollset */ +APT_DECLARE(apt_bool_t) apt_pollset_destroy(apt_pollset_t *pollset) +{ + /* remove wakeup pipe from pollset */ + apr_pollset_remove(pollset->base,&pollset->wakeup_pfd); + /* destroy wakeup pipe */ + apt_wakeup_pipe_destroy(pollset); + /* destroy pollset */ + apr_pollset_destroy(pollset->base); + return TRUE; +} + +/** Add pollset descriptor to a pollset */ +APT_DECLARE(apt_bool_t) apt_pollset_add(apt_pollset_t *pollset, const apr_pollfd_t *descriptor) +{ + return (apr_pollset_add(pollset->base,descriptor) == APR_SUCCESS) ? TRUE : FALSE; +} + +/** Remove pollset descriptor from a pollset */ +APT_DECLARE(apt_bool_t) apt_pollset_remove(apt_pollset_t *pollset, const apr_pollfd_t *descriptor) +{ + return (apr_pollset_remove(pollset->base,descriptor) == APR_SUCCESS) ? TRUE : FALSE; +} + +/** Block for activity on the descriptor(s) in a pollset */ +APT_DECLARE(apr_status_t) apt_pollset_poll( + apt_pollset_t *pollset, + apr_interval_time_t timeout, + apr_int32_t *num, + const apr_pollfd_t **descriptors) +{ + return apr_pollset_poll(pollset->base,timeout,num,descriptors); +} + +/** Interrupt the blocked poll call */ +APT_DECLARE(apt_bool_t) apt_pollset_wakeup(apt_pollset_t *pollset) +{ + apt_bool_t status = TRUE; +#ifdef WIN32 + char tmp = 0; + apr_size_t len = sizeof(tmp); + if(apr_socket_send(pollset->wakeup_pipe[1],&tmp,&len) != APR_SUCCESS) { + status = FALSE; + } +#else + if(apr_file_putc(1, pollset->wakeup_pipe[1]) != APR_SUCCESS) { + status = FALSE; + } +#endif + return status; +} + +/** Match against builtin wake up descriptor in a pollset */ +APT_DECLARE(apt_bool_t) apt_pollset_is_wakeup(apt_pollset_t *pollset, const apr_pollfd_t *descriptor) +{ + apt_bool_t status = FALSE; +#ifdef WIN32 + if(descriptor->desc.s == pollset->wakeup_pipe[0]) { + char rb[512]; + apr_size_t nr = sizeof(rb); + + /* simply read out from the input side of the pipe all the data. */ + while(apr_socket_recv(pollset->wakeup_pipe[0], rb, &nr) == APR_SUCCESS) { + if(nr != sizeof(rb)) { + break; + } + } + status = TRUE; + } +#else + if(descriptor->desc.f == pollset->wakeup_pipe[0]) { + char rb[512]; + apr_size_t nr = sizeof(rb); + + /* simply read out from the input side of the pipe all the data. */ + while(apr_file_read(pollset->wakeup_pipe[0], rb, &nr) == APR_SUCCESS) { + if(nr != sizeof(rb)) { + break; + } + } + status = TRUE; + } +#endif + return status; +} + +#ifdef WIN32 +static apr_status_t socket_pipe_create(apr_socket_t **rd, apr_socket_t **wr, apr_pool_t *pool) +{ + static int id = 0; + + apr_socket_t *ls = NULL; + apr_sockaddr_t *pa = NULL; + apr_sockaddr_t *ca = NULL; + apr_size_t nrd; + int uid[2]; + int iid[2]; + + /* Create the unique socket identifier + * so that we know the connection originated + * from us. + */ + uid[0] = getpid(); + uid[1] = id++; + if(apr_socket_create(&ls, AF_INET, SOCK_STREAM, APR_PROTO_TCP, pool) != APR_SUCCESS) { + return apr_get_netos_error(); + } + apr_socket_opt_set(ls, APR_SO_REUSEADDR, 1); + + if(apr_sockaddr_info_get(&pa,"127.0.0.1",APR_INET,0,0,pool) != APR_SUCCESS) { + apr_socket_close(ls); + return apr_get_netos_error(); + } + + if(apr_socket_bind(ls, pa) != APR_SUCCESS) { + apr_socket_close(ls); + return apr_get_netos_error(); + } + + if(apr_socket_addr_get(&ca,APR_LOCAL,ls) != APR_SUCCESS) { + apr_socket_close(ls); + return apr_get_netos_error(); + } + + if(apr_socket_listen(ls,1) != APR_SUCCESS) { + apr_socket_close(ls); + return apr_get_netos_error(); + } + + if(apr_socket_create(wr, AF_INET, SOCK_STREAM, APR_PROTO_TCP, pool) != APR_SUCCESS) { + apr_socket_close(ls); + return apr_get_netos_error(); + } + apr_socket_opt_set(*wr, APR_SO_REUSEADDR, 1); + + if(apr_socket_connect(*wr, ca) != APR_SUCCESS) { + apr_socket_close(ls); + apr_socket_close(*wr); + return apr_get_netos_error(); + } + nrd = sizeof(uid); + if(apr_socket_send(*wr, (char *)uid, &nrd) != APR_SUCCESS) { + apr_socket_close(ls); + apr_socket_close(*wr); + return apr_get_netos_error(); + } + + apr_socket_opt_set(ls, APR_SO_NONBLOCK, 0); + /* Listening socket is blocking by now. The accept should + * return immediatelly because we connected already. + */ + if(apr_socket_accept(rd, ls, pool) != APR_SUCCESS) { + apr_socket_close(ls); + apr_socket_close(*wr); + return apr_get_netos_error(); + } + + /* Put read side of the pipe to the blocking mode */ + apr_socket_opt_set(*rd, APR_SO_NONBLOCK, 0); + + for (;;) { + /* Verify the connection by reading the sent identification */ + nrd = sizeof(iid); + if(apr_socket_recv(*rd, (char *)iid, &nrd) != APR_SUCCESS) { + apr_socket_close(ls); + apr_socket_close(*wr); + apr_socket_close(*rd); + return apr_get_netos_error(); + } + if(nrd == sizeof(iid)) { + if(memcmp(uid, iid, sizeof(uid)) == 0) { + /* Wow, we recived what we sent */ + break; + } + } + } + + /* We don't need the listening socket any more */ + apr_socket_close(ls); + return APR_SUCCESS; +} + +/** Create a dummy wakeup pipe for interrupting the poller */ +static apt_bool_t apt_wakeup_pipe_create(apt_pollset_t *pollset) +{ + apr_socket_t *rd = NULL; + apr_socket_t *wr = NULL; + apr_status_t rv; + rv = socket_pipe_create(&rd,&wr,pollset->pool); + if(rv != APR_SUCCESS) { + char err_str[256]; + apr_strerror(rv,err_str,sizeof(err_str)); + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Wakeup Pipe: %s",err_str); + return FALSE; + } + pollset->wakeup_pfd.reqevents = APR_POLLIN; + pollset->wakeup_pfd.desc_type = APR_POLL_SOCKET; + pollset->wakeup_pfd.desc.s = rd; + + pollset->wakeup_pipe[0] = rd; + pollset->wakeup_pipe[1] = wr; + return TRUE; +} + +/** Destroy wakeup pipe */ +static apt_bool_t apt_wakeup_pipe_destroy(apt_pollset_t *pollset) +{ + /* Close both sides of the wakeup pipe */ + if(pollset->wakeup_pipe[0]) { + apr_socket_close(pollset->wakeup_pipe[0]); + pollset->wakeup_pipe[0] = NULL; + } + if(pollset->wakeup_pipe[1]) { + apr_socket_close(pollset->wakeup_pipe[1]); + pollset->wakeup_pipe[1] = NULL; + } + return TRUE; +} + +#else + +/** Create a dummy wakeup pipe for interrupting the poller */ +static apt_bool_t apt_wakeup_pipe_create(apt_pollset_t *pollset) +{ + apr_file_t *file_in = NULL; + apr_file_t *file_out = NULL; + + if(apr_file_pipe_create(&file_in,&file_out,pollset->pool) != APR_SUCCESS) { + return FALSE; + } + pollset->wakeup_pfd.reqevents = APR_POLLIN; + pollset->wakeup_pfd.desc_type = APR_POLL_FILE; + pollset->wakeup_pfd.desc.f = file_in; + + pollset->wakeup_pipe[0] = file_in; + pollset->wakeup_pipe[1] = file_out; + return TRUE; +} + +/** Destroy wakeup pipe */ +static apt_bool_t apt_wakeup_pipe_destroy(apt_pollset_t *pollset) +{ + /* Close both sides of the wakeup pipe */ + if(pollset->wakeup_pipe[0]) { + apr_file_close(pollset->wakeup_pipe[0]); + pollset->wakeup_pipe[0] = NULL; + } + if(pollset->wakeup_pipe[1]) { + apr_file_close(pollset->wakeup_pipe[1]); + pollset->wakeup_pipe[1] = NULL; + } + return TRUE; +} + +#endif diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_pool.c b/libs/unimrcp/libs/apr-toolkit/src/apt_pool.c new file mode 100644 index 0000000000..9de0e3fa93 --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_pool.c @@ -0,0 +1,47 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "apt_pool.h" + +//#define OWN_ALLOCATOR_PER_POOL + +APT_DECLARE(apr_pool_t*) apt_pool_create() +{ + apr_pool_t *pool = NULL; + +#ifdef OWN_ALLOCATOR_PER_POOL + apr_allocator_t *allocator = NULL; + apr_thread_mutex_t *mutex = NULL; + + if(apr_allocator_create(&allocator) == APR_SUCCESS) { + if(apr_pool_create_ex(&pool,NULL,NULL,allocator) == APR_SUCCESS) { + apr_allocator_owner_set(allocator,pool); + apr_thread_mutex_create(&mutex,APR_THREAD_MUTEX_NESTED,pool); + apr_allocator_mutex_set(allocator,mutex); + } + } +#else + apr_pool_create(&pool,NULL); +#endif + return pool; +} + +APT_DECLARE(apr_pool_t*) apt_subpool_create(apr_pool_t *parent) +{ + apr_pool_t *pool = NULL; + apr_pool_create(&pool,parent); + return pool; +} diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_string_table.c b/libs/unimrcp/libs/apr-toolkit/src/apt_string_table.c new file mode 100644 index 0000000000..cc0c6be73b --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_string_table.c @@ -0,0 +1,66 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include "apt_string_table.h" + +/* Get the string by a given id. */ +APT_DECLARE(const apt_str_t*) apt_string_table_str_get(const apt_str_table_item_t table[], apr_size_t size, apr_size_t id) +{ + if(id < size) { + return &table[id].value; + } + return NULL; +} + +/* Find the id associated with a given string from the table */ +APT_DECLARE(apr_size_t) apt_string_table_id_find(const apt_str_table_item_t table[], apr_size_t size, const apt_str_t *value) +{ + /* Key character is stored within each apt_string_table_item. + At first, key characters must be matched in a loop crossing the items. + Then whole strings should be compared only for the matched item. + Key characters should be automatically generated once for a given string table. */ + + apr_size_t i; + const apt_str_table_item_t *item; + for(i=0; ivalue.length != value->length) { + /* lengths of th strings differ, just contninue */ + continue; + } + /* check whether key is available */ + if(item->key < value->length) { + /* check whether values are matched by key (using no case compare) */ + if(value->length == item->value.length && + tolower(item->value.buf[item->key]) == tolower(value->buf[item->key])) { + /* whole strings must be compared to ensure, should be done only once for each lookup */ + if(apt_string_compare(&item->value,value) == TRUE) { + return i; + } + } + } + else { + /* no key available, just compare whole strings */ + if(apt_string_compare(&item->value,value) == TRUE) { + return i; + } + } + } + + /* no match found, return invalid id */ + return size; +} diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_task.c b/libs/unimrcp/libs/apr-toolkit/src/apt_task.c new file mode 100644 index 0000000000..0c8f3d0465 --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_task.c @@ -0,0 +1,417 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include +#include "apt_task.h" +#include "apt_obj_list.h" +#include "apt_log.h" + +/** Internal states of the task */ +typedef enum { + TASK_STATE_IDLE, /**< no task activity */ + TASK_STATE_START_REQUESTED, /**< task start is requested and is in progress */ + TASK_STATE_RUNNING, /**< task is running */ + TASK_STATE_TERMINATE_REQUESTED /**< task termination is requested and is in progress */ +} apt_task_state_t; + +struct apt_task_t { + void *obj; /* external object associated with the task */ + apr_pool_t *pool; /* memory pool to allocate task data from */ + apt_task_msg_pool_t *msg_pool; /* message pool to allocate task messages from */ + apr_thread_mutex_t *data_guard; /* mutex to protect task data */ + apr_thread_t *thread_handle; /* thread handle */ + apt_task_state_t state; /* current task state */ + apt_task_vtable_t vtable; /* table of virtual methods */ + apt_task_t *parent_task; /* parent (master) task */ + apt_obj_list_t *child_tasks; /* list of the child (slave) tasks */ + apr_size_t pending_start; /* number of pending start requests */ + apr_size_t pending_term; /* number of pending terminate requests */ + const char *name; /* name of the task */ +}; + +static void* APR_THREAD_FUNC apt_task_run(apr_thread_t *thread_handle, void *data); +static apt_bool_t apt_task_terminate_request(apt_task_t *task); + + +APT_DECLARE(apt_task_t*) apt_task_create( + void *obj, + apt_task_msg_pool_t *msg_pool, + apr_pool_t *pool) +{ + apt_task_t *task = apr_palloc(pool,sizeof(apt_task_t)); + task->obj = obj; + task->pool = pool; + task->msg_pool = msg_pool; + + if(!task->msg_pool) { + task->msg_pool = apt_task_msg_pool_create_dynamic(0,pool); + } + + task->state = TASK_STATE_IDLE; + task->thread_handle = NULL; + if(apr_thread_mutex_create(&task->data_guard, APR_THREAD_MUTEX_DEFAULT, task->pool) != APR_SUCCESS) { + return NULL; + } + + /* reset and copy vtable */ + apt_task_vtable_reset(&task->vtable); + task->vtable.terminate = apt_task_terminate_request; + + task->parent_task = NULL; + task->child_tasks = apt_list_create(pool); + task->pending_start = 0; + task->pending_term = 0; + task->name = "Task"; + return task; +} + +APT_DECLARE(apt_bool_t) apt_task_destroy(apt_task_t *task) +{ + apt_task_t *child_task = NULL; + apt_list_elem_t *elem = apt_list_first_elem_get(task->child_tasks); + /* walk through the list of the child tasks and destroy them */ + while(elem) { + child_task = apt_list_elem_object_get(elem); + if(child_task) { + apt_task_destroy(child_task); + } + elem = apt_list_next_elem_get(task->child_tasks,elem); + } + + if(task->state != TASK_STATE_IDLE) { + apt_task_wait_till_complete(task); + } + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Destroy %s",task->name); + if(task->vtable.destroy) { + task->vtable.destroy(task); + } + + apr_thread_mutex_destroy(task->data_guard); + return TRUE; +} + +APT_DECLARE(apt_bool_t) apt_task_add(apt_task_t *task, apt_task_t *child_task) +{ + child_task->parent_task = task; + return (apt_list_push_back(task->child_tasks,child_task, child_task->pool) ? TRUE : FALSE); +} + +APT_DECLARE(apt_bool_t) apt_task_start(apt_task_t *task) +{ + apt_bool_t status = TRUE; + apr_thread_mutex_lock(task->data_guard); + if(task->state == TASK_STATE_IDLE) { + apr_status_t rv; + task->state = TASK_STATE_START_REQUESTED; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Start %s",task->name); + if(task->vtable.start) { + /* raise virtual start method */ + task->vtable.start(task); + } + else { + /* start new thread by default */ + rv = apr_thread_create(&task->thread_handle,NULL,apt_task_run,task,task->pool); + if(rv != APR_SUCCESS) { + task->state = TASK_STATE_IDLE; + status = FALSE; + } + } + } + else { + status = FALSE; + } + apr_thread_mutex_unlock(task->data_guard); + return status; +} + +APT_DECLARE(apt_bool_t) apt_task_terminate(apt_task_t *task, apt_bool_t wait_till_complete) +{ + apt_bool_t status = FALSE; + apr_thread_mutex_lock(task->data_guard); + if(task->state == TASK_STATE_START_REQUESTED || task->state == TASK_STATE_RUNNING) { + task->state = TASK_STATE_TERMINATE_REQUESTED; + } + apr_thread_mutex_unlock(task->data_guard); + + if(task->state == TASK_STATE_TERMINATE_REQUESTED) { + /* raise virtual terminate method */ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Terminate %s",task->name); + if(task->vtable.terminate) { + status = task->vtable.terminate(task); + } + + if(wait_till_complete == TRUE && status == TRUE) { + apt_task_wait_till_complete(task); + } + } + + return status; +} + +APT_DECLARE(apt_bool_t) apt_task_wait_till_complete(apt_task_t *task) +{ + if(task->thread_handle) { + apr_status_t s; + apr_thread_join(&s,task->thread_handle); + task->thread_handle = NULL; + } + return TRUE; +} + +APT_DECLARE(void) apt_task_delay(apr_size_t msec) +{ + apr_sleep(1000*msec); +} + +APT_DECLARE(apt_task_t*) apt_task_parent_get(apt_task_t *task) +{ + return task->parent_task; +} + +APT_DECLARE(apr_pool_t*) apt_task_pool_get(apt_task_t *task) +{ + return task->pool; +} + +APT_DECLARE(void*) apt_task_object_get(apt_task_t *task) +{ + return task->obj; +} + +APT_DECLARE(apt_task_vtable_t*) apt_task_vtable_get(apt_task_t *task) +{ + return &task->vtable; +} + +APT_DECLARE(void) apt_task_name_set(apt_task_t *task, const char *name) +{ + task->name = name; +} + +APT_DECLARE(const char*) apt_task_name_get(apt_task_t *task) +{ + return task->name; +} + +APT_DECLARE(apt_task_msg_t*) apt_task_msg_get(apt_task_t *task) +{ + if(task->msg_pool) { + return apt_task_msg_acquire(task->msg_pool); + } + return NULL; +} + +APT_DECLARE(apt_bool_t) apt_task_msg_signal(apt_task_t *task, apt_task_msg_t *msg) +{ + if(task->vtable.signal_msg) { + return task->vtable.signal_msg(task,msg); + } + return FALSE; +} + +APT_DECLARE(apt_bool_t) apt_task_msg_parent_signal(apt_task_t *task, apt_task_msg_t *msg) +{ + apt_task_t *parent_task = task->parent_task; + if(parent_task) { + if(parent_task->vtable.signal_msg) { + return parent_task->vtable.signal_msg(parent_task,msg); + } + } + return FALSE; +} + + +APT_DECLARE(apt_bool_t) apt_core_task_msg_process(apt_task_t *task, apt_task_msg_t *msg) +{ + apt_bool_t running = TRUE; + switch(msg->sub_type) { + case CORE_TASK_MSG_START_COMPLETE: + { + if(!task->pending_start) { + /* error case, no pending start */ + break; + } + task->pending_start--; + if(!task->pending_start) { + if(task->vtable.on_start_complete) { + task->vtable.on_start_complete(task); + } + if(task->parent_task) { + /* signal start-complete message */ + apt_task_msg_signal(task->parent_task,msg); + } + } + break; + } + case CORE_TASK_MSG_TERMINATE_REQUEST: + { + apt_task_child_terminate(task); + if(!task->pending_term) { + running = FALSE; + } + break; + } + case CORE_TASK_MSG_TERMINATE_COMPLETE: + { + if(!task->pending_term) { + /* error case, no pending terminate */ + break; + } + task->pending_term--; + if(!task->pending_term) { + if(task->vtable.on_terminate_complete) { + task->vtable.on_terminate_complete(task); + } + if(task->parent_task) { + /* signal terminate-complete message */ + apt_task_msg_signal(task->parent_task,msg); + } + running = FALSE; + } + break; + } + default: break; + } + return running; +} + +APT_DECLARE(apt_bool_t) apt_task_msg_process(apt_task_t *task, apt_task_msg_t *msg) +{ + apt_bool_t running = TRUE; + if(msg->type == TASK_MSG_CORE) { + running = apt_core_task_msg_process(task,msg); + } + else { + if(task->vtable.process_msg) { + task->vtable.process_msg(task,msg); + } + } + + apt_task_msg_release(msg); + return running; +} + +static apt_bool_t apt_task_terminate_request(apt_task_t *task) +{ + if(task->msg_pool) { + apt_task_msg_t *msg = apt_task_msg_acquire(task->msg_pool); + /* signal terminate-request message */ + msg->type = TASK_MSG_CORE; + msg->sub_type = CORE_TASK_MSG_TERMINATE_REQUEST; + return apt_task_msg_signal(task,msg); + } + return FALSE; +} + +APT_DECLARE(apt_bool_t) apt_task_child_start(apt_task_t *task) +{ + apt_task_t *child_task = NULL; + apt_list_elem_t *elem = apt_list_first_elem_get(task->child_tasks); + task->pending_start = 0; + /* walk through the list of the child tasks and start them */ + while(elem) { + child_task = apt_list_elem_object_get(elem); + if(child_task) { + if(apt_task_start(child_task) == TRUE) { + task->pending_start++; + } + } + elem = apt_list_next_elem_get(task->child_tasks,elem); + } + + if(!task->pending_start) { + /* no child task to start, just raise start-complete event */ + if(task->vtable.on_start_complete) { + task->vtable.on_start_complete(task); + } + if(task->parent_task) { + if(task->msg_pool) { + apt_task_msg_t *msg = apt_task_msg_acquire(task->msg_pool); + /* signal start-complete message */ + msg->type = TASK_MSG_CORE; + msg->sub_type = CORE_TASK_MSG_START_COMPLETE; + apt_task_msg_signal(task->parent_task,msg); + } + } + } + return TRUE; +} + +APT_DECLARE(apt_bool_t) apt_task_child_terminate(apt_task_t *task) +{ + apt_task_t *child_task = NULL; + apt_list_elem_t *elem = apt_list_first_elem_get(task->child_tasks); + task->pending_term = 0; + /* walk through the list of the child tasks and terminate them */ + while(elem) { + child_task = apt_list_elem_object_get(elem); + if(child_task) { + if(apt_task_terminate(child_task,FALSE) == TRUE) { + task->pending_term++; + } + } + elem = apt_list_next_elem_get(task->child_tasks,elem); + } + + if(!task->pending_term) { + /* no child task to terminate, just raise terminate-complete event */ + if(task->vtable.on_terminate_complete) { + task->vtable.on_terminate_complete(task); + } + if(task->parent_task) { + if(task->msg_pool) { + apt_task_msg_t *msg = apt_task_msg_acquire(task->msg_pool); + /* signal terminate-complete message */ + msg->type = TASK_MSG_CORE; + msg->sub_type = CORE_TASK_MSG_TERMINATE_COMPLETE; + apt_task_msg_signal(task->parent_task,msg); + } + } + } + return TRUE; +} + +static void* APR_THREAD_FUNC apt_task_run(apr_thread_t *thread_handle, void *data) +{ + apt_task_t *task = data; + + /* raise pre-run event */ + if(task->vtable.on_pre_run) { + task->vtable.on_pre_run(task); + } + apr_thread_mutex_lock(task->data_guard); + task->state = TASK_STATE_RUNNING; + apr_thread_mutex_unlock(task->data_guard); + + /* start child tasks (if any) */ + apt_task_child_start(task); + + /* run task */ + if(task->vtable.run) { + task->vtable.run(task); + } + + apr_thread_mutex_lock(task->data_guard); + task->state = TASK_STATE_IDLE; + apr_thread_mutex_unlock(task->data_guard); + /* raise post-run event */ + if(task->vtable.on_post_run) { + task->vtable.on_post_run(task); + } + return NULL; +} diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_task_msg.c b/libs/unimrcp/libs/apr-toolkit/src/apt_task_msg.c new file mode 100644 index 0000000000..fdff519fb1 --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_task_msg.c @@ -0,0 +1,103 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include "apt_task_msg.h" + +/** Abstract pool of task messages to allocate task messages from */ +struct apt_task_msg_pool_t { + void (*destroy)(apt_task_msg_pool_t *pool); + + apt_task_msg_t* (*acquire_msg)(apt_task_msg_pool_t *pool); + void (*release_msg)(apt_task_msg_t *task_msg); + + void *obj; + apr_pool_t *pool; +}; + + +/** Dynamic allocation of messages (no actual pool exist)*/ +typedef struct apt_msg_pool_dynamic_t apt_msg_pool_dynamic_t; + +struct apt_msg_pool_dynamic_t { + apr_size_t size; +}; + +static apt_task_msg_t* dynamic_pool_acquire_msg(apt_task_msg_pool_t *task_msg_pool) +{ + apt_msg_pool_dynamic_t *dynamic_pool = task_msg_pool->obj; + apt_task_msg_t *task_msg = malloc(dynamic_pool->size); + task_msg->msg_pool = task_msg_pool; + task_msg->type = TASK_MSG_USER; + task_msg->sub_type = 0; + return task_msg; +} + +static void dynamic_pool_release_msg(apt_task_msg_t *task_msg) +{ + if(task_msg) { + free(task_msg); + } +} + +static void dynamic_pool_destroy(apt_task_msg_pool_t *task_msg_pool) +{ + /* nothing to do */ +} + +APT_DECLARE(apt_task_msg_pool_t*) apt_task_msg_pool_create_dynamic(apr_size_t msg_size, apr_pool_t *pool) +{ + apt_task_msg_pool_t *task_msg_pool = apr_palloc(pool,sizeof(apt_task_msg_pool_t)); + apt_msg_pool_dynamic_t *dynamic_pool = apr_palloc(pool,sizeof(apt_msg_pool_dynamic_t)); + dynamic_pool->size = msg_size + sizeof(apt_task_msg_t) - 1; + + task_msg_pool->pool = pool; + task_msg_pool->obj = dynamic_pool; + task_msg_pool->acquire_msg = dynamic_pool_acquire_msg; + task_msg_pool->release_msg = dynamic_pool_release_msg; + task_msg_pool->destroy = dynamic_pool_destroy; + return task_msg_pool; +} + + +/** Static allocation of messages from message pool (not implemented yet) */ +APT_DECLARE(apt_task_msg_pool_t*) apt_task_msg_pool_create_static(apr_size_t msg_size, apr_size_t pool_size, apr_pool_t *pool) +{ + return NULL; +} + + + +APT_DECLARE(void) apt_task_msg_pool_destroy(apt_task_msg_pool_t *msg_pool) +{ + if(msg_pool->destroy) { + msg_pool->destroy(msg_pool); + } +} + +APT_DECLARE(apt_task_msg_t*) apt_task_msg_acquire(apt_task_msg_pool_t *task_msg_pool) +{ + if(!task_msg_pool->acquire_msg) + return NULL; + return task_msg_pool->acquire_msg(task_msg_pool); +} + +APT_DECLARE(void) apt_task_msg_release(apt_task_msg_t *task_msg) +{ + apt_task_msg_pool_t *task_msg_pool = task_msg->msg_pool; + if(task_msg_pool->release_msg) + task_msg_pool->release_msg(task_msg); +} diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_test_suite.c b/libs/unimrcp/libs/apr-toolkit/src/apt_test_suite.c new file mode 100644 index 0000000000..6a5d18ad7d --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_test_suite.c @@ -0,0 +1,119 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "apt_pool.h" +#include "apt_obj_list.h" +#include "apt_test_suite.h" +#include "apt_log.h" + +struct apt_test_framework_t{ + apr_pool_t *pool; + apt_obj_list_t *suites; +}; + +APT_DECLARE(apt_test_suite_t*) apt_test_suite_create(apr_pool_t *pool, const char *name, + void *obj, apt_test_f tester) +{ + apt_test_suite_t *suite = apr_palloc(pool,sizeof(apt_test_suite_t)); + suite->pool = pool; + apt_string_assign(&suite->name,name,pool); + suite->obj = obj; + suite->tester = tester; + return suite; +} + +APT_DECLARE(apt_test_framework_t*) apt_test_framework_create() +{ + apt_test_framework_t *framework; + apr_pool_t* pool = apt_pool_create(); + framework = apr_palloc(pool,sizeof(apt_test_framework_t)); + framework->pool = pool; + framework->suites = apt_list_create(pool); + + apt_log_instance_create(APT_LOG_OUTPUT_CONSOLE,APT_PRIO_INFO,pool); + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create Test Framework"); + return framework; +} + +APT_DECLARE(void) apt_test_framework_destroy(apt_test_framework_t *framework) +{ + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Destroy Test Framework"); + apt_log_instance_destroy(); + apr_pool_destroy(framework->pool); +} + +APT_DECLARE(apt_bool_t) apt_test_framework_suite_add(apt_test_framework_t *framework, apt_test_suite_t *suite) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Add Test Suite [%s]",suite->name); + return (apt_list_push_back(framework->suites,suite,suite->pool) ? TRUE : FALSE); +} + +APT_DECLARE(apr_pool_t*) apt_test_framework_pool_get(apt_test_framework_t *framework) +{ + return framework->pool; +} + +static apt_bool_t apt_test_framework_suite_run(apt_test_framework_t *framework, apt_test_suite_t *suite, + int argc, const char * const *argv) +{ + apt_bool_t status = FALSE; + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"----- Run Test Suite [%s] -----",suite->name); + if(suite->tester) { + status = suite->tester(suite,argc,argv); + } + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"---- Status [%s] ----\n",(status == TRUE) ? "OK" : "Failure"); + return status; +} + +APT_DECLARE(apt_bool_t) apt_test_framework_run(apt_test_framework_t *framework, int argc, const char * const *argv) +{ + apt_test_suite_t *suite = NULL; + apt_list_elem_t *elem = apt_list_first_elem_get(framework->suites); + if(argc == 1) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Run All Test Suites"); + /* walk through the list of test suites and run all of them */ + while(elem) { + suite = apt_list_elem_object_get(elem); + if(suite) { + /* run test suite with the default arguments */ + apt_test_framework_suite_run(framework,suite,0,NULL); + } + elem = apt_list_next_elem_get(framework->suites,elem); + } + } + else { + /* walk through the list of test suites find appropriate one and run it */ + apt_bool_t found = FALSE; + apt_str_t name; + apt_string_set(&name,argv[1]); + while(elem) { + suite = apt_list_elem_object_get(elem); + if(suite && apt_string_compare(&suite->name,&name) == TRUE) { + found = TRUE; + break; + } + elem = apt_list_next_elem_get(framework->suites,elem); + } + if(found == TRUE) { + /* run test suite with remaining arguments */ + apt_test_framework_suite_run(framework,suite,argc-2,&argv[2]); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No Such Test Suite [%s] to Run", argv[1]); + } + } + return TRUE; +} diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_text_stream.c b/libs/unimrcp/libs/apr-toolkit/src/apt_text_stream.c new file mode 100644 index 0000000000..2735337959 --- /dev/null +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_text_stream.c @@ -0,0 +1,372 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include "apt_text_stream.h" + +#define TOKEN_TRUE "true" +#define TOKEN_FALSE "false" +#define TOKEN_TRUE_LENGTH (sizeof(TOKEN_TRUE)-1) +#define TOKEN_FALSE_LENGTH (sizeof(TOKEN_FALSE)-1) + + +/** Navigate through the lines of the text stream (message) */ +APT_DECLARE(apt_bool_t) apt_text_line_read(apt_text_stream_t *stream, apt_str_t *line) +{ + char *pos = stream->pos; + const char *end = stream->text.buf + stream->text.length; + apt_bool_t eol = FALSE; + line->length = 0; + line->buf = pos; + /* while not end of stream */ + while(pos < end) { + if(*pos == APT_TOKEN_CR) { + /* end of line detected */ + line->length = pos - line->buf; + pos++; + if(pos < end && *pos == APT_TOKEN_LF) { + pos++; + } + eol = TRUE; + break; + } + else if(*pos == APT_TOKEN_LF) { + /* end of line detected */ + line->length = pos - line->buf; + pos++; + eol = TRUE; + break; + } + pos++; + } + + stream->pos = pos; + return eol; +} + +/** Navigate through the headers (name:value pairs) of the text stream (message) + Valid headers are: + name:value + name: value + name: value + name: value + name: (only name, no value) + (empty header) + Malformed headers are: + name:value (missing end of line ) + name (missing separator ':') +*/ +APT_DECLARE(apt_bool_t) apt_text_header_read(apt_text_stream_t *stream, apt_pair_t *pair) +{ + char *pos = stream->pos; + const char *end = stream->text.buf + stream->text.length; + apt_bool_t eol = FALSE; + apt_string_reset(&pair->name); + apt_string_reset(&pair->value); + /* while not end of stream */ + while(pos < end) { + if(*pos == APT_TOKEN_CR) { + /* end of line detected */ + if(pair->value.buf) { + /* set length of the value */ + pair->value.length = pos - pair->value.buf; + } + pos++; + if(pos < end && *pos == APT_TOKEN_LF) { + pos++; + } + eol = TRUE; + break; + } + else if(*pos == APT_TOKEN_LF) { + /* end of line detected */ + if(pair->value.buf) { + /* set length of the value */ + pair->value.length = pos - pair->value.buf; + } + pos++; + eol = TRUE; + break; + } + else if(!pair->name.length) { + /* skip initial spaces and read name */ + if(!pair->name.buf && *pos != APT_TOKEN_SP) { + pair->name.buf = pos; + } + if(*pos == ':') { + /* set length of the name */ + pair->name.length = pos - pair->name.buf; + } + } + else if(!pair->value.length) { + /* skip initial spaces and read value */ + if(!pair->value.buf && *pos != APT_TOKEN_SP) { + pair->value.buf = pos; + } + } + pos++; + } + + stream->pos = pos; + /* if length == 0 && buf -> header is malformed */ + return (eol && (pair->name.length || !pair->name.buf)); +} + + +/** Navigate through the fields of the line */ +APT_DECLARE(apt_bool_t) apt_text_field_read(apt_text_stream_t *stream, char separator, apt_bool_t skip_spaces, apt_str_t *field) +{ + char *pos = stream->pos; + const char *end = stream->text.buf + stream->text.length; + if(skip_spaces == TRUE) { + while(pos < end && *pos == APT_TOKEN_SP) pos++; + } + + field->buf = pos; + field->length = 0; + while(pos < end && *pos != separator) pos++; + + field->length = pos - field->buf; + if(pos < end) { + /* skip the separator */ + pos++; + } + + stream->pos = pos; + return field->length ? TRUE : FALSE; +} + +/** Scroll text stream */ +APT_DECLARE(apt_bool_t) apt_text_stream_scroll(apt_text_stream_t *stream) +{ + apr_size_t remaining_length = stream->text.buf + stream->text.length - stream->pos; + if(!remaining_length || remaining_length == stream->text.length) { + stream->pos = stream->text.buf + remaining_length; + return FALSE; + } + memmove(stream->text.buf,stream->pos,remaining_length); + stream->pos = stream->text.buf + remaining_length; + stream->text.length = remaining_length; + *stream->pos = '\0'; + return TRUE; +} + +/** Parse id@resource string */ +APT_DECLARE(apt_bool_t) apt_id_resource_parse(const apt_str_t *str, char separator, apt_str_t *id, apt_str_t *resource, apr_pool_t *pool) +{ + apt_str_t field = *str; + const char *pos = strchr(str->buf,separator); + if(!pos) { + return FALSE; + } + + field.length = pos - field.buf; + if(field.length >= str->length) { + return FALSE; + } + apt_string_copy(id,&field,pool); + field.buf += field.length + 1; + field.length = str->length - (field.length + 1); + apt_string_copy(resource,&field,pool); + return TRUE; +} + +/** Generate id@resource string */ +APT_DECLARE(apt_bool_t) apt_id_resource_generate(const apt_str_t *id, const apt_str_t *resource, char separator, apt_str_t *str, apr_pool_t *pool) +{ + apr_size_t length = id->length+resource->length+1; + char *buf = apr_palloc(pool,length+1); + memcpy(buf,id->buf,id->length); + buf[id->length] = separator; + memcpy(buf+id->length+1,resource->buf,resource->length); + buf[length] = '\0'; + str->buf = buf; + str->length = length; + return TRUE; +} + +/** Generate only the name ("name":) of the header */ +APT_DECLARE(apt_bool_t) apt_text_header_name_generate(const apt_str_t *name, apt_text_stream_t *stream) +{ + char *pos = stream->pos; + memcpy(pos,name->buf,name->length); + pos += name->length; + *pos++ = ':'; + *pos++ = ' '; + stream->pos = pos; + return TRUE; +} + +/** Parse name=value pair */ +static apt_bool_t apt_pair_parse(apt_pair_t *pair, const apt_str_t *field, apr_pool_t *pool) +{ + apt_text_stream_t stream; + stream.text = *field; + stream.pos = stream.text.buf; + + /* read name */ + if(apt_text_field_read(&stream,'=',TRUE,&pair->name) == FALSE) { + return FALSE; + } + + /* read value */ + apt_text_field_read(&stream,';',TRUE,&pair->value); + return TRUE; +} + +/** Parse array of name-value pairs */ +APT_DECLARE(apt_bool_t) apt_pair_array_parse(apt_pair_arr_t *arr, const apt_str_t *value, apr_pool_t *pool) +{ + apt_str_t field; + apt_pair_t *pair; + apt_text_stream_t stream; + if(!arr || !value) { + return FALSE; + } + + stream.text = *value; + stream.pos = stream.text.buf; + /* read name-value pairs */ + while(apt_text_field_read(&stream,';',TRUE,&field) == TRUE) { + pair = apr_array_push(arr); + apt_pair_parse(pair,&field,pool); + } + return TRUE; +} + +/** Generate array of name-value pairs */ +APT_DECLARE(apt_bool_t) apt_pair_array_generate(apt_pair_arr_t *arr, apt_text_stream_t *stream) +{ + int i; + apt_pair_t *pair; + char *pos = stream->pos; + if(!arr) { + return FALSE; + } + + for(i=0; inelts; i++) { + pair = (apt_pair_t*)arr->elts + i; + if(i != 0) { + *pos++ = ';'; + } + if(pair->name.length) { + memcpy(pos,pair->name.buf,pair->name.length); + pos += pair->name.length; + if(pair->value.length) { + *pos++ = '='; + memcpy(pos,pair->value.buf,pair->value.length); + pos += pair->value.length; + } + } + } + stream->pos = pos; + return TRUE; +} + +/** Parse boolean-value */ +APT_DECLARE(apt_bool_t) apt_boolean_value_parse(const apt_str_t *str, apt_bool_t *value) +{ + if(!str->buf) { + return FALSE; + } + if(strncasecmp(str->buf,TOKEN_TRUE,TOKEN_TRUE_LENGTH) == 0) { + *value = TRUE; + return TRUE; + } + if(strncasecmp(str->buf,TOKEN_FALSE,TOKEN_FALSE_LENGTH) == 0) { + *value = FALSE; + return TRUE; + } + return FALSE; +} + +/** Generate boolean-value */ +APT_DECLARE(apt_bool_t) apt_boolean_value_generate(apt_bool_t value, apt_text_stream_t *stream) +{ + if(value == TRUE) { + memcpy(stream->pos,TOKEN_TRUE,TOKEN_TRUE_LENGTH); + stream->pos += TOKEN_TRUE_LENGTH; + } + else { + memcpy(stream->pos,TOKEN_FALSE,TOKEN_FALSE_LENGTH); + stream->pos += TOKEN_FALSE_LENGTH; + } + return TRUE; +} + + +/** Generate value plus the length (number of digits) of the value itself. */ +APT_DECLARE(apt_bool_t) apt_var_length_value_generate(apr_size_t *value, apr_size_t max_count, apt_str_t *str) +{ + /* (N >= (10^M-M)) ? N+M+1 : N+M */ + apr_size_t temp; + apr_size_t count; /* M */ + apr_size_t bounds; /* 10^M */ + int length; + + /* calculate count */ + temp = *value; + count = 0; + do{count++; temp /= 10;} while(temp); + + /* calculate bounds */ + temp = count; + bounds = 1; + do{bounds *= 10; temp--;} while(temp); + + if(*value >= bounds - count) { + count++; + } + + *value += count; + if(count > max_count) { + return FALSE; + } + + str->length = 0; + length = sprintf(str->buf, "%"APR_SIZE_T_FMT, *value); + if(length <= 0) { + return FALSE; + } + str->length = length; + return TRUE; +} + + +/** Generate unique identifier (hex string) */ +APT_DECLARE(apt_bool_t) apt_unique_id_generate(apt_str_t *id, apr_size_t length, apr_pool_t *pool) +{ + char *hex_str; + apr_size_t i; + apr_size_t count; + apr_uuid_t uuid; + apr_uuid_get(&uuid); + + hex_str = apr_palloc(pool,length+1); + + count = length / 2; + if(count > sizeof(uuid)) { + count = sizeof(uuid); + } + for(i=0; ibuf = hex_str; + id->length = length; + return TRUE; +} diff --git a/libs/unimrcp/libs/mpf/Makefile.am b/libs/unimrcp/libs/mpf/Makefile.am new file mode 100644 index 0000000000..65f1493499 --- /dev/null +++ b/libs/unimrcp/libs/mpf/Makefile.am @@ -0,0 +1,63 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_srcdir)/libs/mpf/codecs \ + -I$(top_srcdir)/libs/mpf/include \ + -I$(top_srcdir)/libs/apr-toolkit/include \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) + +noinst_LTLIBRARIES = libmpf.la + +include_HEADERS = codecs/g711/g711.h \ + include/mpf.h \ + include/mpf_activity_detector.h \ + include/mpf_audio_file_descriptor.h \ + include/mpf_audio_file_stream.h \ + include/mpf_bridge.h \ + include/mpf_buffer.h \ + include/mpf_codec.h \ + include/mpf_codec_descriptor.h \ + include/mpf_codec_manager.h \ + include/mpf_context.h \ + include/mpf_engine.h \ + include/mpf_frame.h \ + include/mpf_message.h \ + include/mpf_object.h \ + include/mpf_stream.h \ + include/mpf_stream_mode.h \ + include/mpf_termination.h \ + include/mpf_rtp_termination_factory.h \ + include/mpf_file_termination_factory.h \ + include/mpf_timer.h \ + include/mpf_types.h \ + include/mpf_encoder.h \ + include/mpf_decoder.h \ + include/mpf_jitter_buffer.h \ + include/mpf_rtp_header.h \ + include/mpf_rtp_descriptor.h \ + include/mpf_rtp_stream.h \ + include/mpf_rtp_stat.h \ + include/mpf_rtp_defs.h \ + include/mpf_rtp_attribs.h \ + include/mpf_media_descriptor.h \ + include/mpf_user.h + +libmpf_la_SOURCES = codecs/g711/g711.c \ + src/mpf_activity_detector.c \ + src/mpf_audio_file_stream.c \ + src/mpf_bridge.c \ + src/mpf_buffer.c \ + src/mpf_codec_descriptor.c \ + src/mpf_codec_g711.c \ + src/mpf_codec_linear.c \ + src/mpf_codec_manager.c \ + src/mpf_context.c \ + src/mpf_engine.c \ + src/mpf_termination.c \ + src/mpf_rtp_termination_factory.c \ + src/mpf_file_termination_factory.c \ + src/mpf_timer.c \ + src/mpf_encoder.c \ + src/mpf_decoder.c \ + src/mpf_jitter_buffer.c \ + src/mpf_rtp_stream.c \ + src/mpf_rtp_attribs.c diff --git a/libs/unimrcp/libs/mpf/codecs/g711/g711.c b/libs/unimrcp/libs/mpf/codecs/g711/g711.c new file mode 100644 index 0000000000..be9ab7b134 --- /dev/null +++ b/libs/unimrcp/libs/mpf/codecs/g711/g711.c @@ -0,0 +1,91 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * g711.c - A-law and u-law transcoding routines + * + * Written by Steve Underwood + * + * Copyright (C) 2006 Steve Underwood + * + * Despite my general liking of the GPL, I place this code in the + * public domain for the benefit of all mankind - even the slimy + * ones who might try to proprietize my work and use it to my + * detriment. + * + */ + +/*! \file */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#ifndef _MSC_VER +#include +#ifdef HAVE_TGMATH_H +#include +#endif +#endif + +#include "g711.h" + +/* Copied from the CCITT G.711 specification */ +static const uint8_t ulaw_to_alaw_table[256] = +{ + 42, 43, 40, 41, 46, 47, 44, 45, 34, 35, 32, 33, 38, 39, 36, 37, + 58, 59, 56, 57, 62, 63, 60, 61, 50, 51, 48, 49, 54, 55, 52, 53, + 10, 11, 8, 9, 14, 15, 12, 13, 2, 3, 0, 1, 6, 7, 4, 26, + 27, 24, 25, 30, 31, 28, 29, 18, 19, 16, 17, 22, 23, 20, 21, 106, + 104, 105, 110, 111, 108, 109, 98, 99, 96, 97, 102, 103, 100, 101, 122, 120, + 126, 127, 124, 125, 114, 115, 112, 113, 118, 119, 116, 117, 75, 73, 79, 77, + 66, 67, 64, 65, 70, 71, 68, 69, 90, 91, 88, 89, 94, 95, 92, 93, + 82, 82, 83, 83, 80, 80, 81, 81, 86, 86, 87, 87, 84, 84, 85, 85, + 170, 171, 168, 169, 174, 175, 172, 173, 162, 163, 160, 161, 166, 167, 164, 165, + 186, 187, 184, 185, 190, 191, 188, 189, 178, 179, 176, 177, 182, 183, 180, 181, + 138, 139, 136, 137, 142, 143, 140, 141, 130, 131, 128, 129, 134, 135, 132, 154, + 155, 152, 153, 158, 159, 156, 157, 146, 147, 144, 145, 150, 151, 148, 149, 234, + 232, 233, 238, 239, 236, 237, 226, 227, 224, 225, 230, 231, 228, 229, 250, 248, + 254, 255, 252, 253, 242, 243, 240, 241, 246, 247, 244, 245, 203, 201, 207, 205, + 194, 195, 192, 193, 198, 199, 196, 197, 218, 219, 216, 217, 222, 223, 220, 221, + 210, 210, 211, 211, 208, 208, 209, 209, 214, 214, 215, 215, 212, 212, 213, 213 +}; + +/* These transcoding tables are copied from the CCITT G.711 specification. To achieve + optimal results, do not change them. */ + +static const uint8_t alaw_to_ulaw_table[256] = +{ + 42, 43, 40, 41, 46, 47, 44, 45, 34, 35, 32, 33, 38, 39, 36, 37, + 57, 58, 55, 56, 61, 62, 59, 60, 49, 50, 47, 48, 53, 54, 51, 52, + 10, 11, 8, 9, 14, 15, 12, 13, 2, 3, 0, 1, 6, 7, 4, 5, + 26, 27, 24, 25, 30, 31, 28, 29, 18, 19, 16, 17, 22, 23, 20, 21, + 98, 99, 96, 97, 102, 103, 100, 101, 93, 93, 92, 92, 95, 95, 94, 94, + 116, 118, 112, 114, 124, 126, 120, 122, 106, 107, 104, 105, 110, 111, 108, 109, + 72, 73, 70, 71, 76, 77, 74, 75, 64, 65, 63, 63, 68, 69, 66, 67, + 86, 87, 84, 85, 90, 91, 88, 89, 79, 79, 78, 78, 82, 83, 80, 81, + 170, 171, 168, 169, 174, 175, 172, 173, 162, 163, 160, 161, 166, 167, 164, 165, + 185, 186, 183, 184, 189, 190, 187, 188, 177, 178, 175, 176, 181, 182, 179, 180, + 138, 139, 136, 137, 142, 143, 140, 141, 130, 131, 128, 129, 134, 135, 132, 133, + 154, 155, 152, 153, 158, 159, 156, 157, 146, 147, 144, 145, 150, 151, 148, 149, + 226, 227, 224, 225, 230, 231, 228, 229, 221, 221, 220, 220, 223, 223, 222, 222, + 244, 246, 240, 242, 252, 254, 248, 250, 234, 235, 232, 233, 238, 239, 236, 237, + 200, 201, 198, 199, 204, 205, 202, 203, 192, 193, 191, 191, 196, 197, 194, 195, + 214, 215, 212, 213, 218, 219, 216, 217, 207, 207, 206, 206, 210, 211, 208, 209 +}; + +uint8_t alaw_to_ulaw(uint8_t alaw) +{ + return alaw_to_ulaw_table[alaw]; +} +/*- End of function --------------------------------------------------------*/ + +uint8_t ulaw_to_alaw(uint8_t ulaw) +{ + return ulaw_to_alaw_table[ulaw]; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/ diff --git a/libs/unimrcp/libs/mpf/codecs/g711/g711.h b/libs/unimrcp/libs/mpf/codecs/g711/g711.h new file mode 100644 index 0000000000..a8a0e92150 --- /dev/null +++ b/libs/unimrcp/libs/mpf/codecs/g711/g711.h @@ -0,0 +1,381 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * g711.h - In line A-law and u-law conversion routines + * + * Written by Steve Underwood + * + * Copyright (C) 2001 Steve Underwood + * + * Despite my general liking of the GPL, I place this code in the + * public domain for the benefit of all mankind - even the slimy + * ones who might try to proprietize my work and use it to my + * detriment. + * + */ + +/*! \file */ + +/*! \page g711_page A-law and mu-law handling +Lookup tables for A-law and u-law look attractive, until you consider the impact +on the CPU cache. If it causes a substantial area of your processor cache to get +hit too often, cache sloshing will severely slow things down. The main reason +these routines are slow in C, is the lack of direct access to the CPU's "find +the first 1" instruction. A little in-line assembler fixes that, and the +conversion routines can be faster than lookup tables, in most real world usage. +A "find the first 1" instruction is available on most modern CPUs, and is a +much underused feature. + +If an assembly language method of bit searching is not available, these routines +revert to a method that can be a little slow, so the cache thrashing might not +seem so bad :( + +Feel free to submit patches to add fast "find the first 1" support for your own +favourite processor. + +Look up tables are used for transcoding between A-law and u-law, since it is +difficult to achieve the precise transcoding procedure laid down in the G.711 +specification by other means. +*/ + +#if !defined(_G711_H_) +#define _G711_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER +#ifndef __inline__ +#define __inline__ __inline +#endif +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef __int32 int32_t; +typedef unsigned __int16 uint16_t; +#endif + +#if defined(__i386__) +/*! \brief Find the bit position of the highest set bit in a word + \param bits The word to be searched + \return The bit number of the highest set bit, or -1 if the word is zero. */ +static __inline__ int top_bit(unsigned int bits) +{ + int res; + + __asm__ __volatile__(" movl $-1,%%edx;\n" + " bsrl %%eax,%%edx;\n" + : "=d" (res) + : "a" (bits)); + return res; +} +/*- End of function --------------------------------------------------------*/ + +/*! \brief Find the bit position of the lowest set bit in a word + \param bits The word to be searched + \return The bit number of the lowest set bit, or -1 if the word is zero. */ +static __inline__ int bottom_bit(unsigned int bits) +{ + int res; + + __asm__ __volatile__(" movl $-1,%%edx;\n" + " bsfl %%eax,%%edx;\n" + : "=d" (res) + : "a" (bits)); + return res; +} +/*- End of function --------------------------------------------------------*/ +#elif defined(__x86_64__) +static __inline__ int top_bit(unsigned int bits) +{ + int res; + + __asm__ __volatile__(" movq $-1,%%rdx;\n" + " bsrq %%rax,%%rdx;\n" + : "=d" (res) + : "a" (bits)); + return res; +} +/*- End of function --------------------------------------------------------*/ + +static __inline__ int bottom_bit(unsigned int bits) +{ + int res; + + __asm__ __volatile__(" movq $-1,%%rdx;\n" + " bsfq %%rax,%%rdx;\n" + : "=d" (res) + : "a" (bits)); + return res; +} +/*- End of function --------------------------------------------------------*/ +#else +static __inline__ int top_bit(unsigned int bits) +{ + int i; + + if (bits == 0) + return -1; + i = 0; + if (bits & 0xFFFF0000) + { + bits &= 0xFFFF0000; + i += 16; + } + if (bits & 0xFF00FF00) + { + bits &= 0xFF00FF00; + i += 8; + } + if (bits & 0xF0F0F0F0) + { + bits &= 0xF0F0F0F0; + i += 4; + } + if (bits & 0xCCCCCCCC) + { + bits &= 0xCCCCCCCC; + i += 2; + } + if (bits & 0xAAAAAAAA) + { + bits &= 0xAAAAAAAA; + i += 1; + } + return i; +} +/*- End of function --------------------------------------------------------*/ + +static __inline__ int bottom_bit(unsigned int bits) +{ + int i; + + if (bits == 0) + return -1; + i = 32; + if (bits & 0x0000FFFF) + { + bits &= 0x0000FFFF; + i -= 16; + } + if (bits & 0x00FF00FF) + { + bits &= 0x00FF00FF; + i -= 8; + } + if (bits & 0x0F0F0F0F) + { + bits &= 0x0F0F0F0F; + i -= 4; + } + if (bits & 0x33333333) + { + bits &= 0x33333333; + i -= 2; + } + if (bits & 0x55555555) + { + bits &= 0x55555555; + i -= 1; + } + return i; +} +/*- End of function --------------------------------------------------------*/ +#endif + +/* N.B. It is tempting to use look-up tables for A-law and u-law conversion. + * However, you should consider the cache footprint. + * + * A 64K byte table for linear to x-law and a 512 byte table for x-law to + * linear sound like peanuts these days, and shouldn't an array lookup be + * real fast? No! When the cache sloshes as badly as this one will, a tight + * calculation may be better. The messiest part is normally finding the + * segment, but a little inline assembly can fix that on an i386, x86_64 and + * many other modern processors. + */ + +/* + * Mu-law is basically as follows: + * + * Biased Linear Input Code Compressed Code + * ------------------------ --------------- + * 00000001wxyza 000wxyz + * 0000001wxyzab 001wxyz + * 000001wxyzabc 010wxyz + * 00001wxyzabcd 011wxyz + * 0001wxyzabcde 100wxyz + * 001wxyzabcdef 101wxyz + * 01wxyzabcdefg 110wxyz + * 1wxyzabcdefgh 111wxyz + * + * Each biased linear code has a leading 1 which identifies the segment + * number. The value of the segment number is equal to 7 minus the number + * of leading 0's. The quantization interval is directly available as the + * four bits wxyz. * The trailing bits (a - h) are ignored. + * + * Ordinarily the complement of the resulting code word is used for + * transmission, and so the code word is complemented before it is returned. + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ + +//#define ULAW_ZEROTRAP /* turn on the trap as per the MIL-STD */ +#define ULAW_BIAS 0x84 /* Bias for linear code. */ + +/*! \brief Encode a linear sample to u-law + \param linear The sample to encode. + \return The u-law value. +*/ +static __inline__ uint8_t linear_to_ulaw(int linear) +{ + uint8_t u_val; + int mask; + int seg; + + /* Get the sign and the magnitude of the value. */ + if (linear < 0) + { + linear = ULAW_BIAS - linear; + mask = 0x7F; + } + else + { + linear = ULAW_BIAS + linear; + mask = 0xFF; + } + + seg = top_bit(linear | 0xFF) - 7; + + /* + * Combine the sign, segment, quantization bits, + * and complement the code word. + */ + if (seg >= 8) + u_val = (uint8_t) (0x7F ^ mask); + else + u_val = (uint8_t) (((seg << 4) | ((linear >> (seg + 3)) & 0xF)) ^ mask); +#ifdef ULAW_ZEROTRAP + /* Optional ITU trap */ + if (u_val == 0) + u_val = 0x02; +#endif + return u_val; +} +/*- End of function --------------------------------------------------------*/ + +/*! \brief Decode an u-law sample to a linear value. + \param ulaw The u-law sample to decode. + \return The linear value. +*/ +static __inline__ int16_t ulaw_to_linear(uint8_t ulaw) +{ + int t; + + /* Complement to obtain normal u-law value. */ + ulaw = ~ulaw; + /* + * Extract and bias the quantization bits. Then + * shift up by the segment number and subtract out the bias. + */ + t = (((ulaw & 0x0F) << 3) + ULAW_BIAS) << (((int) ulaw & 0x70) >> 4); + return (int16_t) ((ulaw & 0x80) ? (ULAW_BIAS - t) : (t - ULAW_BIAS)); +} +/*- End of function --------------------------------------------------------*/ + +/* + * A-law is basically as follows: + * + * Linear Input Code Compressed Code + * ----------------- --------------- + * 0000000wxyza 000wxyz + * 0000001wxyza 001wxyz + * 000001wxyzab 010wxyz + * 00001wxyzabc 011wxyz + * 0001wxyzabcd 100wxyz + * 001wxyzabcde 101wxyz + * 01wxyzabcdef 110wxyz + * 1wxyzabcdefg 111wxyz + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ + +#define ALAW_AMI_MASK 0x55 + +/*! \brief Encode a linear sample to A-law + \param linear The sample to encode. + \return The A-law value. +*/ +static __inline__ uint8_t linear_to_alaw(int linear) +{ + int mask; + int seg; + + if (linear >= 0) + { + /* Sign (bit 7) bit = 1 */ + mask = ALAW_AMI_MASK | 0x80; + } + else + { + /* Sign (bit 7) bit = 0 */ + mask = ALAW_AMI_MASK; + linear = -linear - 8; + } + + /* Convert the scaled magnitude to segment number. */ + seg = top_bit(linear | 0xFF) - 7; + if (seg >= 8) + { + if (linear >= 0) + { + /* Out of range. Return maximum value. */ + return (uint8_t) (0x7F ^ mask); + } + /* We must be just a tiny step below zero */ + return (uint8_t) (0x00 ^ mask); + } + /* Combine the sign, segment, and quantization bits. */ + return (uint8_t) (((seg << 4) | ((linear >> ((seg) ? (seg + 3) : 4)) & 0x0F)) ^ mask); +} +/*- End of function --------------------------------------------------------*/ + +/*! \brief Decode an A-law sample to a linear value. + \param alaw The A-law sample to decode. + \return The linear value. +*/ +static __inline__ int16_t alaw_to_linear(uint8_t alaw) +{ + int i; + int seg; + + alaw ^= ALAW_AMI_MASK; + i = ((alaw & 0x0F) << 4); + seg = (((int) alaw & 0x70) >> 4); + if (seg) + i = (i + 0x108) << (seg - 1); + else + i += 8; + return (int16_t) ((alaw & 0x80) ? i : -i); +} +/*- End of function --------------------------------------------------------*/ + +/*! \brief Transcode from A-law to u-law, using the procedure defined in G.711. + \param alaw The A-law sample to transcode. + \return The best matching u-law value. +*/ +uint8_t alaw_to_ulaw(uint8_t alaw); + +/*! \brief Transcode from u-law to A-law, using the procedure defined in G.711. + \param alaw The u-law sample to transcode. + \return The best matching A-law value. +*/ +uint8_t ulaw_to_alaw(uint8_t ulaw); + +#ifdef __cplusplus +} +#endif + +#endif +/*- End of file ------------------------------------------------------------*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf.h b/libs/unimrcp/libs/mpf/include/mpf.h new file mode 100644 index 0000000000..7edc0da71b --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf.h @@ -0,0 +1,42 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_H__ +#define __MPF_H__ + +/** + * @file mpf.h + * @brief Media Processing Framework Definitions + */ + +#include + +/** lib export/import defines (win32) */ +#ifdef WIN32 +#ifdef MPF_STATIC_LIB +#define MPF_DECLARE(type) type __stdcall +#else +#ifdef MPF_LIB_EXPORT +#define MPF_DECLARE(type) __declspec(dllexport) type __stdcall +#else +#define MPF_DECLARE(type) __declspec(dllimport) type __stdcall +#endif +#endif +#else +#define MPF_DECLARE(type) type +#endif + +#endif /*__MPF_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_activity_detector.h b/libs/unimrcp/libs/mpf/include/mpf_activity_detector.h new file mode 100644 index 0000000000..8fcb1b42aa --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_activity_detector.h @@ -0,0 +1,60 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_ACTIVITY_DETECTOR_H__ +#define __MPF_ACTIVITY_DETECTOR_H__ + +/** + * @file mpf_activity_detector.h + * @brief MPF Voice Activity Detector + */ + +#include "mpf_frame.h" +#include "mpf_codec.h" + +APT_BEGIN_EXTERN_C + +/** Opaque (voice) activity detector */ +typedef struct mpf_activity_detector_t mpf_activity_detector_t; + +/** Events of activity detector */ +typedef enum { + MPF_DETECTOR_EVENT_NONE, /**< no event occurred */ + MPF_DETECTOR_EVENT_ACTIVITY, /**< voice activity (transition to activity from inactivity state) */ + MPF_DETECTOR_EVENT_INACTIVITY, /**< voice inactivity (transition to inactivity from activity state) */ + MPF_DETECTOR_EVENT_NOINPUT /**< noinput event occurred */ +} mpf_detector_event_e; + + +/** Create activity detector */ +MPF_DECLARE(mpf_activity_detector_t*) mpf_activity_detector_create(apr_pool_t *pool); + +/** Set threshold of voice activity (silence) level */ +MPF_DECLARE(void) mpf_activity_detector_level_set(mpf_activity_detector_t *detector, apr_size_t level_threshold); + +/** Set noinput timeout */ +MPF_DECLARE(void) mpf_activity_detector_noinput_timeout_set(mpf_activity_detector_t *detector, apr_size_t noinput_timeout); + +/** Set transition complete timeout */ +MPF_DECLARE(void) mpf_activity_detector_complete_timeout_set(mpf_activity_detector_t *detector, apr_size_t complete_timeout); + +/** Process current frame, return detected event if any */ +MPF_DECLARE(mpf_detector_event_e) mpf_activity_detector_process(mpf_activity_detector_t *detector, const mpf_frame_t *frame); + + +APT_END_EXTERN_C + +#endif /*__MPF_ACTIVITY_DETECTOR_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_audio_file_descriptor.h b/libs/unimrcp/libs/mpf/include/mpf_audio_file_descriptor.h new file mode 100644 index 0000000000..4dfb6d3ebb --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_audio_file_descriptor.h @@ -0,0 +1,55 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_AUDIO_FILE_DESCRIPTOR_H__ +#define __MPF_AUDIO_FILE_DESCRIPTOR_H__ + +/** + * @file mpf_audio_file_descriptor.h + * @brief MPF Audio File Descriptor + */ + +#include +#include "mpf_stream_mode.h" +#include "mpf_codec_descriptor.h" + +APT_BEGIN_EXTERN_C + +/** FILE_READER is defined as STREAM_MODE_RECEIVE */ +#define FILE_READER STREAM_MODE_RECEIVE +/** FILE_WRITER is defined as STREAM_MODE_SEND */ +#define FILE_WRITER STREAM_MODE_SEND + +/** Audio file descriptor declaration */ +typedef struct mpf_audio_file_descriptor_t mpf_audio_file_descriptor_t; + +/** Audio file descriptor */ +struct mpf_audio_file_descriptor_t { + /** Indicate what descriptor for (reader and/or write) */ + mpf_stream_mode_e mask; + /** Codec descriptor to use for audio file read/write */ + mpf_codec_descriptor_t codec_descriptor; + /** File handle to read audio stream */ + FILE *read_handle; + /** File handle to write audio stream */ + FILE *write_handle; + /** Max size of file */ + apr_size_t max_write_size; +}; + +APT_END_EXTERN_C + +#endif /*__MPF_AUDIO_FILE_DESCRIPTOR_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_audio_file_stream.h b/libs/unimrcp/libs/mpf/include/mpf_audio_file_stream.h new file mode 100644 index 0000000000..f117fbfbb6 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_audio_file_stream.h @@ -0,0 +1,46 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_AUDIO_FILE_STREAM_H__ +#define __MPF_AUDIO_FILE_STREAM_H__ + +/** + * @file mpf_audio_file_stream.h + * @brief MPF Audio FIle Stream + */ + +#include "mpf_stream.h" +#include "mpf_audio_file_descriptor.h" + +APT_BEGIN_EXTERN_C + +/** + * Create file stream. + * @param termination the back pointer to hold + * @param pool the pool to allocate memory from + */ +MPF_DECLARE(mpf_audio_stream_t*) mpf_file_stream_create(mpf_termination_t *termination, apr_pool_t *pool); + +/** + * Modify file stream. + * @param stream file stream to modify + * @param descriptor the descriptor to modify stream according + */ +MPF_DECLARE(apt_bool_t) mpf_file_stream_modify(mpf_audio_stream_t *stream, mpf_audio_file_descriptor_t *descriptor); + +APT_END_EXTERN_C + +#endif /*__MPF_AUDIO_FILE_STREAM_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_bridge.h b/libs/unimrcp/libs/mpf/include/mpf_bridge.h new file mode 100644 index 0000000000..ceb00713b9 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_bridge.h @@ -0,0 +1,48 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_BRIDGE_H__ +#define __MPF_BRIDGE_H__ + +/** + * @file mpf_bridge.h + * @brief MPF Stream Bridge + */ + +#include "mpf_object.h" + +APT_BEGIN_EXTERN_C + +/** + * Create bridge of audio streams. + * @param source the source audio stream + * @param sink the sink audio stream + * @param pool the pool to allocate memory from + */ +MPF_DECLARE(mpf_object_t*) mpf_bridge_create(mpf_audio_stream_t *source, mpf_audio_stream_t *sink, apr_pool_t *pool); + +/** + * Create bridge of audio streams with the same codec descriptor. + * @param source the source audio stream + * @param sink the sink audio stream + * @param pool the pool to allocate memory from + */ +MPF_DECLARE(mpf_object_t*) mpf_null_bridge_create(mpf_audio_stream_t *source, mpf_audio_stream_t *sink, apr_pool_t *pool); + + +APT_END_EXTERN_C + +#endif /*__MPF_BRIDGE_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_buffer.h b/libs/unimrcp/libs/mpf/include/mpf_buffer.h new file mode 100644 index 0000000000..6dd22f3f42 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_buffer.h @@ -0,0 +1,56 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_BUFFER_H__ +#define __MPF_BUFFER_H__ + +/** + * @file mpf_buffer.h + * @brief Buffer of Media Chunks + */ + +#include "mpf_frame.h" + +APT_BEGIN_EXTERN_C + +/** Opaque media buffer declaration */ +typedef struct mpf_buffer_t mpf_buffer_t; + + +/** Create buffer */ +mpf_buffer_t* mpf_buffer_create(apr_pool_t *pool); + +/** Destroy buffer */ +void mpf_buffer_destroy(mpf_buffer_t *buffer); + +/** Restart buffer */ +apt_bool_t mpf_buffer_restart(mpf_buffer_t *buffer); + +/** Write audio chunk to buffer */ +apt_bool_t mpf_buffer_audio_write(mpf_buffer_t *buffer, void *data, apr_size_t size); + +/** Write event to buffer */ +apt_bool_t mpf_buffer_event_write(mpf_buffer_t *buffer, mpf_frame_type_e event_type); + +/** Read media frame from buffer */ +apt_bool_t mpf_buffer_frame_read(mpf_buffer_t *buffer, mpf_frame_t *media_frame); + +/** Get size of buffer **/ +apr_size_t mpf_buffer_get_size(mpf_buffer_t *buffer); + +APT_END_EXTERN_C + +#endif /*__MPF_BUFFER_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_codec.h b/libs/unimrcp/libs/mpf/include/mpf_codec.h new file mode 100644 index 0000000000..8c9c4b91be --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_codec.h @@ -0,0 +1,169 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_CODEC_H__ +#define __MPF_CODEC_H__ + +/** + * @file mpf_codec.h + * @brief MPF Codec + */ + +#include "mpf_codec_descriptor.h" + +APT_BEGIN_EXTERN_C + +/** Codec virtual table declaration */ +typedef struct mpf_codec_vtable_t mpf_codec_vtable_t; +/** Codec declaration*/ +typedef struct mpf_codec_t mpf_codec_t; + +/** Codec */ +struct mpf_codec_t { + /** Codec manipulators (encode, decode, dissect) */ + const mpf_codec_vtable_t *vtable; + /** Codec attributes (capabilities) */ + const mpf_codec_attribs_t *attribs; + /** Optional static codec descriptor (pt < 96) */ + const mpf_codec_descriptor_t *static_descriptor; + + /** Negotiated codec descriptor */ + mpf_codec_descriptor_t *descriptor; +}; + +/** Table of codec virtual methods */ +struct mpf_codec_vtable_t { + /** Virtual open method */ + apt_bool_t (*open)(mpf_codec_t *codec); + /** Virtual close method */ + apt_bool_t (*close)(mpf_codec_t *codec); + + /** Virtual encode method */ + apt_bool_t (*encode)(mpf_codec_t *codec, const mpf_codec_frame_t *frame_in, mpf_codec_frame_t *frame_out); + /** Virtual decode method */ + apt_bool_t (*decode)(mpf_codec_t *codec, const mpf_codec_frame_t *frame_in, mpf_codec_frame_t *frame_out); + + /** Virtual dissect method */ + apt_bool_t (*dissect)(mpf_codec_t *codec, void **buffer, apr_size_t *size, mpf_codec_frame_t *frame); +}; + +/** + * Create codec. + * @param vtable the table of virtual mthods + * @param attribs the codec attributes + * @param descriptor the codec descriptor + * @param pool the pool to allocate memory from + */ +static APR_INLINE mpf_codec_t* mpf_codec_create( + const mpf_codec_vtable_t *vtable, + const mpf_codec_attribs_t *attribs, + const mpf_codec_descriptor_t *descriptor, + apr_pool_t *pool) +{ + mpf_codec_t *codec = (mpf_codec_t*)apr_palloc(pool,sizeof(mpf_codec_t)); + codec->vtable = vtable; + codec->attribs = attribs; + codec->static_descriptor = descriptor; + codec->descriptor = NULL; + return codec; +} + +/** + * Close codec. + * @param src_codec the source (original) codec to clone + * @param pool the pool to allocate memory from + */ +static APR_INLINE mpf_codec_t* mpf_codec_clone(mpf_codec_t *src_codec, apr_pool_t *pool) +{ + mpf_codec_t *codec = (mpf_codec_t*)apr_palloc(pool,sizeof(mpf_codec_t)); + codec->vtable = src_codec->vtable; + codec->attribs = src_codec->attribs; + codec->static_descriptor = src_codec->static_descriptor; + codec->descriptor = src_codec->descriptor; + return codec; +} + +/** Open codec */ +static APR_INLINE apt_bool_t mpf_codec_open(mpf_codec_t *codec) +{ + apt_bool_t rv = TRUE; + if(codec->descriptor) { + if(codec->vtable->open) { + rv = codec->vtable->open(codec); + } + } + else { + rv = FALSE; + } + return rv; +} + +/** Close codec */ +static APR_INLINE apt_bool_t mpf_codec_close(mpf_codec_t *codec) +{ + apt_bool_t rv = TRUE; + if(codec->vtable->close) { + rv = codec->vtable->close(codec); + } + return rv; +} + +/** Encode codec frame */ +static APR_INLINE apt_bool_t mpf_codec_encode(mpf_codec_t *codec, const mpf_codec_frame_t *frame_in, mpf_codec_frame_t *frame_out) +{ + apt_bool_t rv = TRUE; + if(codec->vtable->encode) { + rv = codec->vtable->encode(codec,frame_in,frame_out); + } + return rv; +} + +/** Decode codec frame */ +static APR_INLINE apt_bool_t mpf_codec_decode(mpf_codec_t *codec, const mpf_codec_frame_t *frame_in, mpf_codec_frame_t *frame_out) +{ + apt_bool_t rv = TRUE; + if(codec->vtable->decode) { + rv = codec->vtable->decode(codec,frame_in,frame_out); + } + return rv; +} + +/** Dissect codec frame (navigate through codec frames in a buffer, which may contain multiple frames) */ +static APR_INLINE apt_bool_t mpf_codec_dissect(mpf_codec_t *codec, void **buffer, apr_size_t *size, mpf_codec_frame_t *frame) +{ + apt_bool_t rv = TRUE; + if(codec->vtable->dissect) { + /* custom dissector for codecs like G.729, G.723 */ + rv = codec->vtable->dissect(codec,buffer,size,frame); + } + else { + /* default dissector */ + if(*size >= frame->size && frame->size) { + memcpy(frame->buffer,*buffer,frame->size); + + *buffer = (apr_byte_t*)*buffer + frame->size; + *size = *size - frame->size; + } + else { + rv = FALSE; + } + } + return rv; +} + +APT_END_EXTERN_C + +#endif /*__MPF_CODEC_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_codec_descriptor.h b/libs/unimrcp/libs/mpf/include/mpf_codec_descriptor.h new file mode 100644 index 0000000000..29adb7b281 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_codec_descriptor.h @@ -0,0 +1,180 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_CODEC_DESCRIPTOR_H__ +#define __MPF_CODEC_DESCRIPTOR_H__ + +/** + * @file mpf_codec_descriptor.h + * @brief MPF Codec Descriptor + */ + +#include +#include "apt_string.h" +#include "mpf.h" + +APT_BEGIN_EXTERN_C + +/** Codec frame time base in msec */ +#define CODEC_FRAME_TIME_BASE 10 +/** Bytes per sample for linear pcm */ +#define BYTES_PER_SAMPLE 2 +/** Bits per sample for linear pcm */ +#define BITS_PER_SAMPLE 16 + +/** Supported sampling rates */ +typedef enum { + MPF_SAMPLE_RATE_NONE = 0x00, + MPF_SAMPLE_RATE_8000 = 0x01, + MPF_SAMPLE_RATE_16000 = 0x02 +} mpf_sample_rates_e; + +/** Codec descriptor declaration */ +typedef struct mpf_codec_descriptor_t mpf_codec_descriptor_t; +/** Codec list declaration */ +typedef struct mpf_codec_list_t mpf_codec_list_t; +/** Codec frame declaration */ +typedef struct mpf_codec_frame_t mpf_codec_frame_t; +/** Codec attributes declaration */ +typedef struct mpf_codec_attribs_t mpf_codec_attribs_t; + + +/** Codec descriptor */ +struct mpf_codec_descriptor_t { + /** Payload type used in RTP packet */ + apr_byte_t payload_type; + /** Codec name */ + apt_str_t name; + /** Sampling rate */ + apr_uint16_t sampling_rate; + /** Channel count */ + apr_byte_t channel_count; + /** Codec dependent additional format */ + const char *format; + /** Enabled/disabled state */ + apt_bool_t enabled; +}; + +/** List of codec descriptors */ +struct mpf_codec_list_t { + /** Dynamic array of mpf_codec_descriptor_t */ + apr_array_header_t *descriptor_arr; + /** Preffered codec descriptor */ + mpf_codec_descriptor_t *preffered; +}; + +/** Codec frame */ +struct mpf_codec_frame_t { + /** Raw buffer, which may contain encoded or decoded data */ + void *buffer; + /** Buffer size */ + apr_size_t size; +}; + +/** Codec attributes */ +struct mpf_codec_attribs_t { + /** Codec name */ + apt_str_t name; + /** Bits per sample */ + apr_byte_t bits_per_samples; + /** Supported sampling rates (mpf_sample_rates_e) */ + int sample_rates; +}; + + +/** Initialize codec descriptor */ +static APR_INLINE void mpf_codec_descriptor_init(mpf_codec_descriptor_t *descriptor) +{ + descriptor->payload_type = 0; + apt_string_reset(&descriptor->name); + descriptor->sampling_rate = 0; + descriptor->channel_count = 0; + descriptor->format = NULL; + descriptor->enabled = TRUE; +} + +/** Calculate encoded frame size in bytes */ +static APR_INLINE apr_size_t mpf_codec_frame_size_calculate(const mpf_codec_descriptor_t *descriptor, const mpf_codec_attribs_t *attribs) +{ + return descriptor->channel_count * attribs->bits_per_samples * CODEC_FRAME_TIME_BASE * + descriptor->sampling_rate / 1000 / 8; /* 1000 - msec per sec, 8 - bits per byte */ +} + +/** Calculate samples of the frame (ts) */ +static APR_INLINE apr_size_t mpf_codec_frame_samples_calculate(const mpf_codec_descriptor_t *descriptor) +{ + return descriptor->channel_count * CODEC_FRAME_TIME_BASE * descriptor->sampling_rate / 1000; +} + +/** Calculate linear frame size in bytes */ +static APR_INLINE apr_size_t mpf_codec_linear_frame_size_calculate(apr_uint16_t sampling_rate, apr_byte_t channel_count) +{ + return channel_count * BYTES_PER_SAMPLE * CODEC_FRAME_TIME_BASE * sampling_rate / 1000; +} + +/** Reset list of codec descriptors */ +static APR_INLINE void mpf_codec_list_reset(mpf_codec_list_t *codec_list) +{ + codec_list->descriptor_arr = NULL; + codec_list->preffered = NULL; +} + +/** Initialize list of codec descriptors */ +static APR_INLINE void mpf_codec_list_init(mpf_codec_list_t *codec_list, apr_size_t initial_count, apr_pool_t *pool) +{ + codec_list->descriptor_arr = apr_array_make(pool,(int)initial_count, sizeof(mpf_codec_descriptor_t)); +} + +/** Copy list of codec descriptors */ +static APR_INLINE void mpf_codec_list_copy(mpf_codec_list_t *codec_list, const mpf_codec_list_t *src_codec_list, apr_pool_t *pool) +{ + codec_list->descriptor_arr = apr_array_copy(pool,src_codec_list->descriptor_arr); +} + +/** Increment number of codec descriptors in the list and return the descriptor to fill */ +static APR_INLINE mpf_codec_descriptor_t* mpf_codec_list_add(mpf_codec_list_t *codec_list) +{ + mpf_codec_descriptor_t* descriptor = (mpf_codec_descriptor_t*)apr_array_push(codec_list->descriptor_arr); + mpf_codec_descriptor_init(descriptor); + return descriptor; +} + +/** Determine if codec list is empty */ +static APR_INLINE apt_bool_t mpf_codec_list_is_empty(const mpf_codec_list_t *codec_list) +{ + return apr_is_empty_array(codec_list->descriptor_arr); +} + +/** Get codec descriptor by index */ +static APR_INLINE mpf_codec_descriptor_t* mpf_codec_get(const mpf_codec_list_t *codec_list, apr_size_t id) +{ + mpf_codec_descriptor_t *descriptor; + if(id >= (apr_size_t)codec_list->descriptor_arr->nelts) { + return NULL; + } + descriptor = (mpf_codec_descriptor_t*)codec_list->descriptor_arr->elts; + return descriptor + id; +} + +/** Match two codec descriptors */ +MPF_DECLARE(apt_bool_t) mpf_codec_descriptor_match(const mpf_codec_descriptor_t *descriptor1, const mpf_codec_descriptor_t *descriptor2); +/** Intersect two codec lists */ +MPF_DECLARE(apt_bool_t) mpf_codec_list_intersect(mpf_codec_list_t *codec_list1, mpf_codec_list_t *codec_list2); + + +APT_END_EXTERN_C + +#endif /*__MPF_CODEC_DESCRIPTOR_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_codec_manager.h b/libs/unimrcp/libs/mpf/include/mpf_codec_manager.h new file mode 100644 index 0000000000..2b14070b52 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_codec_manager.h @@ -0,0 +1,53 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_CODEC_MANAGER_H__ +#define __MPF_CODEC_MANAGER_H__ + +/** + * @file mpf_codec_manager.h + * @brief MPF Codec Manager + */ + +#include "mpf_types.h" +#include "mpf_codec.h" + +APT_BEGIN_EXTERN_C + +/** Create codec manager */ +MPF_DECLARE(mpf_codec_manager_t*) mpf_codec_manager_create(apr_size_t codec_count, apr_pool_t *pool); + +/** Destroy codec manager */ +MPF_DECLARE(void) mpf_codec_manager_destroy(mpf_codec_manager_t *codec_manager); + +/** Register codec in codec manager */ +MPF_DECLARE(apt_bool_t) mpf_codec_manager_codec_register(mpf_codec_manager_t *codec_manager, mpf_codec_t *codec); + +/** Get (allocate) codec by codec descriptor */ +MPF_DECLARE(mpf_codec_t*) mpf_codec_manager_codec_get(const mpf_codec_manager_t *codec_manager, mpf_codec_descriptor_t *descriptor, apr_pool_t *pool); + +/** Get (allocate) list of available codecs */ +MPF_DECLARE(apt_bool_t) mpf_codec_manager_codec_list_get(const mpf_codec_manager_t *codec_manager, mpf_codec_list_t *codec_list, apr_pool_t *pool); + +/** Load (allocate) list of codecs */ +MPF_DECLARE(apt_bool_t) mpf_codec_manager_codec_list_load(const mpf_codec_manager_t *codec_manager, mpf_codec_list_t *codec_list, const char *str, apr_pool_t *pool); + +/** Find codec by name */ +MPF_DECLARE(const mpf_codec_t*) mpf_codec_manager_codec_find(const mpf_codec_manager_t *codec_manager, const apt_str_t *codec_name); + +APT_END_EXTERN_C + +#endif /*__MPF_CODEC_MANAGER_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_context.h b/libs/unimrcp/libs/mpf/include/mpf_context.h new file mode 100644 index 0000000000..49f99e27b1 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_context.h @@ -0,0 +1,88 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_CONTEXT_H__ +#define __MPF_CONTEXT_H__ + +/** + * @file mpf_context.h + * @brief MPF Context + */ + +#include "mpf_object.h" +#include "apt_obj_list.h" + +APT_BEGIN_EXTERN_C + +/** Definition of table item used in context */ +typedef void* table_item_t; + +/** Media processing context */ +struct mpf_context_t { + /** Pool to allocate memory from */ + apr_pool_t *pool; + /** External object */ + void *obj; + /** Set when context is addded to the list to ensure quick find on delete */ + apt_list_elem_t *elem; + + /** Max number of terminations */ + apr_size_t max_termination_count; + /** Current number of terminations */ + apr_size_t termination_count; + /** Table, which holds terminations and topology */ + table_item_t **table; +}; + + +/** + * Add termination to context. + * @param context the context to add termination to + * @param termination the termination to add + */ +MPF_DECLARE(apt_bool_t) mpf_context_termination_add(mpf_context_t *context, mpf_termination_t *termination); + +/** + * Subtract termination from context. + * @param context the context to subtract termination from + * @param termination the termination to subtract + */ +MPF_DECLARE(apt_bool_t) mpf_context_termination_subtract(mpf_context_t *context, mpf_termination_t *termination); + +/** + * Apply topology. + * @param context the context which holds the termination + * @param termination the termination to apply toplogy for + */ +MPF_DECLARE(apt_bool_t) mpf_context_topology_apply(mpf_context_t *context, mpf_termination_t *termination); + +/** + * Destroy topology. + * @param context the context which holds the termination + * @param termination the termination to destroy toplogy for + */ +MPF_DECLARE(apt_bool_t) mpf_context_topology_destroy(mpf_context_t *context, mpf_termination_t *termination); + +/** + * Process context. + * @param context the context + */ +MPF_DECLARE(apt_bool_t) mpf_context_process(mpf_context_t *context); + + +APT_END_EXTERN_C + +#endif /*__MPF_CONTEXT_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_decoder.h b/libs/unimrcp/libs/mpf/include/mpf_decoder.h new file mode 100644 index 0000000000..0d40bd346c --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_decoder.h @@ -0,0 +1,39 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_DECODER_H__ +#define __MPF_DECODER_H__ + +/** + * @file mpf_decoder.h + * @brief MPF Stream Decoder + */ + +#include "mpf_stream.h" + +APT_BEGIN_EXTERN_C + +/** + * Create audio stream decoder. + * @param source the source to get encoded stream from + * @param pool the pool to allocate memory from + */ +MPF_DECLARE(mpf_audio_stream_t*) mpf_decoder_create(mpf_audio_stream_t *source, apr_pool_t *pool); + + +APT_END_EXTERN_C + +#endif /*__MPF_ENCODER_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_encoder.h b/libs/unimrcp/libs/mpf/include/mpf_encoder.h new file mode 100644 index 0000000000..36a819da6a --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_encoder.h @@ -0,0 +1,39 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_ENCODER_H__ +#define __MPF_ENCODER_H__ + +/** + * @file mpf_encoder.h + * @brief MPF Stream Encoder + */ + +#include "mpf_stream.h" + +APT_BEGIN_EXTERN_C + +/** + * Create audio stream encoder. + * @param sink the sink to write encoded stream to + * @param pool the pool to allocate memory from + */ +MPF_DECLARE(mpf_audio_stream_t*) mpf_encoder_create(mpf_audio_stream_t *sink, apr_pool_t *pool); + + +APT_END_EXTERN_C + +#endif /*__MPF_ENCODER_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_engine.h b/libs/unimrcp/libs/mpf/include/mpf_engine.h new file mode 100644 index 0000000000..d1d38a61ea --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_engine.h @@ -0,0 +1,65 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_ENGINE_H__ +#define __MPF_ENGINE_H__ + +/** + * @file mpf_engine.h + * @brief Media Processing Framework Engine + */ + +#include "apt_task.h" +#include "mpf_message.h" + +APT_BEGIN_EXTERN_C + +/** + * Create MPF engine. + * @param pool the pool to allocate memory from + */ +MPF_DECLARE(mpf_engine_t*) mpf_engine_create(apr_pool_t *pool); + +/** + * Create MPF codec manager. + * @param pool the pool to allocate memory from + */ +MPF_DECLARE(mpf_codec_manager_t*) mpf_engine_codec_manager_create(apr_pool_t *pool); + +/** + * Register MPF codec manager. + * @param engine the engine to register codec manager for + * @param codec_manager the codec manager to register + */ +MPF_DECLARE(apt_bool_t) mpf_engine_codec_manager_register(mpf_engine_t *engine, const mpf_codec_manager_t *codec_manager); + +/** + * Get task. + * @param engine the engine to get task from + */ +MPF_DECLARE(apt_task_t*) mpf_task_get(mpf_engine_t *engine); + +/** + * Set task msg type to send responses and events with. + * @param engine the engine to set task msg type for + * @param type the type to set + */ +MPF_DECLARE(void) mpf_engine_task_msg_type_set(mpf_engine_t *engine, apt_task_msg_type_e type); + + +APT_END_EXTERN_C + +#endif /*__MPF_ENGINE_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_file_termination_factory.h b/libs/unimrcp/libs/mpf/include/mpf_file_termination_factory.h new file mode 100644 index 0000000000..f824328039 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_file_termination_factory.h @@ -0,0 +1,37 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_FILE_TERMINATION_FACTORY_H__ +#define __MPF_FILE_TERMINATION_FACTORY_H__ + +/** + * @file mpf_file_termination_factory.h + * @brief MPF File Termination Factory + */ + +#include "mpf_types.h" + +APT_BEGIN_EXTERN_C + +/** + * Create file termination factory. + */ +MPF_DECLARE(mpf_termination_factory_t*) mpf_file_termination_factory_create(apr_pool_t *pool); + + +APT_END_EXTERN_C + +#endif /*__MPF_RTP_TERMINATION_FACTORY_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_frame.h b/libs/unimrcp/libs/mpf/include/mpf_frame.h new file mode 100644 index 0000000000..b1c8c438ac --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_frame.h @@ -0,0 +1,79 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_FRAME_H__ +#define __MPF_FRAME_H__ + +/** + * @file mpf_frame.h + * @brief MPF Audio/Video/Named-event Frame + */ + +#include "mpf_codec_descriptor.h" + +APT_BEGIN_EXTERN_C + +/** Media frame types */ +typedef enum { + MEDIA_FRAME_TYPE_NONE = 0x0, /**< none */ + MEDIA_FRAME_TYPE_AUDIO = 0x1, /**< audio frame */ + MEDIA_FRAME_TYPE_VIDEO = 0x2, /**< video frame */ + MEDIA_FRAME_TYPE_EVENT = 0x4 /**< named event frame (RFC2833) */ +} mpf_frame_type_e; + +/** Named event declaration */ +typedef struct mpf_named_event_frame_t mpf_named_event_frame_t; +/** Media frame declaration */ +typedef struct mpf_frame_t mpf_frame_t; + + +/** Named event (RFC2833, out-of-band DTMF) */ +struct mpf_named_event_frame_t { + /** event (DTMF, tone) identifier */ + apr_uint32_t event_id: 8; +#if (APR_IS_BIGENDIAN == 1) + /** end of event */ + apr_uint32_t edge: 1; + /** reserved */ + apr_uint32_t reserved: 1; + /** tone volume */ + apr_uint32_t volume: 6; +#else + /** tone volume */ + apr_uint32_t volume: 6; + /** reserved */ + apr_uint32_t reserved: 1; + /** end of event */ + apr_uint32_t edge: 1; +#endif + /** event duration */ + apr_uint32_t duration: 16; +}; + +/** Media frame */ +struct mpf_frame_t { + /** frame type (audio/video/named-event) mpf_frame_type_e */ + int type; + /** codec frame */ + mpf_codec_frame_t codec_frame; + /** named-event frame */ + mpf_named_event_frame_t event_frame; +}; + + +APT_END_EXTERN_C + +#endif /*__MPF_FRAME_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_jitter_buffer.h b/libs/unimrcp/libs/mpf/include/mpf_jitter_buffer.h new file mode 100644 index 0000000000..d638439824 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_jitter_buffer.h @@ -0,0 +1,63 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_JITTER_BUFFER_H__ +#define __MPF_JITTER_BUFFER_H__ + +/** + * @file mpf_jitter_buffer.h + * @brief Jitter Buffer + */ + +#include "mpf_frame.h" +#include "mpf_codec.h" +#include "mpf_rtp_descriptor.h" + +APT_BEGIN_EXTERN_C + +/** Jitter buffer write result */ +typedef enum { + JB_OK, /**< successful write */ + JB_DISCARD_NOT_ALLIGNED, /**< discarded write (frame isn't alligned to CODEC_FRAME_TIME_BASE) */ + JB_DISCARD_TOO_LATE, /**< discarded write (frame is arrived too late) */ + JB_DISCARD_TOO_EARLY, /**< discarded write (frame is arrived too early, buffer is full) */ +} jb_result_t; + +/** Opaque jitter buffer declaration */ +typedef struct mpf_jitter_buffer_t mpf_jitter_buffer_t; + + +/** Create jitter buffer */ +mpf_jitter_buffer_t* mpf_jitter_buffer_create(mpf_jb_config_t *jb_config, mpf_codec_t *codec, apr_pool_t *pool); + +/** Destroy jitter buffer */ +void mpf_jitter_buffer_destroy(mpf_jitter_buffer_t *jb); + +/** Restart jitter buffer */ +apt_bool_t mpf_jitter_buffer_restart(mpf_jitter_buffer_t *jb); + +/** Write audio data to jitter buffer */ +jb_result_t mpf_jitter_buffer_write(mpf_jitter_buffer_t *jb, mpf_codec_t *codec, void *buffer, apr_size_t size, apr_uint32_t ts); + +/** Write named event to jitter buffer */ +jb_result_t mpf_jitter_buffer_write_named_event(mpf_jitter_buffer_t *jb, mpf_named_event_frame_t *named_event, apr_uint32_t ts); + +/** Read media frame from jitter buffer */ +apt_bool_t mpf_jitter_buffer_read(mpf_jitter_buffer_t *jb, mpf_frame_t *media_frame); + +APT_END_EXTERN_C + +#endif /*__MPF_JITTER_BUFFER_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_media_descriptor.h b/libs/unimrcp/libs/mpf/include/mpf_media_descriptor.h new file mode 100644 index 0000000000..af54224ceb --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_media_descriptor.h @@ -0,0 +1,66 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_MEDIA_DESCRIPTOR_H__ +#define __MPF_MEDIA_DESCRIPTOR_H__ + +/** + * @file mpf_media_descriptor.h + * @brief Media Descriptor Base + */ + +#include +#include "apt_string.h" + +APT_BEGIN_EXTERN_C + +/** MPF media state */ +typedef enum { + MPF_MEDIA_DISABLED, /**< disabled media */ + MPF_MEDIA_ENABLED /**< enabled media */ +} mpf_media_state_e; + +/** MPF media descriptor declaration */ +typedef struct mpf_media_descriptor_t mpf_media_descriptor_t; + +/** MPF media descriptor */ +struct mpf_media_descriptor_t { + /** Media state (disabled/enabled)*/ + mpf_media_state_e state; + + /** Ip address */ + apt_str_t ip; + /** External (NAT) Ip address */ + apt_str_t ext_ip; + /** Port */ + apr_port_t port; + /** Identifier (0,1,...) */ + apr_size_t id; +}; + +/** Initialize MPF media descriptor */ +static APR_INLINE void mpf_media_descriptor_init(mpf_media_descriptor_t *descriptor) +{ + descriptor->state = MPF_MEDIA_DISABLED; + apt_string_reset(&descriptor->ip); + apt_string_reset(&descriptor->ext_ip); + descriptor->port = 0; + descriptor->id = 0; +} + +APT_END_EXTERN_C + +#endif /*__MPF_MEDIA_DESCRIPTOR_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_message.h b/libs/unimrcp/libs/mpf/include/mpf_message.h new file mode 100644 index 0000000000..ae69638519 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_message.h @@ -0,0 +1,73 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_MESSAGE_H__ +#define __MPF_MESSAGE_H__ + +/** + * @file mpf_message.h + * @brief Media Processing Framework Message Definitions + */ + +#include "mpf_types.h" + +APT_BEGIN_EXTERN_C + +/** Enumeration of MPF message types */ +typedef enum { + MPF_MESSAGE_TYPE_REQUEST, /**< request message */ + MPF_MESSAGE_TYPE_RESPONSE, /**< response message */ + MPF_MESSAGE_TYPE_EVENT /**< event message */ +} mpf_message_type_e; + +/** Enumeration of MPF status codes */ +typedef enum { + MPF_STATUS_CODE_SUCCESS, /**< indicates success */ + MPF_STATUS_CODE_FAILURE /**< indicates failure */ +} mpf_status_code_e; + + +/** Enumeration of commands */ +typedef enum { + MPF_COMMAND_ADD, /**< add termination to context */ + MPF_COMMAND_MODIFY, /**< modify termination properties */ + MPF_COMMAND_SUBTRACT,/**< subtract termination from context */ + MPF_COMMAND_MOVE /**< move termination to another context */ +} mpf_command_type_e; + +/** MPF message declaration */ +typedef struct mpf_message_t mpf_message_t; + +/** MPF message definition */ +struct mpf_message_t { + /** Message type (request/response/event) */ + mpf_message_type_e message_type; + /** Command identifier (add, modify, subtract, ...) */ + mpf_command_type_e command_id; + /** Status code used in responses */ + mpf_status_code_e status_code; + + /** Context */ + mpf_context_t *context; + /** Termination */ + mpf_termination_t *termination; + /** Termination type dependent descriptor */ + void *descriptor; +}; + +APT_END_EXTERN_C + +#endif /*__MPF_MESSAGE_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_object.h b/libs/unimrcp/libs/mpf/include/mpf_object.h new file mode 100644 index 0000000000..f4136b0ed2 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_object.h @@ -0,0 +1,52 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_OBJECT_H__ +#define __MPF_OBJECT_H__ + +/** + * @file mpf_object.h + * @brief Media Processing Object Base (bridge, multiplexor, mixer, ...) + */ + +#include "mpf_types.h" +#include "mpf_frame.h" + +APT_BEGIN_EXTERN_C + +/** MPF object declaration */ +typedef struct mpf_object_t mpf_object_t; + +/** Base for media processing objects */ +struct mpf_object_t { + /** Audio stream source */ + mpf_audio_stream_t *source; + /** Audio stream sink */ + mpf_audio_stream_t *sink; + + /** Media frame used to read data from source and write it to sink */ + mpf_frame_t frame; + + /** Virtual process */ + apt_bool_t (*process)(mpf_object_t *object); + /** Virtual destroy */ + apt_bool_t (*destroy)(mpf_object_t *object); +}; + + +APT_END_EXTERN_C + +#endif /*__MPF_OBJECT_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_rtp_attribs.h b/libs/unimrcp/libs/mpf/include/mpf_rtp_attribs.h new file mode 100644 index 0000000000..5fe3d96067 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_rtp_attribs.h @@ -0,0 +1,55 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_RTP_ATTRIBS_H__ +#define __MPF_RTP_ATTRIBS_H__ + +/** + * @file mpf_rtp_attribs.h + * @brief RTP Attributes (SDP) + */ + +#include "mpf_media_descriptor.h" +#include "mpf_stream_mode.h" + +APT_BEGIN_EXTERN_C + +/** RTP attributes */ +typedef enum { + RTP_ATTRIB_RTPMAP, + RTP_ATTRIB_SENDONLY, + RTP_ATTRIB_RECVONLY, + RTP_ATTRIB_SENDRECV, + RTP_ATTRIB_MID, + RTP_ATTRIB_PTIME, + + RTP_ATTRIB_COUNT, + RTP_ATTRIB_UNKNOWN = RTP_ATTRIB_COUNT +} mpf_rtp_attrib_e; + + +/** Get audio media attribute name by attribute identifier */ +MPF_DECLARE(const apt_str_t*) mpf_rtp_attrib_str_get(mpf_rtp_attrib_e attrib_id); + +/** Find audio media attribute identifier by attribute name */ +MPF_DECLARE(mpf_rtp_attrib_e) mpf_rtp_attrib_id_find(const apt_str_t *attrib); + +/** Get string by stream mode (send/receive) */ +MPF_DECLARE(const apt_str_t*) mpf_stream_mode_str_get(mpf_stream_mode_e direction); + +APT_END_EXTERN_C + +#endif /*__MPF_RTP_ATTRIBS_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_rtp_defs.h b/libs/unimrcp/libs/mpf/include/mpf_rtp_defs.h new file mode 100644 index 0000000000..9fc7925fc7 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_rtp_defs.h @@ -0,0 +1,184 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_RTP_DEFS_H__ +#define __MPF_RTP_DEFS_H__ + +/** + * @file mpf_rtp_defs.h + * @brief Internal RTP Definitions + */ + +#include "mpf_rtp_stat.h" +#include "mpf_jitter_buffer.h" + +APT_BEGIN_EXTERN_C + +/** Used to calculate actual number of received packets (32bit) in + * case seq number (16bit) wrapped around */ +#define RTP_SEQ_MOD (1 << 16) +/** Number of max dropout packets (seq numbers) is used to trigger drift + * in seq number or misorder packets */ +#define MAX_DROPOUT 3000 +/** Number of max misorder packets (seq numbers) to differentiate + * seq drift from misorder packets */ +#define MAX_MISORDER 100 +/** Restart receiver if threshold is fired */ +#define DISCARDED_TO_RECEIVED_RATIO_THRESHOLD 30 /* 30% */ +/** Deviation threshold is used to trigger drift in timestamps */ +#define DEVIATION_THRESHOLD 4000 + +/** RTP receive history declaration */ +typedef struct rtp_rx_history_t rtp_rx_history_t; +/** RTP receive periodic history declaration */ +typedef struct rtp_rx_periodic_history_t rtp_rx_periodic_history_t; +/** RTP receiver declaration */ +typedef struct rtp_receiver_t rtp_receiver_t; +/** RTP transmitter declaration */ +typedef struct rtp_transmitter_t rtp_transmitter_t; + +/** History of RTP receive */ +struct rtp_rx_history_t { + /** Updated on every seq num wrap around*/ + apr_uint32_t seq_cycles; + + /** First seq num received */ + apr_uint16_t seq_num_base; + /** Max seq num received */ + apr_uint16_t seq_num_max; + + /** Last timestamp received */ + apr_uint32_t ts_last; + /** Local time measured on last packet received */ + apr_time_t time_last; + + /** New ssrc, which is in probation */ + apr_uint32_t ssrc_new; + /** Period of ssrc probation */ + apr_byte_t ssrc_probation; +}; + +/** Periodic history of RTP receive (initialized after every N packets) */ +struct rtp_rx_periodic_history_t { + /** Number of packets received */ + apr_uint32_t received_prior; + /** Number of packets discarded */ + apr_uint32_t discarded_prior; + + /** Min jitter */ + apr_uint32_t jitter_min; + /** Max jitter */ + apr_uint32_t jitter_max; +}; + +/** Reset RTP receive history */ +static APR_INLINE void mpf_rtp_rx_history_reset(rtp_rx_history_t *rx_history) +{ + memset(rx_history,0,sizeof(rtp_rx_history_t)); +} + +/** Reset RTP receive periodic history */ +static APR_INLINE void mpf_rtp_rx_periodic_history_reset(rtp_rx_periodic_history_t *rx_periodic_history) +{ + memset(rx_periodic_history,0,sizeof(rtp_rx_periodic_history_t)); +} + +/** RTP receiver */ +struct rtp_receiver_t { + /** Payload type of named-event packets (RFC2833) */ + apr_byte_t event_pt; + + /** Jitter buffer */ + mpf_jitter_buffer_t *jb; + + /** RTP receive statistics to report */ + rtp_rx_stat_t stat; + /** RTP history */ + rtp_rx_history_t history; + /** RTP periodic history */ + rtp_rx_periodic_history_t periodic_history; +}; + + +/** RTP transmitter */ +struct rtp_transmitter_t { + /** RTP stream ssrc */ + apr_uint32_t ssrc; + /** Payload type of named-event packets (RFC2833) */ + apr_byte_t event_pt; + /** Packetization time in msec */ + apr_uint16_t ptime; + + /** Number of frames in a packet */ + apr_uint16_t packet_frames; + /** Current number of frames */ + apr_uint16_t current_frames; + /** Samples in frames in timestamp units */ + apr_uint32_t samples_per_frame; + + /** Indicate silence period among the talkspurts */ + apr_byte_t inactivity; + /** Last seq number sent */ + apr_uint16_t last_seq_num; + /** Current timestamp (samples processed) */ + apr_uint32_t timestamp; + + /** RTP packet payload */ + char *packet_data; + /** RTP packet payload size */ + apr_size_t packet_size; + + /** RTP transmit statistics to report */ + rtp_tx_stat_t stat; +}; + + +/** Initialize RTP receiver */ +static APR_INLINE void rtp_receiver_init(rtp_receiver_t *receiver) +{ + receiver->event_pt = 0; + + receiver->jb = NULL; + + mpf_rtp_rx_stat_reset(&receiver->stat); + mpf_rtp_rx_history_reset(&receiver->history); + mpf_rtp_rx_periodic_history_reset(&receiver->periodic_history); +} + +/** Initialize RTP transmitter */ +static APR_INLINE void rtp_transmitter_init(rtp_transmitter_t *transmitter) +{ + transmitter->ssrc = 0; + transmitter->event_pt = 0; + transmitter->ptime = 0; + + transmitter->packet_frames = 0; + transmitter->current_frames = 0; + transmitter->samples_per_frame = 0; + + transmitter->inactivity = 0; + transmitter->last_seq_num = 0; + transmitter->timestamp = 0; + + transmitter->packet_data = NULL; + transmitter->packet_size = 0; + + mpf_rtp_tx_stat_reset(&transmitter->stat); +} + +APT_END_EXTERN_C + +#endif /*__MPF_RTP_DEFS_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_rtp_descriptor.h b/libs/unimrcp/libs/mpf/include/mpf_rtp_descriptor.h new file mode 100644 index 0000000000..46655e4c27 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_rtp_descriptor.h @@ -0,0 +1,158 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_RTP_DESCRIPTOR_H__ +#define __MPF_RTP_DESCRIPTOR_H__ + +/** + * @file mpf_rtp_descriptor.h + * @brief MPF RTP Stream Descriptor + */ + +#include "mpf_stream_mode.h" +#include "mpf_media_descriptor.h" +#include "mpf_codec_descriptor.h" + +APT_BEGIN_EXTERN_C + +/** RTP media descriptor declaration */ +typedef struct mpf_rtp_media_descriptor_t mpf_rtp_media_descriptor_t; +/** RTP stream descriptor declaration */ +typedef struct mpf_rtp_stream_descriptor_t mpf_rtp_stream_descriptor_t; +/** RTP termination descriptor declaration */ +typedef struct mpf_rtp_termination_descriptor_t mpf_rtp_termination_descriptor_t; +/** RTP configuration declaration */ +typedef struct mpf_rtp_config_t mpf_rtp_config_t; +/** Jitter buffer configuration declaration */ +typedef struct mpf_jb_config_t mpf_jb_config_t; + + +/** RTP media (local/remote) descriptor */ +struct mpf_rtp_media_descriptor_t { + /** Media descriptor base */ + mpf_media_descriptor_t base; + /** Stream mode (send/receive) */ + mpf_stream_mode_e mode; + /** Packetization time */ + apr_uint16_t ptime; + /** Codec list */ + mpf_codec_list_t codec_list; + /** Media identifier */ + apr_size_t mid; +}; + +/** RTP stream descriptor */ +struct mpf_rtp_stream_descriptor_t { + /** Local media descriptor */ + mpf_rtp_media_descriptor_t *local; + /** Remote media descriptor */ + mpf_rtp_media_descriptor_t *remote; +}; + +/** RTP termination descriptor */ +struct mpf_rtp_termination_descriptor_t { + /** Audio stream descriptor */ + mpf_rtp_stream_descriptor_t audio; + /** Video stream descriptor */ + mpf_rtp_stream_descriptor_t video; +}; + +/** Jitter buffer configuration */ +struct mpf_jb_config_t { + /** Min playout delay in msec (used in case of adaptive jitter buffer) */ + apr_size_t min_playout_delay; + /** Initial playout delay in msec */ + apr_size_t initial_playout_delay; + /** Max playout delay in msec (used in case of adaptive jitter buffer) */ + apr_size_t max_playout_delay; + /** Static - 0, adaptive - 1 jitter buffer */ + apr_byte_t adaptive; +}; + +/** RTP config */ +struct mpf_rtp_config_t { + /** Local IP address to bind to */ + apt_str_t ip; + /** External (NAT) IP address */ + apt_str_t ext_ip; + /** Min RTP port */ + apr_port_t rtp_port_min; + /** Max RTP port */ + apr_port_t rtp_port_max; + /** Current RTP port */ + apr_port_t rtp_port_cur; + /** Packetization time */ + apr_uint16_t ptime; + /** Codec list */ + mpf_codec_list_t codec_list; + /** Preference in offer/anser: 1 - own(local) preference, 0 - remote preference */ + apt_bool_t own_preferrence; + /** Jitter buffer config */ + mpf_jb_config_t jb_config; +}; + +/** Initialize media descriptor */ +static APR_INLINE void mpf_rtp_media_descriptor_init(mpf_rtp_media_descriptor_t *media) +{ + mpf_media_descriptor_init(&media->base); + media->mode = STREAM_MODE_NONE; + media->ptime = 0; + mpf_codec_list_reset(&media->codec_list); + media->mid = 0; +} + +/** Initialize stream descriptor */ +static APR_INLINE void mpf_rtp_stream_descriptor_init(mpf_rtp_stream_descriptor_t *stream) +{ + stream->local = NULL; + stream->remote = NULL; +} + +/** Initialize RTP termination descriptor */ +static APR_INLINE void mpf_rtp_termination_descriptor_init(mpf_rtp_termination_descriptor_t *rtp_descriptor) +{ + mpf_rtp_stream_descriptor_init(&rtp_descriptor->audio); + mpf_rtp_stream_descriptor_init(&rtp_descriptor->video); +} + +/** Initialize JB config */ +static APR_INLINE void mpf_jb_config_init(mpf_jb_config_t *jb_config) +{ + jb_config->adaptive = 0; + jb_config->initial_playout_delay = 0; + jb_config->min_playout_delay = 0; + jb_config->max_playout_delay = 0; +} + +/** Create/allocate RTP config */ +static APR_INLINE mpf_rtp_config_t* mpf_rtp_config_create(apr_pool_t *pool) +{ + mpf_rtp_config_t *rtp_config = (mpf_rtp_config_t*)apr_palloc(pool,sizeof(mpf_rtp_config_t)); + apt_string_reset(&rtp_config->ip); + apt_string_reset(&rtp_config->ext_ip); + rtp_config->rtp_port_cur = 0; + rtp_config->rtp_port_min = 0; + rtp_config->rtp_port_max = 0; + rtp_config->ptime = 0; + mpf_codec_list_init(&rtp_config->codec_list,0,pool); + rtp_config->own_preferrence = FALSE; + mpf_jb_config_init(&rtp_config->jb_config); + return rtp_config; +} + +APT_END_EXTERN_C + +#endif /*__MPF_RTP_DESCRIPTOR_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_rtp_header.h b/libs/unimrcp/libs/mpf/include/mpf_rtp_header.h new file mode 100644 index 0000000000..070bf8c99c --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_rtp_header.h @@ -0,0 +1,87 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_RTP_HEADER_H__ +#define __MPF_RTP_HEADER_H__ + +/** + * @file mpf_rtp_header.h + * @brief RTP Header Definition + */ + +#include "mpf.h" + +APT_BEGIN_EXTERN_C + +/** Protocol version. */ +#define RTP_VERSION 2 + +/** RTP header declaration */ +typedef struct rtp_header_t rtp_header_t; +/** RTP extension header declaration */ +typedef struct rtp_extension_header_t rtp_extension_header_t; + + +/** RTP header */ +struct rtp_header_t { +#if (APR_IS_BIGENDIAN == 1) + /** protocol version */ + apr_uint32_t version: 2; + /** padding flag */ + apr_uint32_t padding: 1; + /** header extension flag */ + apr_uint32_t extension: 1; + /** CSRC count */ + apr_uint32_t count: 4; + /** marker bit */ + apr_uint32_t marker: 1; + /** payload type */ + apr_uint32_t type: 7; +#else + /** CSRC count */ + apr_uint32_t count: 4; + /** header extension flag */ + apr_uint32_t extension: 1; + /** padding flag */ + apr_uint32_t padding: 1; + /** protocol version */ + apr_uint32_t version: 2; + /** payload type */ + apr_uint32_t type: 7; + /** marker bit */ + apr_uint32_t marker: 1; +#endif + + /** sequence number */ + apr_uint32_t sequence: 16; + /** timestamp */ + apr_uint32_t timestamp; + /** synchronization source */ + apr_uint32_t ssrc; +}; + +/** RTP extension header */ +struct rtp_extension_header_t +{ + /** profile */ + apr_uint16_t profile; + /** length */ + apr_uint16_t length; +}; + +APT_END_EXTERN_C + +#endif /*__MPF_RTP_HEADER_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_rtp_stat.h b/libs/unimrcp/libs/mpf/include/mpf_rtp_stat.h new file mode 100644 index 0000000000..248a4b5c66 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_rtp_stat.h @@ -0,0 +1,83 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_RTP_STAT_H__ +#define __MPF_RTP_STAT_H__ + +/** + * @file mpf_rtp_stat.h + * @brief RTP Statistics + */ + +#include "mpf.h" + +APT_BEGIN_EXTERN_C + +/** RTP transmit statistics declaration */ +typedef struct rtp_tx_stat_t rtp_tx_stat_t; +/** RTP receive statistics declaration */ +typedef struct rtp_rx_stat_t rtp_rx_stat_t; + + +/** RTP transmit statistics */ +struct rtp_tx_stat_t { + /** number of RTP packets received */ + apr_uint32_t sent_packets; + + /* more to come */ +}; + +/** RTP receive statistics */ +struct rtp_rx_stat_t { + /** number of valid RTP packets received */ + apr_uint32_t received_packets; + /** number of invalid RTP packets received */ + apr_uint32_t invalid_packets; + + /** number of discarded in jitter buffer packets */ + apr_uint32_t discarded_packets; + /** number of ignored packets */ + apr_uint32_t ignored_packets; + + /** number of lost in network packets */ + apr_uint32_t lost_packets; + + /** number of restarts */ + apr_byte_t restarts; + + /** network jitter (rfc3550) */ + apr_uint32_t jitter; + + /** source id of received RTP stream */ + apr_uint32_t ssrc; +}; + + +/** Reset RTP transmit statistics */ +static APR_INLINE void mpf_rtp_tx_stat_reset(rtp_tx_stat_t *tx_stat) +{ + memset(tx_stat,0,sizeof(rtp_tx_stat_t)); +} + +/** Reset RTP receive statistics */ +static APR_INLINE void mpf_rtp_rx_stat_reset(rtp_rx_stat_t *rx_stat) +{ + memset(rx_stat,0,sizeof(rtp_rx_stat_t)); +} + +APT_END_EXTERN_C + +#endif /*__MPF_RTP_STAT_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_rtp_stream.h b/libs/unimrcp/libs/mpf/include/mpf_rtp_stream.h new file mode 100644 index 0000000000..848cb54c84 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_rtp_stream.h @@ -0,0 +1,47 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_RTP_STREAM_H__ +#define __MPF_RTP_STREAM_H__ + +/** + * @file mpf_rtp_stream.h + * @brief MPF RTP Stream + */ + +#include "mpf_stream.h" +#include "mpf_rtp_descriptor.h" + +APT_BEGIN_EXTERN_C + +/** + * Create RTP stream. + * @param termination the back pointer to hold + * @param config the configuration to use + * @param pool the pool to allocate memory from + */ +MPF_DECLARE(mpf_audio_stream_t*) mpf_rtp_stream_create(mpf_termination_t *termination, mpf_rtp_config_t *config, apr_pool_t *pool); + +/** + * Modify RTP stream. + * @param stream RTP stream to modify + * @param descriptor the descriptor to modify stream according + */ +MPF_DECLARE(apt_bool_t) mpf_rtp_stream_modify(mpf_audio_stream_t *stream, mpf_rtp_stream_descriptor_t *descriptor); + +APT_END_EXTERN_C + +#endif /*__MPF_RTP_STREAM_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_rtp_termination_factory.h b/libs/unimrcp/libs/mpf/include/mpf_rtp_termination_factory.h new file mode 100644 index 0000000000..2f9f171e3c --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_rtp_termination_factory.h @@ -0,0 +1,41 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_RTP_TERMINATION_FACTORY_H__ +#define __MPF_RTP_TERMINATION_FACTORY_H__ + +/** + * @file mpf_rtp_termination_factory.h + * @brief MPF RTP Termination Factory + */ + +#include +#include "mpf_types.h" +#include "mpf_rtp_descriptor.h" + +APT_BEGIN_EXTERN_C + +/** + * Create RTP termination factory. + */ +MPF_DECLARE(mpf_termination_factory_t*) mpf_rtp_termination_factory_create( + mpf_rtp_config_t *rtp_config, + apr_pool_t *pool); + + +APT_END_EXTERN_C + +#endif /*__MPF_RTP_TERMINATION_FACTORY_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_stream.h b/libs/unimrcp/libs/mpf/include/mpf_stream.h new file mode 100644 index 0000000000..9df864ecfc --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_stream.h @@ -0,0 +1,151 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_STREAM_H__ +#define __MPF_STREAM_H__ + +/** + * @file mpf_stream.h + * @brief MPF Bidirectional Stream + */ + +#include "mpf_types.h" +#include "mpf_stream_mode.h" +#include "mpf_frame.h" +#include "mpf_codec.h" + +APT_BEGIN_EXTERN_C + +/** Opaque audio stream virtual table declaration */ +typedef struct mpf_audio_stream_vtable_t mpf_audio_stream_vtable_t; + +/** Audio stream */ +struct mpf_audio_stream_t { + /** External object */ + void *obj; + /** Table of virtual methods */ + const mpf_audio_stream_vtable_t *vtable; + /** Back pointer */ + mpf_termination_t *termination; + /** Stream mode (send/receive) */ + mpf_stream_mode_e mode; + /** Receive codec */ + mpf_codec_t *rx_codec; + /** Transmit codec */ + mpf_codec_t *tx_codec; +}; + +/** Video stream */ +struct mpf_video_stream_t { + /** Back pointer */ + mpf_termination_t *termination; + /** Stream mode (send/receive) */ + mpf_stream_mode_e mode; +}; + +/** Table of audio stream virtual methods */ +struct mpf_audio_stream_vtable_t { + /** Virtual destroy method */ + apt_bool_t (*destroy)(mpf_audio_stream_t *stream); + + /** Virtual open receiver method */ + apt_bool_t (*open_rx)(mpf_audio_stream_t *stream); + /** Virtual close receiver method */ + apt_bool_t (*close_rx)(mpf_audio_stream_t *stream); + /** Virtual read frame method */ + apt_bool_t (*read_frame)(mpf_audio_stream_t *stream, mpf_frame_t *frame); + + /** Virtual open transmitter method */ + apt_bool_t (*open_tx)(mpf_audio_stream_t *stream); + /** Virtual close transmitter method */ + apt_bool_t (*close_tx)(mpf_audio_stream_t *stream); + /** Virtual write frame method */ + apt_bool_t (*write_frame)(mpf_audio_stream_t *stream, const mpf_frame_t *frame); +}; + + +/** Create audio stream */ +static APR_INLINE mpf_audio_stream_t* mpf_audio_stream_create(void *obj, const mpf_audio_stream_vtable_t *vtable, mpf_stream_mode_e mode, apr_pool_t *pool) +{ + mpf_audio_stream_t *stream = (mpf_audio_stream_t*)apr_palloc(pool,sizeof(mpf_audio_stream_t)); + stream->obj = obj; + stream->vtable = vtable; + stream->termination = NULL; + stream->mode = mode; + stream->rx_codec = NULL; + stream->tx_codec = NULL; + return stream; +} + +/** Destroy audio stream */ +static APR_INLINE apt_bool_t mpf_audio_stream_destroy(mpf_audio_stream_t *stream) +{ + if(stream->vtable->destroy) + return stream->vtable->destroy(stream); + return TRUE; +} + +/** Open audio stream receive */ +static APR_INLINE apt_bool_t mpf_audio_stream_rx_open(mpf_audio_stream_t *stream) +{ + if(stream->vtable->open_rx) + return stream->vtable->open_rx(stream); + return TRUE; +} + +/** Close audio stream receive */ +static APR_INLINE apt_bool_t mpf_audio_stream_rx_close(mpf_audio_stream_t *stream) +{ + if(stream->vtable->close_rx) + return stream->vtable->close_rx(stream); + return TRUE; +} + +/** Read frame */ +static APR_INLINE apt_bool_t mpf_audio_stream_frame_read(mpf_audio_stream_t *stream, mpf_frame_t *frame) +{ + if(stream->vtable->read_frame) + return stream->vtable->read_frame(stream,frame); + return TRUE; +} + +/** Open audio stream transmit */ +static APR_INLINE apt_bool_t mpf_audio_stream_tx_open(mpf_audio_stream_t *stream) +{ + if(stream->vtable->open_tx) + return stream->vtable->open_tx(stream); + return TRUE; +} + +/** Close audio stream transmit */ +static APR_INLINE apt_bool_t mpf_audio_stream_tx_close(mpf_audio_stream_t *stream) +{ + if(stream->vtable->close_tx) + return stream->vtable->close_tx(stream); + return TRUE; +} + +/** Write frame */ +static APR_INLINE apt_bool_t mpf_audio_stream_frame_write(mpf_audio_stream_t *stream, const mpf_frame_t *frame) +{ + if(stream->vtable->write_frame) + return stream->vtable->write_frame(stream,frame); + return TRUE; +} + +APT_END_EXTERN_C + +#endif /*__MPF_STREAM_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_stream_mode.h b/libs/unimrcp/libs/mpf/include/mpf_stream_mode.h new file mode 100644 index 0000000000..9b7cf0da55 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_stream_mode.h @@ -0,0 +1,53 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_STREAM_MODE_H__ +#define __MPF_STREAM_MODE_H__ + +/** + * @file mpf_stream_mode.h + * @brief MPF Stream Mode (Send/Receive) + */ + +#include "mpf.h" + +APT_BEGIN_EXTERN_C + +/** Enumeration of stream modes */ +typedef enum { + STREAM_MODE_NONE = 0x0, /**< none */ + STREAM_MODE_SEND = 0x1, /**< send */ + STREAM_MODE_RECEIVE = 0x2, /**< receive */ + + STREAM_MODE_SEND_RECEIVE = STREAM_MODE_SEND | STREAM_MODE_RECEIVE /**< send and receive */ +} mpf_stream_mode_e; + +static APR_INLINE mpf_stream_mode_e mpf_stream_mode_negotiate(mpf_stream_mode_e remote_mode) +{ + mpf_stream_mode_e local_mode = remote_mode; + if(local_mode == STREAM_MODE_SEND) { + local_mode = STREAM_MODE_RECEIVE; + } + else if(local_mode == STREAM_MODE_RECEIVE) { + local_mode = STREAM_MODE_SEND; + } + return local_mode; +} + + +APT_END_EXTERN_C + +#endif /*__MPF_STREAM_MODE_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_termination.h b/libs/unimrcp/libs/mpf/include/mpf_termination.h new file mode 100644 index 0000000000..df51fbbbf6 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_termination.h @@ -0,0 +1,135 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_TERMINATION_H__ +#define __MPF_TERMINATION_H__ + +/** + * @file mpf_termination.h + * @brief MPF Termination + */ + +#include "mpf_types.h" + +APT_BEGIN_EXTERN_C + +/** Prototype of termination event handler */ +typedef apt_bool_t (*mpf_termination_event_handler_f)(mpf_termination_t *termination, int event_id, void *descriptor); + +/** MPF Termination */ +struct mpf_termination_t { + /** Pool to allocate memory from */ + apr_pool_t *pool; + /** External object */ + void *obj; + /** Object to send events to */ + void *event_handler_obj; + /** Event handler */ + mpf_termination_event_handler_f event_handler; + /** Codec manager */ + const mpf_codec_manager_t *codec_manager; + /** Termination factory entire termination created by */ + mpf_termination_factory_t *termination_factory; + /** Table of virtual methods */ + const mpf_termination_vtable_t *vtable; + /** Slot in context */ + apr_size_t slot; + + /** Audio stream */ + mpf_audio_stream_t *audio_stream; + /** Video stream */ + mpf_video_stream_t *video_stream; +}; + +/** MPF termination factory */ +struct mpf_termination_factory_t { + /** Virtual create */ + mpf_termination_t* (*create_termination)(mpf_termination_factory_t *factory, void *obj, apr_pool_t *pool); + + /* more to add */ +}; + + +/** + * Create MPF termination base. + * @param termination_factory the termination factory + * @param obj the external object associated with termination + * @param vtable the table of virtual functions of termination + * @param audio_stream the audio stream + * @param video_stream the video stream + * @param pool the pool to allocate memory from + */ +MPF_DECLARE(mpf_termination_t*) mpf_termination_base_create( + mpf_termination_factory_t *termination_factory, + void *obj, + const mpf_termination_vtable_t *vtable, + mpf_audio_stream_t *audio_stream, + mpf_video_stream_t *video_stream, + apr_pool_t *pool); + +/** + * Modify MPF termination. + * @param termination the termination to modify + * @param descriptor the termination specific descriptor + */ +MPF_DECLARE(apt_bool_t) mpf_termination_modify(mpf_termination_t *termination, void *descriptor); + +/** + * Validate MPF termination. + * @param termination the termination to validate + */ +MPF_DECLARE(apt_bool_t) mpf_termination_validate(mpf_termination_t *termination); + +/** + * Destroy MPF termination. + * @param termination the termination to destroy + */ +MPF_DECLARE(apt_bool_t) mpf_termination_destroy(mpf_termination_t *termination); + +/** + * Get associated object. + * @param termination the termination to get object from + */ +MPF_DECLARE(void*) mpf_termination_object_get(mpf_termination_t *termination); + + +/** + * Create MPF termination by termination factory. + * @param termination_factory the termination factory to create termination from + * @param obj the external object associated with termination + * @param pool the pool to allocate memory from + */ +MPF_DECLARE(mpf_termination_t*) mpf_termination_create( + mpf_termination_factory_t *termination_factory, + void *obj, + apr_pool_t *pool); + +/** + * Create raw MPF termination. + * @param obj the external object associated with termination + * @param audio_stream the audio stream of the termination + * @param video_stream the video stream of the termination + * @param pool the pool to allocate memory from + */ +MPF_DECLARE(mpf_termination_t*) mpf_raw_termination_create( + void *obj, + mpf_audio_stream_t *audio_stream, + mpf_video_stream_t *video_stream, + apr_pool_t *pool); + +APT_END_EXTERN_C + +#endif /*__MPF_TERMINATION_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_timer.h b/libs/unimrcp/libs/mpf/include/mpf_timer.h new file mode 100644 index 0000000000..a58af07f50 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_timer.h @@ -0,0 +1,44 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_TIMER_H__ +#define __MPF_TIMER_H__ + +/** + * @file mpf_timer.h + * @brief MPF High Resolution Timer + */ + +#include "mpf.h" + +APT_BEGIN_EXTERN_C + +/** Opaque MPF timer declaration */ +typedef struct mpf_timer_t mpf_timer_t; + +/** Prototype of timer callback */ +typedef void (*mpf_timer_proc_f)(mpf_timer_t *timer, void *obj); + +/** Start periodic timer */ +MPF_DECLARE(mpf_timer_t*) mpf_timer_start(unsigned long timeout, mpf_timer_proc_f timer_proc, void *obj, apr_pool_t *pool); + +/** Stop timer */ +MPF_DECLARE(void) mpf_timer_stop(mpf_timer_t *timer); + + +APT_END_EXTERN_C + +#endif /*__MPF_TIMER_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_types.h b/libs/unimrcp/libs/mpf/include/mpf_types.h new file mode 100644 index 0000000000..721fa61c36 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_types.h @@ -0,0 +1,63 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_TYPES_H__ +#define __MPF_TYPES_H__ + +/** + * @file mpf_types.h + * @brief MPF Types Declarations + */ + +#include "mpf.h" + +APT_BEGIN_EXTERN_C + +/** Opaque MPF engine declaration */ +typedef struct mpf_engine_t mpf_engine_t; + +/** Opaque codec manager declaration */ +typedef struct mpf_codec_manager_t mpf_codec_manager_t; + +/** Opaque MPF context declaration */ +typedef struct mpf_context_t mpf_context_t; + +/** Opaque MPF termination declaration */ +typedef struct mpf_termination_t mpf_termination_t; + +/** Opaque MPF termination factory declaration */ +typedef struct mpf_termination_factory_t mpf_termination_factory_t; + +/** Opaque MPF audio stream declaration */ +typedef struct mpf_audio_stream_t mpf_audio_stream_t; + +/** Opaque MPF video stream declaration */ +typedef struct mpf_video_stream_t mpf_video_stream_t; + +/** Termination vtable declaration */ +typedef struct mpf_termination_vtable_t mpf_termination_vtable_t; + +/** Table of termination virtual methods */ +struct mpf_termination_vtable_t { + /** Virtual termination destroy method */ + apt_bool_t (*destroy)(mpf_termination_t *termination); + /** Virtual termination modify method */ + apt_bool_t (*modify)(mpf_termination_t *termination, void *descriptor); +}; + +APT_END_EXTERN_C + +#endif /*__MPF_TYPES_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_user.h b/libs/unimrcp/libs/mpf/include/mpf_user.h new file mode 100644 index 0000000000..0d6ab83e92 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_user.h @@ -0,0 +1,52 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MPF_USER_H__ +#define __MPF_USER_H__ + +/** + * @file mpf_user.h + * @brief MPF User Interface + */ + +#include "mpf_types.h" + +APT_BEGIN_EXTERN_C + +/** + * Create MPF context. + * @param obj the external object associated with context + * @param max_termination_count the max number of terminations in context + * @param pool the pool to allocate memory from + */ +MPF_DECLARE(mpf_context_t*) mpf_context_create(void *obj, apr_size_t max_termination_count, apr_pool_t *pool); + +/** + * Destroy MPF context. + * @param context the context to destroy + */ +MPF_DECLARE(apt_bool_t) mpf_context_destroy(mpf_context_t *context); + +/** + * Get external object associated with MPF context. + * @param context the context to get object from + */ +MPF_DECLARE(void*) mpf_context_object_get(mpf_context_t *context); + + +APT_END_EXTERN_C + +#endif /*__MPF_USER_H__*/ diff --git a/libs/unimrcp/libs/mpf/mpf.vcproj b/libs/unimrcp/libs/mpf/mpf.vcproj new file mode 100644 index 0000000000..4dd2794d8c --- /dev/null +++ b/libs/unimrcp/libs/mpf/mpf.vcproj @@ -0,0 +1,363 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/libs/mpf/src/mpf_activity_detector.c b/libs/unimrcp/libs/mpf/src/mpf_activity_detector.c new file mode 100644 index 0000000000..1b40c84039 --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_activity_detector.c @@ -0,0 +1,165 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mpf_activity_detector.h" +#include "apt_log.h" + +/** Detector states */ +typedef enum { + DETECTOR_STATE_INACTIVITY, /**< inactivity detected */ + DETECTOR_STATE_ACTIVITY_TRANSITION, /**< activity detection is in-progress */ + DETECTOR_STATE_ACTIVITY, /**< activity detected */ + DETECTOR_STATE_INACTIVITY_TRANSITION /**< inactivity detection is in-progress */ +} mpf_detector_state_e; + +/** Activity detector */ +struct mpf_activity_detector_t { + /* voice activity (silence) level threshold */ + apr_size_t level_threshold; + + /* period of activity/inactivity required to complete/raise an event */ + apr_size_t complete_timeout; + /* noinput timeout */ + apr_size_t noinput_timeout; + + /* current state */ + apt_bool_t state; + /* duration spent in current state */ + apr_size_t duration; +}; + +/** Create activity detector */ +MPF_DECLARE(mpf_activity_detector_t*) mpf_activity_detector_create(apr_pool_t *pool) +{ + mpf_activity_detector_t *detector = apr_palloc(pool,sizeof(mpf_activity_detector_t)); + detector->level_threshold = 2; /* 0 .. 255 */ + detector->complete_timeout = 300; /* 0.3 s */ + detector->noinput_timeout = 5000; /* 5 s */ + detector->duration = 0; + detector->state = DETECTOR_STATE_INACTIVITY; + return detector; +} + +/** Set threshold of voice activity (silence) level */ +MPF_DECLARE(void) mpf_activity_detector_level_set(mpf_activity_detector_t *detector, apr_size_t level_threshold) +{ + detector->level_threshold = level_threshold; +} + +/** Set noinput timeout */ +MPF_DECLARE(void) mpf_activity_detector_noinput_timeout_set(mpf_activity_detector_t *detector, apr_size_t noinput_timeout) +{ + detector->noinput_timeout = noinput_timeout; +} + +/** Set transition complete timeout */ +MPF_DECLARE(void) mpf_activity_detector_complete_timeout_set(mpf_activity_detector_t *detector, apr_size_t complete_timeout) +{ + detector->complete_timeout = complete_timeout; +} + + +static APR_INLINE void mpf_activity_detector_state_change(mpf_activity_detector_t *detector, mpf_detector_state_e state) +{ + detector->duration = 0; + detector->state = state; +} + +static apr_size_t mpf_activity_detector_level_calculate(const mpf_frame_t *frame) +{ + apr_size_t sum = 0; + apr_size_t count = frame->codec_frame.size/2; + const apr_int16_t *cur = frame->codec_frame.buffer; + const apr_int16_t *end = cur + count; + + for(; cur < end; cur++) { + if(*cur < 0) { + sum -= *cur; + } + else { + sum += *cur; + } + } + + return sum / count; +} + +/** Process current frame */ +MPF_DECLARE(mpf_detector_event_e) mpf_activity_detector_process(mpf_activity_detector_t *detector, const mpf_frame_t *frame) +{ + mpf_detector_event_e det_event = MPF_DETECTOR_EVENT_NONE; + apr_size_t level = 0; + if((frame->type & MEDIA_FRAME_TYPE_AUDIO) == MEDIA_FRAME_TYPE_AUDIO) { + /* first, calculate current activity level of processed frame */ + level = mpf_activity_detector_level_calculate(frame); +#if 0 + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Activity Detector [%d]",level); +#endif + } + + if(detector->state == DETECTOR_STATE_INACTIVITY) { + if(level >= detector->level_threshold) { + /* start to detect activity */ + mpf_activity_detector_state_change(detector,DETECTOR_STATE_ACTIVITY_TRANSITION); + } + else { + detector->duration += CODEC_FRAME_TIME_BASE; + if(detector->duration >= detector->noinput_timeout) { + /* detected noinput */ + det_event = MPF_DETECTOR_EVENT_NOINPUT; + } + } + } + else if(detector->state == DETECTOR_STATE_ACTIVITY_TRANSITION) { + if(level >= detector->level_threshold) { + detector->duration += CODEC_FRAME_TIME_BASE; + if(detector->duration >= detector->complete_timeout) { + /* finally detected activity */ + det_event = MPF_DETECTOR_EVENT_ACTIVITY; + mpf_activity_detector_state_change(detector,DETECTOR_STATE_ACTIVITY); + } + } + else { + /* fallback to inactivity */ + mpf_activity_detector_state_change(detector,DETECTOR_STATE_INACTIVITY); + } + } + else if(detector->state == DETECTOR_STATE_ACTIVITY) { + if(level >= detector->level_threshold) { + detector->duration += CODEC_FRAME_TIME_BASE; + } + else { + /* start to detect inactivity */ + mpf_activity_detector_state_change(detector,DETECTOR_STATE_INACTIVITY_TRANSITION); + } + } + else if(detector->state == DETECTOR_STATE_INACTIVITY_TRANSITION) { + if(level >= detector->level_threshold) { + /* fallback to activity */ + mpf_activity_detector_state_change(detector,DETECTOR_STATE_ACTIVITY); + } + else { + detector->duration += CODEC_FRAME_TIME_BASE; + if(detector->duration >= detector->complete_timeout) { + /* detected inactivity */ + det_event = MPF_DETECTOR_EVENT_INACTIVITY; + mpf_activity_detector_state_change(detector,DETECTOR_STATE_INACTIVITY); + } + } + } + + return det_event; +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_audio_file_stream.c b/libs/unimrcp/libs/mpf/src/mpf_audio_file_stream.c new file mode 100644 index 0000000000..e52d156cb7 --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_audio_file_stream.c @@ -0,0 +1,168 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mpf_audio_file_stream.h" +#include "mpf_termination.h" +#include "mpf_frame.h" +#include "mpf_codec_manager.h" +#include "apt_log.h" + +/** Audio file stream */ +typedef struct mpf_audio_file_stream_t mpf_audio_file_stream_t; +struct mpf_audio_file_stream_t { + mpf_audio_stream_t *audio_stream; + + FILE *read_handle; + FILE *write_handle; + + apt_bool_t eof; + apr_size_t max_write_size; + apr_size_t cur_write_size; +}; + +static APR_INLINE void mpf_audio_file_event_raise(mpf_audio_stream_t *stream, int event_id, void *descriptor); + + +static apt_bool_t mpf_audio_file_destroy(mpf_audio_stream_t *stream) +{ + mpf_audio_file_stream_t *file_stream = stream->obj; + if(file_stream->read_handle) { + fclose(file_stream->read_handle); + file_stream->read_handle = NULL; + } + if(file_stream->write_handle) { + fclose(file_stream->write_handle); + file_stream->write_handle = NULL; + } + return TRUE; +} + +static apt_bool_t mpf_audio_file_reader_open(mpf_audio_stream_t *stream) +{ + return TRUE; +} + +static apt_bool_t mpf_audio_file_reader_close(mpf_audio_stream_t *stream) +{ + return TRUE; +} + +static apt_bool_t mpf_audio_file_frame_read(mpf_audio_stream_t *stream, mpf_frame_t *frame) +{ + mpf_audio_file_stream_t *file_stream = stream->obj; + if(file_stream->read_handle && file_stream->eof == FALSE) { + if(fread(frame->codec_frame.buffer,1,frame->codec_frame.size,file_stream->read_handle) == frame->codec_frame.size) { + frame->type = MEDIA_FRAME_TYPE_AUDIO; + } + else { + file_stream->eof = TRUE; + mpf_audio_file_event_raise(stream,0,NULL); + } + } + return TRUE; +} + + +static apt_bool_t mpf_audio_file_writer_open(mpf_audio_stream_t *stream) +{ + return TRUE; +} + +static apt_bool_t mpf_audio_file_writer_close(mpf_audio_stream_t *stream) +{ + return TRUE; +} + +static apt_bool_t mpf_audio_file_frame_write(mpf_audio_stream_t *stream, const mpf_frame_t *frame) +{ + mpf_audio_file_stream_t *file_stream = stream->obj; + if(file_stream->write_handle && + (!file_stream->max_write_size || file_stream->cur_write_size < file_stream->max_write_size)) { + file_stream->cur_write_size += fwrite( + frame->codec_frame.buffer, + 1, + frame->codec_frame.size, + file_stream->write_handle); + if(file_stream->cur_write_size >= file_stream->max_write_size) { + mpf_audio_file_event_raise(stream,0,NULL); + } + } + return TRUE; +} + +static const mpf_audio_stream_vtable_t vtable = { + mpf_audio_file_destroy, + mpf_audio_file_reader_open, + mpf_audio_file_reader_close, + mpf_audio_file_frame_read, + mpf_audio_file_writer_open, + mpf_audio_file_writer_close, + mpf_audio_file_frame_write +}; + +MPF_DECLARE(mpf_audio_stream_t*) mpf_file_stream_create(mpf_termination_t *termination, apr_pool_t *pool) +{ + mpf_audio_file_stream_t *file_stream = apr_palloc(pool,sizeof(mpf_audio_file_stream_t)); + file_stream->audio_stream = mpf_audio_stream_create(file_stream,&vtable,STREAM_MODE_NONE,pool); + file_stream->audio_stream->termination = termination; + + file_stream->write_handle = NULL; + file_stream->read_handle = NULL; + file_stream->eof = FALSE; + file_stream->max_write_size = 0; + file_stream->cur_write_size = 0; + return file_stream->audio_stream; +} + +MPF_DECLARE(apt_bool_t) mpf_file_stream_modify(mpf_audio_stream_t *stream, mpf_audio_file_descriptor_t *descriptor) +{ + mpf_audio_file_stream_t *file_stream = stream->obj; + if(descriptor->mask & FILE_READER) { + if(file_stream->read_handle) { + fclose(file_stream->read_handle); + } + file_stream->read_handle = descriptor->read_handle; + file_stream->eof = FALSE; + stream->mode |= FILE_READER; + + stream->rx_codec = mpf_codec_manager_codec_get( + stream->termination->codec_manager, + &descriptor->codec_descriptor, + stream->termination->pool); + } + if(descriptor->mask & FILE_WRITER) { + if(file_stream->write_handle) { + fclose(file_stream->write_handle); + } + file_stream->write_handle = descriptor->write_handle; + file_stream->max_write_size = descriptor->max_write_size; + file_stream->cur_write_size = 0; + stream->mode |= FILE_WRITER; + + stream->tx_codec = mpf_codec_manager_codec_get( + stream->termination->codec_manager, + &descriptor->codec_descriptor, + stream->termination->pool); + } + return TRUE; +} + +static APR_INLINE void mpf_audio_file_event_raise(mpf_audio_stream_t *stream, int event_id, void *descriptor) +{ + if(stream->termination->event_handler) { + stream->termination->event_handler(stream->termination,event_id,descriptor); + } +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_bridge.c b/libs/unimrcp/libs/mpf/src/mpf_bridge.c new file mode 100644 index 0000000000..45aa1c754c --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_bridge.c @@ -0,0 +1,112 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mpf_bridge.h" +#include "mpf_stream.h" +#include "apt_log.h" + +static apt_bool_t mpf_bridge_process(mpf_object_t *object) +{ + object->frame.type = MEDIA_FRAME_TYPE_NONE; + object->source->vtable->read_frame(object->source,&object->frame); + + if((object->frame.type & MEDIA_FRAME_TYPE_AUDIO) == 0) { + memset( object->frame.codec_frame.buffer, + 0, + object->frame.codec_frame.size); + } + + object->sink->vtable->write_frame(object->sink,&object->frame); + return TRUE; +} + +static apt_bool_t mpf_null_bridge_process(mpf_object_t *object) +{ + object->frame.type = MEDIA_FRAME_TYPE_NONE; + object->source->vtable->read_frame(object->source,&object->frame); + object->sink->vtable->write_frame(object->sink,&object->frame); + return TRUE; +} + + +static apt_bool_t mpf_bridge_destroy(mpf_object_t *object) +{ + mpf_object_t *bridge = object; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Destroy Audio Bridge"); + mpf_audio_stream_rx_close(bridge->source); + mpf_audio_stream_tx_close(bridge->sink); + return TRUE; +} + +static mpf_object_t* mpf_bridge_base_create(mpf_audio_stream_t *source, mpf_audio_stream_t *sink, apr_pool_t *pool) +{ + mpf_object_t *bridge; + if(!source || !sink) { + return NULL; + } + + bridge = apr_palloc(pool,sizeof(mpf_object_t)); + bridge->source = source; + bridge->sink = sink; + bridge->process = mpf_bridge_process; + bridge->destroy = mpf_bridge_destroy; + + if(mpf_audio_stream_rx_open(source) == FALSE) { + return NULL; + } + if(mpf_audio_stream_tx_open(sink) == FALSE) { + mpf_audio_stream_rx_close(source); + return NULL; + } + return bridge; +} + +MPF_DECLARE(mpf_object_t*) mpf_bridge_create(mpf_audio_stream_t *source, mpf_audio_stream_t *sink, apr_pool_t *pool) +{ + mpf_codec_descriptor_t *descriptor; + apr_size_t frame_size; + mpf_object_t *bridge; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Create Audio Bridge"); + bridge = mpf_bridge_base_create(source,sink,pool); + if(!bridge) { + return NULL; + } + + descriptor = source->rx_codec->descriptor; + frame_size = mpf_codec_linear_frame_size_calculate(descriptor->sampling_rate,descriptor->channel_count); + bridge->frame.codec_frame.size = frame_size; + bridge->frame.codec_frame.buffer = apr_palloc(pool,frame_size); + return bridge; +} + +MPF_DECLARE(mpf_object_t*) mpf_null_bridge_create(mpf_audio_stream_t *source, mpf_audio_stream_t *sink, apr_pool_t *pool) +{ + mpf_codec_t *codec; + apr_size_t frame_size; + mpf_object_t *bridge; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Create Audio Null Bridge"); + bridge = mpf_bridge_base_create(source,sink,pool); + if(!bridge) { + return NULL; + } + bridge->process = mpf_null_bridge_process; + + codec = source->rx_codec; + frame_size = mpf_codec_frame_size_calculate(codec->descriptor,codec->attribs); + bridge->frame.codec_frame.size = frame_size; + bridge->frame.codec_frame.buffer = apr_palloc(pool,frame_size); + return bridge; +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_buffer.c b/libs/unimrcp/libs/mpf/src/mpf_buffer.c new file mode 100644 index 0000000000..f128760277 --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_buffer.c @@ -0,0 +1,171 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WIN32 +#pragma warning(disable: 4127) +#endif +#include +#include "mpf_buffer.h" + +typedef struct mpf_chunk_t mpf_chunk_t; + +struct mpf_chunk_t { + APR_RING_ENTRY(mpf_chunk_t) link; + mpf_frame_t frame; +}; + +struct mpf_buffer_t { + APR_RING_HEAD(mpf_chunk_head_t, mpf_chunk_t) head; + mpf_chunk_t *cur_chunk; + apr_size_t remaining_chunk_size; + apr_thread_mutex_t *guard; + apr_pool_t *pool; + apr_size_t size; /* total size */ +}; + +mpf_buffer_t* mpf_buffer_create(apr_pool_t *pool) +{ + mpf_buffer_t *buffer = apr_palloc(pool,sizeof(mpf_buffer_t)); + buffer->pool = pool; + buffer->cur_chunk = NULL; + buffer->remaining_chunk_size = 0; + buffer->size = 0; + APR_RING_INIT(&buffer->head, mpf_chunk_t, link); + apr_thread_mutex_create(&buffer->guard,APR_THREAD_MUTEX_UNNESTED,pool); + return buffer; +} + +void mpf_buffer_destroy(mpf_buffer_t *buffer) +{ + if(buffer->guard) { + apr_thread_mutex_destroy(buffer->guard); + buffer->guard = NULL; + } +} + +apt_bool_t mpf_buffer_restart(mpf_buffer_t *buffer) +{ + apr_thread_mutex_lock(buffer->guard); + APR_RING_INIT(&buffer->head, mpf_chunk_t, link); + apr_thread_mutex_unlock(buffer->guard); + return TRUE; +} + +static APR_INLINE apt_bool_t mpf_buffer_chunk_write(mpf_buffer_t *buffer, mpf_chunk_t *chunk) +{ + APR_RING_INSERT_TAIL(&buffer->head,chunk,mpf_chunk_t,link); + return TRUE; +} + +static APR_INLINE mpf_chunk_t* mpf_buffer_chunk_read(mpf_buffer_t *buffer) +{ + mpf_chunk_t *chunk = NULL; + if(!APR_RING_EMPTY(&buffer->head,mpf_chunk_t,link)) { + chunk = APR_RING_FIRST(&buffer->head); + APR_RING_REMOVE(chunk,link); + } + return chunk; +} + +apt_bool_t mpf_buffer_audio_write(mpf_buffer_t *buffer, void *data, apr_size_t size) +{ + mpf_chunk_t *chunk; + apt_bool_t status = TRUE; + apr_thread_mutex_lock(buffer->guard); + + chunk = apr_palloc(buffer->pool,sizeof(mpf_chunk_t)); + chunk->frame.codec_frame.buffer = apr_palloc(buffer->pool,size); + memcpy(chunk->frame.codec_frame.buffer,data,size); + chunk->frame.codec_frame.size = size; + chunk->frame.type = MEDIA_FRAME_TYPE_AUDIO; + status = mpf_buffer_chunk_write(buffer,chunk); + + buffer->size += size; + apr_thread_mutex_unlock(buffer->guard); + return status; +} + +apt_bool_t mpf_buffer_event_write(mpf_buffer_t *buffer, mpf_frame_type_e event_type) +{ + mpf_chunk_t *chunk; + apt_bool_t status = TRUE; + apr_thread_mutex_lock(buffer->guard); + + chunk = apr_palloc(buffer->pool,sizeof(mpf_chunk_t)); + chunk->frame.codec_frame.buffer = NULL; + chunk->frame.codec_frame.size = 0; + chunk->frame.type = event_type; + status = mpf_buffer_chunk_write(buffer,chunk); + + apr_thread_mutex_unlock(buffer->guard); + return status; +} + +apt_bool_t mpf_buffer_frame_read(mpf_buffer_t *buffer, mpf_frame_t *media_frame) +{ + mpf_codec_frame_t *dest; + mpf_codec_frame_t *src; + apr_size_t remaining_frame_size = media_frame->codec_frame.size; + apr_thread_mutex_lock(buffer->guard); + do { + if(!buffer->cur_chunk) { + buffer->cur_chunk = mpf_buffer_chunk_read(buffer); + if(!buffer->cur_chunk) { + /* buffer is empty */ + break; + } + buffer->remaining_chunk_size = buffer->cur_chunk->frame.codec_frame.size; + } + + dest = &media_frame->codec_frame; + src = &buffer->cur_chunk->frame.codec_frame; + media_frame->type |= buffer->cur_chunk->frame.type; + if(remaining_frame_size < buffer->remaining_chunk_size) { + /* copy remaining_frame_size */ + memcpy( + (char*)dest->buffer + dest->size - remaining_frame_size, + (char*)src->buffer + src->size - buffer->remaining_chunk_size, + remaining_frame_size); + buffer->remaining_chunk_size -= remaining_frame_size; + buffer->size -= remaining_frame_size; + remaining_frame_size = 0; + } + else { + /* copy remaining_chunk_size and proceed to the next chunk */ + memcpy( + (char*)dest->buffer + dest->size - remaining_frame_size, + (char*)src->buffer + src->size - buffer->remaining_chunk_size, + buffer->remaining_chunk_size); + remaining_frame_size -= buffer->remaining_chunk_size; + buffer->size -= buffer->remaining_chunk_size; + buffer->remaining_chunk_size = 0; + buffer->cur_chunk = NULL; + } + } + while(remaining_frame_size); + + if(remaining_frame_size) { + apr_size_t offset = media_frame->codec_frame.size - remaining_frame_size; + memset((char*)media_frame->codec_frame.buffer + offset, 0, remaining_frame_size); + } + apr_thread_mutex_unlock(buffer->guard); + return TRUE; +} + +apr_size_t mpf_buffer_get_size(mpf_buffer_t *buffer) +{ + return buffer->size; +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_codec_descriptor.c b/libs/unimrcp/libs/mpf/src/mpf_codec_descriptor.c new file mode 100644 index 0000000000..0a811ac058 --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_codec_descriptor.c @@ -0,0 +1,73 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mpf_codec_descriptor.h" + +/** Match two codec descriptors */ +MPF_DECLARE(apt_bool_t) mpf_codec_descriptor_match(const mpf_codec_descriptor_t *descriptor1, const mpf_codec_descriptor_t *descriptor2) +{ + apt_bool_t match = FALSE; + if(descriptor1->payload_type < 96 && descriptor2->payload_type < 96) { + if(descriptor1->payload_type == descriptor2->payload_type) { + match = TRUE; + } + } + else { + if(apt_string_compare(&descriptor1->name,&descriptor2->name) == TRUE) { + if(descriptor1->sampling_rate == descriptor2->sampling_rate && + descriptor1->channel_count == descriptor2->channel_count) { + match = TRUE; + } + } + } + return match; +} + +/** Intersect two codec lists */ +MPF_DECLARE(apt_bool_t) mpf_codec_list_intersect(mpf_codec_list_t *codec_list1, mpf_codec_list_t *codec_list2) +{ + int i; + int j; + mpf_codec_descriptor_t *descriptor1; + mpf_codec_descriptor_t *descriptor2; + codec_list1->preffered = NULL; + codec_list2->preffered = NULL; + /* find only one match, set the matched codec as preffered, disable the others */ + for(i=0; idescriptor_arr->nelts; i++) { + descriptor1 = (mpf_codec_descriptor_t*)codec_list1->descriptor_arr->elts + i; + if(codec_list1->preffered) { + descriptor1->enabled = FALSE; + continue; + } + + for(j=0; jdescriptor_arr->nelts; j++) { + descriptor2 = (mpf_codec_descriptor_t*)codec_list2->descriptor_arr->elts + j; + + descriptor1->enabled = mpf_codec_descriptor_match(descriptor1,descriptor2); + if(descriptor1->enabled == TRUE) { + codec_list1->preffered = descriptor1; + codec_list2->preffered = descriptor2; + break; + } + } + } + for(j=0; jdescriptor_arr->nelts; j++) { + descriptor2 = (mpf_codec_descriptor_t*)codec_list2->descriptor_arr->elts + j; + descriptor2->enabled = (codec_list2->preffered == descriptor2) ? TRUE : FALSE; + } + + return TRUE; +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_codec_g711.c b/libs/unimrcp/libs/mpf/src/mpf_codec_g711.c new file mode 100644 index 0000000000..c2feefafa6 --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_codec_g711.c @@ -0,0 +1,162 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mpf_codec.h" +#include "g711/g711.h" + +#define G711u_CODEC_NAME "PCMU" +#define G711u_CODEC_NAME_LENGTH (sizeof(G711u_CODEC_NAME)-1) + +#define G711a_CODEC_NAME "PCMA" +#define G711a_CODEC_NAME_LENGTH (sizeof(G711a_CODEC_NAME)-1) + +static apt_bool_t g711_open(mpf_codec_t *codec) +{ + return TRUE; +} + +static apt_bool_t g711_close(mpf_codec_t *codec) +{ + return TRUE; +} + +static apt_bool_t g711u_encode(mpf_codec_t *codec, const mpf_codec_frame_t *frame_in, mpf_codec_frame_t *frame_out) +{ + const short *decode_buf; + unsigned char *encode_buf; + apr_uint32_t i; + + decode_buf = frame_in->buffer; + encode_buf = frame_out->buffer; + + frame_out->size = frame_in->size / sizeof(short); + + for(i=0; isize; i++) { + encode_buf[i] = linear_to_ulaw(decode_buf[i]); + } + + return TRUE; +} + +static apt_bool_t g711u_decode(mpf_codec_t *codec, const mpf_codec_frame_t *frame_in, mpf_codec_frame_t *frame_out) +{ + short *decode_buf; + const unsigned char *encode_buf; + apr_uint32_t i; + + decode_buf = frame_out->buffer; + encode_buf = frame_in->buffer; + + frame_out->size = frame_in->size * sizeof(short); + + for(i=0; isize; i++) { + decode_buf[i] = ulaw_to_linear(encode_buf[i]); + } + + return TRUE; +} + +static apt_bool_t g711a_encode(mpf_codec_t *codec, const mpf_codec_frame_t *frame_in, mpf_codec_frame_t *frame_out) +{ + const short *decode_buf; + unsigned char *encode_buf; + apr_uint32_t i; + + decode_buf = frame_in->buffer; + encode_buf = frame_out->buffer; + + frame_out->size = frame_in->size / sizeof(short); + + for(i=0; isize; i++) { + encode_buf[i] = linear_to_alaw(decode_buf[i]); + } + + return TRUE; +} + +static apt_bool_t g711a_decode(mpf_codec_t *codec, const mpf_codec_frame_t *frame_in, mpf_codec_frame_t *frame_out) +{ + short *decode_buf; + const unsigned char *encode_buf; + apr_uint32_t i; + + decode_buf = frame_out->buffer; + encode_buf = frame_in->buffer; + + frame_out->size = frame_in->size * sizeof(short); + + for(i=0; isize; i++) { + decode_buf[i] = alaw_to_linear(encode_buf[i]); + } + + return TRUE; +} + +static const mpf_codec_vtable_t g711u_vtable = { + g711_open, + g711_close, + g711u_encode, + g711u_decode, + NULL +}; + +static const mpf_codec_vtable_t g711a_vtable = { + g711_open, + g711_close, + g711a_encode, + g711a_decode, + NULL +}; + +static const mpf_codec_descriptor_t g711u_descriptor = { + 0, + {G711u_CODEC_NAME, G711u_CODEC_NAME_LENGTH}, + 8000, + 1, + NULL, + TRUE +}; + +static const mpf_codec_descriptor_t g711a_descriptor = { + 8, + {G711a_CODEC_NAME, G711a_CODEC_NAME_LENGTH}, + 8000, + 1, + NULL, + TRUE +}; + +static const mpf_codec_attribs_t g711u_attribs = { + {G711u_CODEC_NAME, G711u_CODEC_NAME_LENGTH}, /* codec name */ + 8, /* bits per sample */ + MPF_SAMPLE_RATE_8000 | MPF_SAMPLE_RATE_16000 /* sampling rates */ +}; + +static const mpf_codec_attribs_t g711a_attribs = { + {G711a_CODEC_NAME, G711a_CODEC_NAME_LENGTH}, /* codec name */ + 8, /* bits per sample */ + MPF_SAMPLE_RATE_8000 | MPF_SAMPLE_RATE_16000 /* sampling rates */ +}; + +mpf_codec_t* mpf_codec_g711u_create(apr_pool_t *pool) +{ + return mpf_codec_create(&g711u_vtable,&g711u_attribs,&g711u_descriptor,pool); +} + +mpf_codec_t* mpf_codec_g711a_create(apr_pool_t *pool) +{ + return mpf_codec_create(&g711a_vtable,&g711a_attribs,&g711a_descriptor,pool); +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_codec_linear.c b/libs/unimrcp/libs/mpf/src/mpf_codec_linear.c new file mode 100644 index 0000000000..f2aa0a703b --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_codec_linear.c @@ -0,0 +1,49 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mpf_codec.h" + +#define L16_CODEC_NAME "L16" +#define L16_CODEC_NAME_LENGTH (sizeof(L16_CODEC_NAME)-1) + +static const mpf_codec_vtable_t l16_vtable = { + NULL +}; + +static const mpf_codec_descriptor_t l16_descriptor = { + 96, /* not specified */ + {L16_CODEC_NAME, L16_CODEC_NAME_LENGTH}, + 8000, + 1, + NULL, + TRUE +}; + +static const mpf_codec_attribs_t l16_attribs = { + {L16_CODEC_NAME, L16_CODEC_NAME_LENGTH}, /* codec name */ + 16, /* bits per sample */ + MPF_SAMPLE_RATE_8000 | MPF_SAMPLE_RATE_16000 /* sampling rates */ +}; + +mpf_codec_t* mpf_codec_l16_create(apr_pool_t *pool) +{ + return mpf_codec_create(&l16_vtable,&l16_attribs,NULL,pool); +} + +const mpf_codec_descriptor_t* l16_descriptor_get() +{ + return &l16_descriptor; +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_codec_manager.c b/libs/unimrcp/libs/mpf/src/mpf_codec_manager.c new file mode 100644 index 0000000000..3492cb23d7 --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_codec_manager.c @@ -0,0 +1,199 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include "mpf_codec_manager.h" +#include "apt_log.h" + + +struct mpf_codec_manager_t { + /** Memory pool */ + apr_pool_t *pool; + + /** Dynamic array of codecs (mpf_codec_t*) */ + apr_array_header_t *codec_arr; +}; + + +MPF_DECLARE(mpf_codec_manager_t*) mpf_codec_manager_create(apr_size_t codec_count, apr_pool_t *pool) +{ + mpf_codec_manager_t *codec_manager = apr_palloc(pool,sizeof(mpf_codec_manager_t)); + codec_manager->pool = pool; + codec_manager->codec_arr = apr_array_make(pool,(int)codec_count,sizeof(mpf_codec_t*)); + return codec_manager; +} + +MPF_DECLARE(void) mpf_codec_manager_destroy(mpf_codec_manager_t *codec_manager) +{ + /* nothing to do */ +} + +MPF_DECLARE(apt_bool_t) mpf_codec_manager_codec_register(mpf_codec_manager_t *codec_manager, mpf_codec_t *codec) +{ + mpf_codec_t **slot; + if(!codec || !codec->attribs || !codec->attribs->name.buf) { + return FALSE; + } + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Register Codec [%s]",codec->attribs->name.buf); + + slot = apr_array_push(codec_manager->codec_arr); + *slot = codec; + return TRUE; +} + +MPF_DECLARE(mpf_codec_t*) mpf_codec_manager_codec_get(const mpf_codec_manager_t *codec_manager, mpf_codec_descriptor_t *descriptor, apr_pool_t *pool) +{ + int i; + mpf_codec_t *codec = NULL; + mpf_codec_t *ret_codec = NULL; + if(!descriptor) { + return NULL; + } + + for(i=0; icodec_arr->nelts; i++) { + codec = ((mpf_codec_t**)codec_manager->codec_arr->elts)[i]; + if(descriptor->payload_type < 96) { + if(codec->static_descriptor && codec->static_descriptor->payload_type == descriptor->payload_type) { + descriptor->name = codec->static_descriptor->name; + descriptor->sampling_rate = codec->static_descriptor->sampling_rate; + descriptor->channel_count = codec->static_descriptor->channel_count; + break; + } + } + else { + if(apt_string_compare(&codec->attribs->name,&descriptor->name) == TRUE) { + /* sampling rate must be checked as well */ + break; + } + } + } + + if(i == codec_manager->codec_arr->nelts) { + /* no match found */ + return NULL; + } + if(codec) { + ret_codec = mpf_codec_clone(codec,pool); + ret_codec->descriptor = descriptor; + } + return ret_codec; +} + +MPF_DECLARE(apt_bool_t) mpf_codec_manager_codec_list_get(const mpf_codec_manager_t *codec_manager, mpf_codec_list_t *codec_list, apr_pool_t *pool) +{ + const mpf_codec_descriptor_t *static_descriptor; + mpf_codec_descriptor_t *descriptor; + int i; + mpf_codec_t *codec; + + mpf_codec_list_init(codec_list,codec_manager->codec_arr->nelts,pool); + for(i=0; icodec_arr->nelts; i++) { + codec = ((mpf_codec_t**)codec_manager->codec_arr->elts)[i]; + static_descriptor = codec->static_descriptor; + if(static_descriptor) { + descriptor = mpf_codec_list_add(codec_list); + if(descriptor) { + *descriptor = *static_descriptor; + } + } + } + return TRUE; +} + +static apt_bool_t mpf_codec_manager_codec_parse(const mpf_codec_manager_t *codec_manager, mpf_codec_list_t *codec_list, char *codec_desc_str, apr_pool_t *pool) +{ + const mpf_codec_t *codec; + mpf_codec_descriptor_t *descriptor; + const char *separator = "/"; + char *state; + /* parse codec name */ + char *str = apr_strtok(codec_desc_str, separator, &state); + codec_desc_str = NULL; /* make sure we pass NULL on subsequent calls of apr_strtok() */ + if(str) { + apt_str_t name; + apt_string_assign(&name,str,pool); + /* find codec by name */ + codec = mpf_codec_manager_codec_find(codec_manager,&name); + if(!codec) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No Such Codec [%s]",str); + return FALSE; + } + + descriptor = mpf_codec_list_add(codec_list); + descriptor->name = name; + + /* set defualt attributes */ + if(codec->static_descriptor) { + descriptor->payload_type = codec->static_descriptor->payload_type; + descriptor->sampling_rate = codec->static_descriptor->sampling_rate; + descriptor->channel_count = codec->static_descriptor->channel_count; + } + else { + descriptor->payload_type = 96; + descriptor->sampling_rate = 8000; + descriptor->channel_count = 1; + } + + /* parse optional payload type */ + str = apr_strtok(codec_desc_str, separator, &state); + if(str) { + descriptor->payload_type = (apr_byte_t)atol(str); + + /* parse optional sampling rate */ + str = apr_strtok(codec_desc_str, separator, &state); + if(str) { + descriptor->sampling_rate = (apr_uint16_t)atol(str); + + /* parse optional channel count */ + str = apr_strtok(codec_desc_str, separator, &state); + if(str) { + descriptor->channel_count = (apr_byte_t)atol(str); + } + } + } + } + return TRUE; +} + +MPF_DECLARE(apt_bool_t) mpf_codec_manager_codec_list_load(const mpf_codec_manager_t *codec_manager, mpf_codec_list_t *codec_list, const char *str, apr_pool_t *pool) +{ + char *codec_desc_str; + char *state; + char *codec_list_str = apr_pstrdup(pool,str); + do { + codec_desc_str = apr_strtok(codec_list_str, " ", &state); + if(codec_desc_str) { + mpf_codec_manager_codec_parse(codec_manager,codec_list,codec_desc_str,pool); + } + codec_list_str = NULL; /* make sure we pass NULL on subsequent calls of apr_strtok() */ + } + while(codec_desc_str); + return TRUE; +} + +MPF_DECLARE(const mpf_codec_t*) mpf_codec_manager_codec_find(const mpf_codec_manager_t *codec_manager, const apt_str_t *codec_name) +{ + int i; + mpf_codec_t *codec; + for(i=0; icodec_arr->nelts; i++) { + codec = ((mpf_codec_t**)codec_manager->codec_arr->elts)[i]; + if(apt_string_compare(&codec->attribs->name,codec_name) == TRUE) { + return codec; + } + } + return NULL; +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_context.c b/libs/unimrcp/libs/mpf/src/mpf_context.c new file mode 100644 index 0000000000..2f261484a5 --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_context.c @@ -0,0 +1,226 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mpf_context.h" +#include "mpf_termination.h" +#include "mpf_stream.h" +#include "mpf_encoder.h" +#include "mpf_decoder.h" +#include "mpf_bridge.h" +#include "apt_log.h" + +static mpf_object_t* mpf_context_connection_create(mpf_context_t *context, mpf_termination_t *src_termination, mpf_termination_t *sink_termination); + +MPF_DECLARE(mpf_context_t*) mpf_context_create(void *obj, apr_size_t max_termination_count, apr_pool_t *pool) +{ + apr_size_t i,j; + mpf_context_t *context = apr_palloc(pool,sizeof(mpf_context_t)); + context->obj = obj; + context->pool = pool; + context->elem = NULL; + context->max_termination_count = max_termination_count; + context->termination_count = 0; + context->table = apr_palloc(pool,sizeof(table_item_t)*max_termination_count); + for(i=0; itable[i] = apr_palloc(pool,sizeof(table_item_t)*max_termination_count); + for(j=0; jtable[i][j] = NULL; + } + } + + return context; +} + +MPF_DECLARE(apt_bool_t) mpf_context_destroy(mpf_context_t *context) +{ + apr_size_t i; + apr_size_t count = context->max_termination_count; + mpf_termination_t *termination; + for(i=0; itable[i][i]; + if(termination) { + mpf_context_termination_subtract(context,termination); + if(termination->audio_stream) { + mpf_audio_stream_destroy(termination->audio_stream); + } + } + } + return TRUE; +} + +MPF_DECLARE(void*) mpf_context_object_get(mpf_context_t *context) +{ + return context->obj; +} + +MPF_DECLARE(apt_bool_t) mpf_context_termination_add(mpf_context_t *context, mpf_termination_t *termination) +{ + apr_size_t i; + apr_size_t count = context->max_termination_count; + for(i=0; itable[i][i]) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Add Termination"); + context->table[i][i] = termination; + termination->slot = i; + context->termination_count++; + return TRUE; + } + } + return FALSE; +} + +MPF_DECLARE(apt_bool_t) mpf_context_termination_subtract(mpf_context_t *context, mpf_termination_t *termination) +{ + apr_size_t i = termination->slot; + if(i >= context->max_termination_count) { + return FALSE; + } + if(context->table[i][i] != termination) { + return FALSE; + } + + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Subtract Termination"); + context->table[i][i] = NULL; + termination->slot = (apr_size_t)-1; + context->termination_count--; + return TRUE; +} + +MPF_DECLARE(apt_bool_t) mpf_context_process(mpf_context_t *context) +{ + mpf_object_t *object; + apr_size_t i,j; + for(i=0; imax_termination_count; i++) { + for(j=0; jmax_termination_count; j++) { + if(i==j) continue; + + object = context->table[i][j]; + if(object && object->process) { + object->process(object); + } + } + } + return TRUE; +} + +MPF_DECLARE(apt_bool_t) mpf_context_topology_apply(mpf_context_t *context, mpf_termination_t *termination) +{ + apr_size_t i,j; + mpf_object_t *object; + mpf_termination_t *sink_termination; + mpf_termination_t *source_termination; + if(context->termination_count <= 1) { + /* at least 2 terminations are required to apply topology on them */ + return TRUE; + } + + i = termination->slot; + for(j=0; jmax_termination_count; j++) { + if(i == j) continue; + + sink_termination = context->table[j][j]; + object = mpf_context_connection_create(context,termination,sink_termination); + if(object) { + context->table[i][j] = object; + } + } + + j = termination->slot; + for(i=0; imax_termination_count; i++) { + if(i == j) continue; + + source_termination = context->table[i][i]; + object = mpf_context_connection_create(context,source_termination,termination); + if(object) { + context->table[i][j] = object; + } + } + + return TRUE; +} + +MPF_DECLARE(apt_bool_t) mpf_context_topology_destroy(mpf_context_t *context, mpf_termination_t *termination) +{ + apr_size_t i,j; + mpf_object_t *object; + if(context->termination_count <= 1) { + /* at least 2 terminations are required to destroy topology */ + return TRUE; + } + + i = termination->slot; + for(j=0; jmax_termination_count; j++) { + if(i == j) continue; + + object = context->table[i][j]; + if(object) { + if(object->destroy) { + object->destroy(object); + } + context->table[i][j] = NULL; + } + } + + j = termination->slot; + for(i=0; imax_termination_count; i++) { + if(i == j) continue; + + object = context->table[i][j]; + if(object) { + if(object->destroy) { + object->destroy(object); + } + context->table[i][j] = NULL; + } + } + return TRUE; +} + +static mpf_object_t* mpf_context_connection_create(mpf_context_t *context, mpf_termination_t *src_termination, mpf_termination_t *sink_termination) +{ + mpf_object_t *object = NULL; + mpf_audio_stream_t *source; + mpf_audio_stream_t *sink; + if(!src_termination || !sink_termination) { + return NULL; + } + source = src_termination->audio_stream; + sink = sink_termination->audio_stream; + if(source && (source->mode & STREAM_MODE_RECEIVE) == STREAM_MODE_RECEIVE && + sink && (sink->mode & STREAM_MODE_SEND) == STREAM_MODE_SEND) { + mpf_codec_t *rx_codec = source->rx_codec; + mpf_codec_t *tx_codec = sink->tx_codec; + if(rx_codec && tx_codec) { + if(mpf_codec_descriptor_match(rx_codec->descriptor,tx_codec->descriptor) == TRUE) { + object = mpf_null_bridge_create(source,sink,context->pool); + } + else { + if(rx_codec->attribs->bits_per_samples != BITS_PER_SAMPLE) { + /* set decoder before bridge */ + mpf_audio_stream_t *decoder = mpf_decoder_create(source,context->pool); + source = decoder; + } + if(tx_codec->attribs->bits_per_samples != BITS_PER_SAMPLE) { + /* set encoder after bridge */ + mpf_audio_stream_t *encoder = mpf_encoder_create(sink,context->pool); + sink = encoder; + } + object = mpf_bridge_create(source,sink,context->pool); + } + } + } + return object; +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_decoder.c b/libs/unimrcp/libs/mpf/src/mpf_decoder.c new file mode 100644 index 0000000000..2add5e43d0 --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_decoder.c @@ -0,0 +1,93 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mpf_decoder.h" +#include "apt_log.h" + +typedef struct mpf_decoder_t mpf_decoder_t; + +struct mpf_decoder_t { + mpf_audio_stream_t *base; + mpf_audio_stream_t *source; + mpf_frame_t frame_in; +}; + + +static apt_bool_t mpf_decoder_destroy(mpf_audio_stream_t *stream) +{ + mpf_decoder_t *decoder = stream->obj; + return mpf_audio_stream_destroy(decoder->source); +} + +static apt_bool_t mpf_decoder_open(mpf_audio_stream_t *stream) +{ + mpf_decoder_t *decoder = stream->obj; + return mpf_audio_stream_rx_open(decoder->source); +} + +static apt_bool_t mpf_decoder_close(mpf_audio_stream_t *stream) +{ + mpf_decoder_t *decoder = stream->obj; + return mpf_audio_stream_rx_close(decoder->source); +} + +static apt_bool_t mpf_decoder_process(mpf_audio_stream_t *stream, mpf_frame_t *frame) +{ + mpf_decoder_t *decoder = stream->obj; + if(mpf_audio_stream_frame_read(decoder->source,&decoder->frame_in) != TRUE) { + return FALSE; + } + + frame->type = decoder->frame_in.type; + if((frame->type & MEDIA_FRAME_TYPE_EVENT) == MEDIA_FRAME_TYPE_EVENT) { + frame->event_frame = decoder->frame_in.event_frame; + } + if((frame->type & MEDIA_FRAME_TYPE_AUDIO) == MEDIA_FRAME_TYPE_AUDIO) { + mpf_codec_decode(decoder->source->rx_codec,&decoder->frame_in.codec_frame,&frame->codec_frame); + } + return TRUE; +} + + +static const mpf_audio_stream_vtable_t vtable = { + mpf_decoder_destroy, + mpf_decoder_open, + mpf_decoder_close, + mpf_decoder_process, + NULL, + NULL, + NULL +}; + +MPF_DECLARE(mpf_audio_stream_t*) mpf_decoder_create(mpf_audio_stream_t *source, apr_pool_t *pool) +{ + apr_size_t frame_size; + mpf_codec_t *codec; + mpf_decoder_t *decoder; + if(!source || !source->rx_codec) { + return NULL; + } + decoder = apr_palloc(pool,sizeof(mpf_decoder_t)); + decoder->base = mpf_audio_stream_create(decoder,&vtable,STREAM_MODE_RECEIVE,pool); + decoder->source = source; + + codec = source->rx_codec; + frame_size = mpf_codec_frame_size_calculate(codec->descriptor,codec->attribs); + decoder->base->rx_codec = codec; + decoder->frame_in.codec_frame.size = frame_size; + decoder->frame_in.codec_frame.buffer = apr_palloc(pool,frame_size); + return decoder->base; +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_encoder.c b/libs/unimrcp/libs/mpf/src/mpf_encoder.c new file mode 100644 index 0000000000..0c85ac9dc7 --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_encoder.c @@ -0,0 +1,90 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mpf_encoder.h" +#include "apt_log.h" + +typedef struct mpf_encoder_t mpf_encoder_t; + +struct mpf_encoder_t { + mpf_audio_stream_t *base; + mpf_audio_stream_t *sink; + mpf_frame_t frame_out; +}; + + +static apt_bool_t mpf_encoder_destroy(mpf_audio_stream_t *stream) +{ + mpf_encoder_t *encoder = stream->obj; + return mpf_audio_stream_destroy(encoder->sink); +} + +static apt_bool_t mpf_encoder_open(mpf_audio_stream_t *stream) +{ + mpf_encoder_t *encoder = stream->obj; + return mpf_audio_stream_tx_open(encoder->sink); +} + +static apt_bool_t mpf_encoder_close(mpf_audio_stream_t *stream) +{ + mpf_encoder_t *encoder = stream->obj; + return mpf_audio_stream_tx_close(encoder->sink); +} + +static apt_bool_t mpf_encoder_process(mpf_audio_stream_t *stream, const mpf_frame_t *frame) +{ + mpf_encoder_t *encoder = stream->obj; + + encoder->frame_out.type = frame->type; + if((frame->type & MEDIA_FRAME_TYPE_EVENT) == MEDIA_FRAME_TYPE_EVENT) { + encoder->frame_out.event_frame = frame->event_frame; + } + if((frame->type & MEDIA_FRAME_TYPE_AUDIO) == MEDIA_FRAME_TYPE_AUDIO) { + mpf_codec_encode(encoder->sink->tx_codec,&frame->codec_frame,&encoder->frame_out.codec_frame); + } + return mpf_audio_stream_frame_write(encoder->sink,&encoder->frame_out); +} + + +static const mpf_audio_stream_vtable_t vtable = { + mpf_encoder_destroy, + NULL, + NULL, + NULL, + mpf_encoder_open, + mpf_encoder_close, + mpf_encoder_process +}; + +MPF_DECLARE(mpf_audio_stream_t*) mpf_encoder_create(mpf_audio_stream_t *sink, apr_pool_t *pool) +{ + apr_size_t frame_size; + mpf_codec_t *codec; + mpf_encoder_t *encoder; + if(!sink || !sink->tx_codec) { + return NULL; + } + encoder = apr_palloc(pool,sizeof(mpf_encoder_t)); + encoder->base = mpf_audio_stream_create(encoder,&vtable,STREAM_MODE_SEND,pool); + encoder->sink = sink; + + codec = sink->tx_codec; + frame_size = mpf_codec_frame_size_calculate(codec->descriptor,codec->attribs); + encoder->base->tx_codec = codec; + encoder->frame_out.codec_frame.size = frame_size; + encoder->frame_out.codec_frame.buffer = apr_palloc(pool,frame_size); + return encoder->base; +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_engine.c b/libs/unimrcp/libs/mpf/src/mpf_engine.c new file mode 100644 index 0000000000..2c6dca93a7 --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_engine.c @@ -0,0 +1,306 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mpf_engine.h" +#include "mpf_user.h" +#include "mpf_context.h" +#include "mpf_termination.h" +#include "mpf_stream.h" +#include "mpf_timer.h" +#include "mpf_codec_descriptor.h" +#include "mpf_codec_manager.h" +#include "apt_obj_list.h" +#include "apt_cyclic_queue.h" +#include "apt_log.h" + +#define MPF_TASK_NAME "Media Processing Engine" + +struct mpf_engine_t { + apr_pool_t *pool; + apt_task_t *task; + apt_task_msg_type_e task_msg_type; + apr_thread_mutex_t *request_queue_guard; + apt_cyclic_queue_t *request_queue; + apt_obj_list_t *contexts; + mpf_timer_t *timer; + const mpf_codec_manager_t *codec_manager; +}; + +static void mpf_engine_main(mpf_timer_t *timer, void *data); +static apt_bool_t mpf_engine_destroy(apt_task_t *task); +static apt_bool_t mpf_engine_start(apt_task_t *task); +static apt_bool_t mpf_engine_terminate(apt_task_t *task); +static apt_bool_t mpf_engine_msg_signal(apt_task_t *task, apt_task_msg_t *msg); +static apt_bool_t mpf_engine_msg_process(apt_task_t *task, apt_task_msg_t *msg); + +static apt_bool_t mpf_engine_contexts_destroy(mpf_engine_t *engine); + +mpf_codec_t* mpf_codec_l16_create(apr_pool_t *pool); +mpf_codec_t* mpf_codec_g711u_create(apr_pool_t *pool); +mpf_codec_t* mpf_codec_g711a_create(apr_pool_t *pool); + +MPF_DECLARE(mpf_engine_t*) mpf_engine_create(apr_pool_t *pool) +{ + apt_task_vtable_t *vtable; + apt_task_msg_pool_t *msg_pool; + mpf_engine_t *engine = apr_palloc(pool,sizeof(mpf_engine_t)); + engine->pool = pool; + engine->request_queue = NULL; + engine->contexts = NULL; + engine->codec_manager = NULL; + + msg_pool = apt_task_msg_pool_create_dynamic(sizeof(mpf_message_t),pool); + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create "MPF_TASK_NAME); + engine->task = apt_task_create(engine,msg_pool,pool); + if(!engine->task) { + return NULL; + } + + apt_task_name_set(engine->task,MPF_TASK_NAME); + + vtable = apt_task_vtable_get(engine->task); + if(vtable) { + vtable->destroy = mpf_engine_destroy; + vtable->start = mpf_engine_start; + vtable->terminate = mpf_engine_terminate; + vtable->signal_msg = mpf_engine_msg_signal; + vtable->process_msg = mpf_engine_msg_process; + } + + engine->task_msg_type = TASK_MSG_USER; + + engine->request_queue = apt_cyclic_queue_create(CYCLIC_QUEUE_DEFAULT_SIZE); + apr_thread_mutex_create(&engine->request_queue_guard,APR_THREAD_MUTEX_UNNESTED,engine->pool); + + engine->contexts = apt_list_create(engine->pool); + + return engine; +} + +MPF_DECLARE(apt_task_t*) mpf_task_get(mpf_engine_t *engine) +{ + return engine->task; +} + +MPF_DECLARE(void) mpf_engine_task_msg_type_set(mpf_engine_t *engine, apt_task_msg_type_e type) +{ + engine->task_msg_type = type; +} + +static apt_bool_t mpf_engine_destroy(apt_task_t *task) +{ + mpf_engine_t *engine = apt_task_object_get(task); + + apt_list_destroy(engine->contexts); + + apt_cyclic_queue_destroy(engine->request_queue); + apr_thread_mutex_destroy(engine->request_queue_guard); + return TRUE; +} + +static apt_bool_t mpf_engine_start(apt_task_t *task) +{ + mpf_engine_t *engine = apt_task_object_get(task); + + engine->timer = mpf_timer_start(CODEC_FRAME_TIME_BASE,mpf_engine_main,engine,engine->pool); + apt_task_child_start(task); + return TRUE; +} + +static apt_bool_t mpf_engine_terminate(apt_task_t *task) +{ + mpf_engine_t *engine = apt_task_object_get(task); + + mpf_timer_stop(engine->timer); + mpf_engine_contexts_destroy(engine); + return TRUE; +} + +static apt_bool_t mpf_engine_contexts_destroy(mpf_engine_t *engine) +{ + mpf_context_t *context; + context = apt_list_pop_front(engine->contexts); + while(context) { + mpf_context_destroy(context); + + context = apt_list_pop_front(engine->contexts); + } + return TRUE; +} + +static apt_bool_t mpf_engine_event_raise(mpf_termination_t *termination, int event_id, void *descriptor) +{ + apt_task_msg_t *task_msg; + mpf_message_t *event_msg; + mpf_engine_t *engine; + engine = termination->event_handler_obj; + if(!engine) { + return FALSE; + } + + task_msg = apt_task_msg_get(engine->task); + task_msg->type = engine->task_msg_type; + event_msg = (mpf_message_t*) task_msg->data; + event_msg->command_id = event_id; + event_msg->message_type = MPF_MESSAGE_TYPE_EVENT; + event_msg->status_code = MPF_STATUS_CODE_SUCCESS; + event_msg->context = NULL; + event_msg->termination = termination; + event_msg->descriptor = descriptor; + + return apt_task_msg_parent_signal(engine->task,task_msg); +} + +static apt_bool_t mpf_engine_msg_signal(apt_task_t *task, apt_task_msg_t *msg) +{ + mpf_engine_t *engine = apt_task_object_get(task); + + apr_thread_mutex_lock(engine->request_queue_guard); + if(apt_cyclic_queue_push(engine->request_queue,msg) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_ERROR,"MPF Request Queue is Full"); + } + apr_thread_mutex_unlock(engine->request_queue_guard); + return TRUE; +} + +static apt_bool_t mpf_engine_msg_process(apt_task_t *task, apt_task_msg_t *msg) +{ + mpf_engine_t *engine = apt_task_object_get(task); + apt_task_msg_t *response_msg; + mpf_message_t *response; + mpf_context_t *context; + mpf_termination_t *termination; + const mpf_message_t *request = (const mpf_message_t*) msg->data; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Process MPF Message"); + if(request->message_type != MPF_MESSAGE_TYPE_REQUEST) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Invalid MPF Message Type [%d]",request->message_type); + return FALSE; + } + + response_msg = apt_task_msg_get(engine->task); + response_msg->type = engine->task_msg_type; + response = (mpf_message_t*) response_msg->data; + *response = *request; + response->message_type = MPF_MESSAGE_TYPE_RESPONSE; + response->status_code = MPF_STATUS_CODE_SUCCESS; + context = request->context; + termination = request->termination; + switch(request->command_id) { + case MPF_COMMAND_ADD: + { + termination->event_handler_obj = engine; + termination->event_handler = mpf_engine_event_raise; + termination->codec_manager = engine->codec_manager; + if(request->descriptor) { + mpf_termination_modify(termination,request->descriptor); + } + mpf_termination_validate(termination); + if(mpf_context_termination_add(context,termination) == FALSE) { + response->status_code = MPF_STATUS_CODE_FAILURE; + break; + } + mpf_context_topology_apply(context,termination); + if(context->termination_count == 1) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Add Context"); + context->elem = apt_list_push_back(engine->contexts,context,context->pool); + } + break; + } + case MPF_COMMAND_SUBTRACT: + { + mpf_context_topology_destroy(context,termination); + if(mpf_context_termination_subtract(context,termination) == FALSE) { + response->status_code = MPF_STATUS_CODE_FAILURE; + break; + } + if(context->termination_count == 0) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Remove Context"); + apt_list_elem_remove(engine->contexts,context->elem); + context->elem = NULL; + } + break; + } + case MPF_COMMAND_MODIFY: + { + if(request->descriptor) { + mpf_context_topology_destroy(context,termination); + mpf_termination_modify(termination,request->descriptor); + mpf_termination_validate(termination); + mpf_context_topology_apply(context,termination); + } + break; + } + default: + { + response->status_code = MPF_STATUS_CODE_FAILURE; + } + } + + return apt_task_msg_parent_signal(engine->task,response_msg); +} + +static void mpf_engine_main(mpf_timer_t *timer, void *data) +{ + mpf_engine_t *engine = data; + apt_task_msg_t *msg; + apt_list_elem_t *elem; + mpf_context_t *context; + + /* process request queue */ + apr_thread_mutex_lock(engine->request_queue_guard); + msg = apt_cyclic_queue_pop(engine->request_queue); + while(msg) { + apr_thread_mutex_unlock(engine->request_queue_guard); + apt_task_msg_process(engine->task,msg); + apr_thread_mutex_lock(engine->request_queue_guard); + msg = apt_cyclic_queue_pop(engine->request_queue); + } + apr_thread_mutex_unlock(engine->request_queue_guard); + + /* process contexts */ + elem = apt_list_first_elem_get(engine->contexts); + while(elem) { + context = apt_list_elem_object_get(elem); + if(context) { + mpf_context_process(context); + } + elem = apt_list_next_elem_get(engine->contexts,elem); + } +} + +MPF_DECLARE(mpf_codec_manager_t*) mpf_engine_codec_manager_create(apr_pool_t *pool) +{ + mpf_codec_manager_t *codec_manager = mpf_codec_manager_create(3,pool); + if(codec_manager) { + mpf_codec_t *codec; + codec = mpf_codec_g711u_create(pool); + mpf_codec_manager_codec_register(codec_manager,codec); + + codec = mpf_codec_g711a_create(pool); + mpf_codec_manager_codec_register(codec_manager,codec); + + codec = mpf_codec_l16_create(pool); + mpf_codec_manager_codec_register(codec_manager,codec); + } + return codec_manager; +} + +MPF_DECLARE(apt_bool_t) mpf_engine_codec_manager_register(mpf_engine_t *engine, const mpf_codec_manager_t *codec_manager) +{ + engine->codec_manager = codec_manager; + return TRUE; +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_file_termination_factory.c b/libs/unimrcp/libs/mpf/src/mpf_file_termination_factory.c new file mode 100644 index 0000000000..4bab083ffa --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_file_termination_factory.c @@ -0,0 +1,58 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mpf_termination.h" +#include "mpf_file_termination_factory.h" +#include "mpf_audio_file_stream.h" + +static apt_bool_t mpf_file_termination_destroy(mpf_termination_t *termination) +{ + return TRUE; +} + +static apt_bool_t mpf_file_termination_modify(mpf_termination_t *termination, void *descriptor) +{ + mpf_audio_stream_t *audio_stream = termination->audio_stream; + if(!audio_stream) { + audio_stream = mpf_file_stream_create(termination,termination->pool); + if(!audio_stream) { + return FALSE; + } + termination->audio_stream = audio_stream; + } + + return mpf_file_stream_modify(audio_stream,descriptor); +} + +static const mpf_termination_vtable_t file_vtable = { + mpf_file_termination_destroy, + mpf_file_termination_modify, +}; + +static mpf_termination_t* mpf_file_termination_create( + mpf_termination_factory_t *termination_factory, + void *obj, + apr_pool_t *pool) +{ + return mpf_termination_base_create(termination_factory,obj,&file_vtable,NULL,NULL,pool); +} + +MPF_DECLARE(mpf_termination_factory_t*) mpf_file_termination_factory_create(apr_pool_t *pool) +{ + mpf_termination_factory_t *file_termination_factory = apr_palloc(pool,sizeof(mpf_termination_factory_t)); + file_termination_factory->create_termination = mpf_file_termination_create; + return file_termination_factory; +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_jitter_buffer.c b/libs/unimrcp/libs/mpf/src/mpf_jitter_buffer.c new file mode 100644 index 0000000000..55a90bfc60 --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_jitter_buffer.c @@ -0,0 +1,197 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mpf_jitter_buffer.h" + +struct mpf_jitter_buffer_t { + mpf_jb_config_t *config; + + apr_byte_t *raw_data; + mpf_frame_t *frames; + apr_size_t frame_count; + apr_size_t frame_ts; + apr_size_t frame_size; + + apr_size_t playout_delay_ts; + + apr_byte_t write_sync; + int write_ts_offset; + + apr_size_t write_ts; + apr_size_t read_ts; + + apr_pool_t *pool; +}; + + +mpf_jitter_buffer_t* mpf_jitter_buffer_create(mpf_jb_config_t *jb_config, mpf_codec_t *codec, apr_pool_t *pool) +{ + size_t i; + mpf_jitter_buffer_t *jb = apr_palloc(pool,sizeof(mpf_jitter_buffer_t)); + if(!jb_config) { + /* create default jb config */ + jb_config = apr_palloc(pool,sizeof(mpf_jb_config_t)); + mpf_jb_config_init(jb_config); + } + /* validate jb config */ + if(jb_config->initial_playout_delay == 0) { + /* default configuration */ + jb_config->min_playout_delay = 10; /* ms */ + jb_config->initial_playout_delay = 50; /* ms */ + jb_config->max_playout_delay = 200; /* ms */ + } + else { + if(jb_config->min_playout_delay > jb_config->initial_playout_delay) { + jb_config->min_playout_delay = jb_config->initial_playout_delay; + } + if(jb_config->max_playout_delay < jb_config->initial_playout_delay) { + jb_config->max_playout_delay = 2 * jb_config->initial_playout_delay; + } + } + jb->config = jb_config; + + jb->frame_ts = mpf_codec_frame_samples_calculate(codec->descriptor); + jb->frame_size = mpf_codec_frame_size_calculate(codec->descriptor,codec->attribs); + jb->frame_count = jb->config->max_playout_delay / CODEC_FRAME_TIME_BASE; + jb->raw_data = apr_palloc(pool,jb->frame_size*jb->frame_count); + jb->frames = apr_palloc(pool,sizeof(mpf_frame_t)*jb->frame_count); + for(i=0; iframe_count; i++) { + jb->frames[i].type = MEDIA_FRAME_TYPE_NONE; + jb->frames[i].codec_frame.buffer = jb->raw_data + i*jb->frame_size; + } + + jb->playout_delay_ts = jb->config->initial_playout_delay * + codec->descriptor->channel_count * codec->descriptor->sampling_rate / 1000; + + jb->write_sync = 1; + jb->write_ts_offset = 0; + jb->write_ts = jb->read_ts = 0; + + return jb; +} + +void mpf_jitter_buffer_destroy(mpf_jitter_buffer_t *jb) +{ +} + +apt_bool_t mpf_jitter_buffer_restart(mpf_jitter_buffer_t *jb) +{ + jb->write_sync = 1; + jb->write_ts_offset = 0; + jb->write_ts = jb->read_ts; + return TRUE; +} + +static APR_INLINE mpf_frame_t* mpf_jitter_buffer_frame_get(mpf_jitter_buffer_t *jb, apr_size_t ts) +{ + apr_size_t index = (ts / jb->frame_ts) % jb->frame_count; + return &jb->frames[index]; +} + +static APR_INLINE jb_result_t mpf_jitter_buffer_write_prepare(mpf_jitter_buffer_t *jb, apr_uint32_t ts, + apr_size_t *write_ts, apr_size_t *available_frame_count) +{ + jb_result_t result = JB_OK; + if(jb->write_sync) { + jb->write_ts_offset = ts - jb->write_ts; + jb->write_sync = 0; + } + + *write_ts = ts - jb->write_ts_offset + jb->playout_delay_ts; + if(*write_ts % jb->frame_ts != 0) { + /* not frame alligned */ + return JB_DISCARD_NOT_ALLIGNED; + } + + if(*write_ts >= jb->write_ts) { + if(*write_ts - jb->write_ts > jb->frame_ts) { + /* gap */ + } + /* normal write */ + } + else { + if(*write_ts >= jb->read_ts) { + /* backward write */ + } + else { + /* too late */ + result = JB_DISCARD_TOO_LATE; + } + } + *available_frame_count = jb->frame_count - (*write_ts - jb->read_ts)/jb->frame_ts; + return result; +} + +jb_result_t mpf_jitter_buffer_write(mpf_jitter_buffer_t *jb, mpf_codec_t *codec, void *buffer, apr_size_t size, apr_uint32_t ts) +{ + mpf_frame_t *media_frame; + apr_size_t write_ts; + apr_size_t available_frame_count = 0; + jb_result_t result = mpf_jitter_buffer_write_prepare(jb,ts,&write_ts,&available_frame_count); + if(result != JB_OK) { + return result; + } + + while(available_frame_count && size) { + media_frame = mpf_jitter_buffer_frame_get(jb,write_ts); + media_frame->codec_frame.size = jb->frame_size; + if(mpf_codec_dissect(codec,&buffer,&size,&media_frame->codec_frame) == FALSE) { + break; + } + + media_frame->type |= MEDIA_FRAME_TYPE_AUDIO; + write_ts += jb->frame_ts; + available_frame_count--; + } + + if(size) { + /* no frame available to write, but some data remains in buffer (partialy too early) */ + } + + if(write_ts > jb->write_ts) { + jb->write_ts = write_ts; + } + return result; +} + +jb_result_t mpf_jitter_buffer_write_named_event(mpf_jitter_buffer_t *jb, mpf_named_event_frame_t *named_event, apr_uint32_t ts) +{ + return JB_OK; +} + +apt_bool_t mpf_jitter_buffer_read(mpf_jitter_buffer_t *jb, mpf_frame_t *media_frame) +{ + mpf_frame_t *src_media_frame = mpf_jitter_buffer_frame_get(jb,jb->read_ts); + if(jb->write_ts > jb->read_ts) { + /* normal read */ + media_frame->type = src_media_frame->type; + if(media_frame->type & MEDIA_FRAME_TYPE_AUDIO) { + media_frame->codec_frame.size = src_media_frame->codec_frame.size; + memcpy(media_frame->codec_frame.buffer,src_media_frame->codec_frame.buffer,media_frame->codec_frame.size); + } + if(media_frame->type & MEDIA_FRAME_TYPE_EVENT) { + media_frame->event_frame = src_media_frame->event_frame; + } + } + else { + /* underflow */ + media_frame->type = MEDIA_FRAME_TYPE_NONE; + jb->write_ts += jb->frame_ts; + } + src_media_frame->type = MEDIA_FRAME_TYPE_NONE; + jb->read_ts += jb->frame_ts; + return TRUE; +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_rtp_attribs.c b/libs/unimrcp/libs/mpf/src/mpf_rtp_attribs.c new file mode 100644 index 0000000000..5b5931546f --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_rtp_attribs.c @@ -0,0 +1,58 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "apt_string_table.h" +#include "mpf_rtp_attribs.h" + +/** String table of RTP attributes (mpf_rtp_attrib_e) */ +static const apt_str_table_item_t mpf_rtp_attrib_table[] = { + {{"rtpmap", 6},1}, + {{"sendonly", 8},8}, + {{"recvonly", 8},2}, + {{"sendrecv", 8},4}, + {{"mid", 3},0}, + {{"ptime", 5},0} +}; + + +MPF_DECLARE(const apt_str_t*) mpf_rtp_attrib_str_get(mpf_rtp_attrib_e attrib_id) +{ + return apt_string_table_str_get(mpf_rtp_attrib_table,RTP_ATTRIB_COUNT,attrib_id); +} + +MPF_DECLARE(mpf_rtp_attrib_e) mpf_rtp_attrib_id_find(const apt_str_t *attrib) +{ + return apt_string_table_id_find(mpf_rtp_attrib_table,RTP_ATTRIB_COUNT,attrib); +} + +MPF_DECLARE(const apt_str_t*) mpf_stream_mode_str_get(mpf_stream_mode_e direction) +{ + mpf_rtp_attrib_e attrib_id = RTP_ATTRIB_UNKNOWN; + switch(direction) { + case STREAM_MODE_SEND: + attrib_id = RTP_ATTRIB_SENDONLY; + break; + case STREAM_MODE_RECEIVE: + attrib_id = RTP_ATTRIB_RECVONLY; + break; + case STREAM_MODE_SEND_RECEIVE: + attrib_id = RTP_ATTRIB_SENDRECV; + break; + default: + break; + } + return apt_string_table_str_get(mpf_rtp_attrib_table,RTP_ATTRIB_COUNT,attrib_id); +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_rtp_stream.c b/libs/unimrcp/libs/mpf/src/mpf_rtp_stream.c new file mode 100644 index 0000000000..3956d1e6e3 --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_rtp_stream.c @@ -0,0 +1,764 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include "mpf_rtp_stream.h" +#include "mpf_termination.h" +#include "mpf_codec_manager.h" +#include "mpf_rtp_header.h" +#include "mpf_rtp_defs.h" +#include "apt_log.h" + + +/** RTP stream */ +typedef struct mpf_rtp_stream_t mpf_rtp_stream_t; +struct mpf_rtp_stream_t { + mpf_audio_stream_t *base; + + mpf_rtp_media_descriptor_t *local_media; + mpf_rtp_media_descriptor_t *remote_media; + + rtp_transmitter_t transmitter; + rtp_receiver_t receiver; + + mpf_rtp_config_t *config; + + apr_socket_t *socket; + apr_sockaddr_t *local_sockaddr; + apr_sockaddr_t *remote_sockaddr; + + apr_pool_t *pool; +}; + +static apt_bool_t mpf_rtp_stream_destroy(mpf_audio_stream_t *stream); +static apt_bool_t mpf_rtp_rx_stream_open(mpf_audio_stream_t *stream); +static apt_bool_t mpf_rtp_rx_stream_close(mpf_audio_stream_t *stream); +static apt_bool_t mpf_rtp_stream_receive(mpf_audio_stream_t *stream, mpf_frame_t *frame); +static apt_bool_t mpf_rtp_tx_stream_open(mpf_audio_stream_t *stream); +static apt_bool_t mpf_rtp_tx_stream_close(mpf_audio_stream_t *stream); +static apt_bool_t mpf_rtp_stream_transmit(mpf_audio_stream_t *stream, const mpf_frame_t *frame); + +static const mpf_audio_stream_vtable_t vtable = { + mpf_rtp_stream_destroy, + mpf_rtp_rx_stream_open, + mpf_rtp_rx_stream_close, + mpf_rtp_stream_receive, + mpf_rtp_tx_stream_open, + mpf_rtp_tx_stream_close, + mpf_rtp_stream_transmit +}; + +static apt_bool_t mpf_rtp_socket_create(mpf_rtp_stream_t *stream, mpf_rtp_media_descriptor_t *local_media); + + +MPF_DECLARE(mpf_audio_stream_t*) mpf_rtp_stream_create(mpf_termination_t *termination, mpf_rtp_config_t *config, apr_pool_t *pool) +{ + mpf_rtp_stream_t *rtp_stream = apr_palloc(pool,sizeof(mpf_rtp_stream_t)); + rtp_stream->pool = pool; + rtp_stream->config = config; + rtp_stream->local_media = NULL; + rtp_stream->remote_media = NULL; + rtp_stream->socket = NULL; + rtp_stream->local_sockaddr = NULL; + rtp_stream->remote_sockaddr = NULL; + rtp_stream->base = mpf_audio_stream_create(rtp_stream,&vtable,STREAM_MODE_NONE,pool); + rtp_stream->base->termination = termination; + rtp_receiver_init(&rtp_stream->receiver); + rtp_transmitter_init(&rtp_stream->transmitter); + rtp_stream->transmitter.ssrc = (apr_uint32_t)apr_time_now(); + + return rtp_stream->base; +} + +static void mpf_rtp_stream_ip_port_set(mpf_rtp_media_descriptor_t *media, mpf_rtp_config_t *config) +{ + if(media->base.ip.length == 0) { + media->base.ip = config->ip; + media->base.ext_ip = config->ext_ip; + } + if(media->base.port == 0) { + media->base.port = config->rtp_port_cur; + config->rtp_port_cur += 2; + if(config->rtp_port_cur == config->rtp_port_max) { + config->rtp_port_cur = config->rtp_port_min; + } + } +} + +static apt_bool_t mpf_rtp_stream_local_media_create(mpf_rtp_stream_t *rtp_stream, mpf_rtp_media_descriptor_t *local_media, mpf_rtp_media_descriptor_t *remote_media) +{ + apt_bool_t status = TRUE; + if(!local_media) { + /* local media is not specified, create the default one */ + local_media = apr_palloc(rtp_stream->pool,sizeof(mpf_rtp_media_descriptor_t)); + mpf_rtp_media_descriptor_init(local_media); + local_media->base.state = MPF_MEDIA_ENABLED; + local_media->mode = STREAM_MODE_SEND_RECEIVE; + } + if(remote_media) { + local_media->base.id = remote_media->base.id; + } + + mpf_rtp_stream_ip_port_set(local_media,rtp_stream->config); + if(mpf_rtp_socket_create(rtp_stream,local_media) == FALSE) { + local_media->base.state = MPF_MEDIA_DISABLED; + status = FALSE; + } + + if(rtp_stream->config->ptime) { + local_media->ptime = rtp_stream->config->ptime; + } + + if(mpf_codec_list_is_empty(&local_media->codec_list) == TRUE) { + if(mpf_codec_list_is_empty(&rtp_stream->config->codec_list) == TRUE) { + mpf_codec_manager_codec_list_get( + rtp_stream->base->termination->codec_manager, + &local_media->codec_list, + rtp_stream->pool); + } + else { + mpf_codec_list_copy(&local_media->codec_list, + &rtp_stream->config->codec_list, + rtp_stream->pool); + } + } + + rtp_stream->local_media = local_media; + return status; +} + +static apt_bool_t mpf_rtp_stream_local_media_update(mpf_rtp_stream_t *rtp_stream, mpf_rtp_media_descriptor_t *media) +{ + apt_bool_t status = TRUE; + if(apt_string_compare(&rtp_stream->local_media->base.ip,&media->base.ip) == FALSE || + rtp_stream->local_media->base.port != media->base.port) { + + if(mpf_rtp_socket_create(rtp_stream,media) == FALSE) { + media->base.state = MPF_MEDIA_DISABLED; + status = FALSE; + } + } + if(mpf_codec_list_is_empty(&media->codec_list) == TRUE) { + mpf_codec_manager_codec_list_get( + rtp_stream->base->termination->codec_manager, + &media->codec_list, + rtp_stream->pool); + } + + rtp_stream->local_media = media; + return status; +} + +static apt_bool_t mpf_rtp_stream_remote_media_update(mpf_rtp_stream_t *rtp_stream, mpf_rtp_media_descriptor_t *media) +{ + apt_bool_t status = TRUE; + if(!rtp_stream->remote_media || + apt_string_compare(&rtp_stream->remote_media->base.ip,&media->base.ip) == FALSE || + rtp_stream->remote_media->base.port != media->base.port) { + + rtp_stream->remote_sockaddr = NULL; + apr_sockaddr_info_get( + &rtp_stream->remote_sockaddr, + media->base.ip.buf, + APR_INET, + media->base.port, + 0, + rtp_stream->pool); + if(!rtp_stream->remote_sockaddr) { + status = FALSE; + } + } + + rtp_stream->remote_media = media; + return status; +} + +static apt_bool_t mpf_rtp_stream_media_negotiate(mpf_rtp_stream_t *rtp_stream) +{ + if(!rtp_stream->local_media || !rtp_stream->remote_media) { + return FALSE; + } + + rtp_stream->local_media->base.id = rtp_stream->remote_media->base.id; + rtp_stream->local_media->base.state = rtp_stream->remote_media->base.state; + + rtp_stream->local_media->mid = rtp_stream->remote_media->mid; + rtp_stream->local_media->ptime = rtp_stream->remote_media->ptime; + rtp_stream->local_media->mode = mpf_stream_mode_negotiate(rtp_stream->remote_media->mode); + + if(mpf_codec_list_is_empty(&rtp_stream->remote_media->codec_list) == TRUE) { + /* no remote codecs available, initialize them according to the local codecs */ + mpf_codec_list_copy(&rtp_stream->remote_media->codec_list, + &rtp_stream->local_media->codec_list, + rtp_stream->pool); + } + + /* intersect local and remote codecs */ + if(rtp_stream->config->own_preferrence == TRUE) { + mpf_codec_list_intersect(&rtp_stream->local_media->codec_list,&rtp_stream->remote_media->codec_list); + } + else { + mpf_codec_list_intersect(&rtp_stream->remote_media->codec_list,&rtp_stream->local_media->codec_list); + } + + rtp_stream->base->mode = rtp_stream->local_media->mode; + return TRUE; +} + +MPF_DECLARE(apt_bool_t) mpf_rtp_stream_modify(mpf_audio_stream_t *stream, mpf_rtp_stream_descriptor_t *descriptor) +{ + apt_bool_t status = TRUE; + mpf_rtp_stream_t *rtp_stream = stream->obj; + if(!rtp_stream) { + return FALSE; + } + + if(!rtp_stream->local_media) { + /* create local media */ + status = mpf_rtp_stream_local_media_create(rtp_stream,descriptor->local,descriptor->remote); + } + else if(descriptor->local) { + /* update local media */ + status = mpf_rtp_stream_local_media_update(rtp_stream,descriptor->local); + } + + if(descriptor->remote && status == TRUE) { + /* update remote media */ + mpf_rtp_stream_remote_media_update(rtp_stream,descriptor->remote); + + /* negotiate local and remote media */ + mpf_rtp_stream_media_negotiate(rtp_stream); + } + + if((rtp_stream->base->mode & STREAM_MODE_SEND) == STREAM_MODE_SEND) { + rtp_stream->base->tx_codec = mpf_codec_manager_codec_get( + rtp_stream->base->termination->codec_manager, + rtp_stream->remote_media->codec_list.preffered, + rtp_stream->pool); + if(rtp_stream->base->tx_codec) { + rtp_stream->transmitter.samples_per_frame = + (apr_uint32_t)mpf_codec_frame_samples_calculate(rtp_stream->base->tx_codec->descriptor); + } + } + if((rtp_stream->base->mode & STREAM_MODE_RECEIVE) == STREAM_MODE_RECEIVE) { + rtp_stream->base->rx_codec = mpf_codec_manager_codec_get( + rtp_stream->base->termination->codec_manager, + rtp_stream->local_media->codec_list.preffered, + rtp_stream->pool); + } + + if(!descriptor->local) { + descriptor->local = rtp_stream->local_media; + } + return status; +} + +static apt_bool_t mpf_rtp_stream_destroy(mpf_audio_stream_t *stream) +{ + mpf_rtp_stream_t *rtp_stream = stream->obj; + if(rtp_stream->socket) { + apr_socket_close(rtp_stream->socket); + rtp_stream->socket = NULL; + } + + return TRUE; +} + +static apt_bool_t mpf_rtp_rx_stream_open(mpf_audio_stream_t *stream) +{ + mpf_rtp_stream_t *rtp_stream = stream->obj; + rtp_receiver_t *receiver = &rtp_stream->receiver; + if(!rtp_stream->socket || !rtp_stream->local_media) { + return FALSE; + } + + receiver->jb = mpf_jitter_buffer_create( + &rtp_stream->config->jb_config, + stream->rx_codec, + rtp_stream->pool); + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Open RTP Receive %s:%hu <- %s:%hu playout [%d ms]", + rtp_stream->local_media->base.ip.buf, + rtp_stream->local_media->base.port, + rtp_stream->remote_media->base.ip.buf, + rtp_stream->remote_media->base.port, + rtp_stream->config->jb_config.initial_playout_delay); + return TRUE; +} + +static apt_bool_t mpf_rtp_rx_stream_close(mpf_audio_stream_t *stream) +{ + mpf_rtp_stream_t *rtp_stream = stream->obj; + rtp_receiver_t *receiver = &rtp_stream->receiver; + receiver->stat.lost_packets = 0; + if(receiver->stat.received_packets) { + apr_uint32_t expected_packets = receiver->history.seq_cycles + + receiver->history.seq_num_max - receiver->history.seq_num_base + 1; + if(expected_packets > receiver->stat.received_packets) { + receiver->stat.lost_packets = expected_packets - receiver->stat.received_packets; + } + } + + mpf_jitter_buffer_destroy(receiver->jb); + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Close RTP Receive %s:%hu <- %s:%hu [r:%lu l:%lu j:%lu]", + rtp_stream->local_media->base.ip.buf, + rtp_stream->local_media->base.port, + rtp_stream->remote_media->base.ip.buf, + rtp_stream->remote_media->base.port, + receiver->stat.received_packets, + receiver->stat.lost_packets, + receiver->stat.jitter); + return TRUE; +} + + +static APR_INLINE void rtp_rx_overall_stat_reset(rtp_receiver_t *receiver) +{ + memset(&receiver->stat,0,sizeof(receiver->stat)); + memset(&receiver->history,0,sizeof(receiver->history)); + memset(&receiver->periodic_history,0,sizeof(receiver->periodic_history)); +} + +static APR_INLINE void rtp_rx_stat_init(rtp_receiver_t *receiver, rtp_header_t *header, apr_time_t *time) +{ + receiver->stat.ssrc = header->ssrc; + receiver->history.seq_num_base = receiver->history.seq_num_max = (apr_uint16_t)header->sequence; + receiver->history.ts_last = header->timestamp; + receiver->history.time_last = *time; +} + +static APR_INLINE void rtp_rx_restart(rtp_receiver_t *receiver) +{ + apr_byte_t restarts = ++receiver->stat.restarts; + rtp_rx_overall_stat_reset(receiver); + mpf_jitter_buffer_restart(receiver->jb); + receiver->stat.restarts = restarts; +} + +static rtp_header_t* rtp_rx_header_skip(void **buffer, apr_size_t *size) +{ + apr_size_t offset = 0; + rtp_header_t *header = (rtp_header_t*)*buffer; + + /* RTP header validity check */ + if(header->version != RTP_VERSION) { + return NULL; + } + + /* calculate payload offset */ + offset = sizeof(rtp_header_t) + (header->count * sizeof(apr_uint32_t)); + + /* additional offset in case of RTP extension */ + if(header->extension) { + rtp_extension_header_t *ext_header = (rtp_extension_header_t*)(((apr_byte_t*)*buffer)+offset); + offset += (ntohs(ext_header->length) * sizeof(apr_uint32_t)); + } + + if (offset >= *size) { + return NULL; + } + + /* skip to payload */ + *buffer = (apr_byte_t*)*buffer + offset; + *size = *size - offset; + + return header; +} + +typedef enum { + RTP_SSRC_UPDATE, + RTP_SSRC_PROBATION, + RTP_SSRC_RESTART +} rtp_ssrc_result_e; + +static APR_INLINE rtp_ssrc_result_e rtp_rx_ssrc_update(rtp_receiver_t *receiver, apr_uint32_t ssrc) +{ + if(receiver->stat.ssrc == ssrc) { + /* known ssrc */ + if(receiver->history.ssrc_probation) { + /* reset the probation for new ssrc */ + receiver->history.ssrc_probation = 0; + receiver->history.ssrc_new = 0; + } + } + else { + if(receiver->history.ssrc_new == ssrc) { + if(--receiver->history.ssrc_probation == 0) { + /* restart with new ssrc */ + receiver->stat.ssrc = ssrc; + return RTP_SSRC_RESTART; + } + else { + return RTP_SSRC_PROBATION; + } + } + else { + /* start probation for new ssrc */ + receiver->history.ssrc_new = ssrc; + receiver->history.ssrc_probation = 5; + return RTP_SSRC_PROBATION; + } + } + return RTP_SSRC_UPDATE; +} + +typedef enum { + RTP_SEQ_UPDATE, + RTP_SEQ_MISORDER, + RTP_SEQ_DRIFT +} rtp_seq_result_e; + +static APR_INLINE rtp_seq_result_e rtp_rx_seq_update(rtp_receiver_t *receiver, apr_uint16_t seq_num) +{ + rtp_seq_result_e result = RTP_SEQ_UPDATE; + apr_uint16_t seq_delta = seq_num - receiver->history.seq_num_max; + if(seq_delta < MAX_DROPOUT) { + if(seq_num < receiver->history.seq_num_max) { + /* sequence number wrapped */ + receiver->history.seq_cycles += RTP_SEQ_MOD; + } + receiver->history.seq_num_max = seq_num; + } + else if(seq_delta <= RTP_SEQ_MOD - MAX_MISORDER) { + /* sequence number made a very large jump */ + result = RTP_SEQ_DRIFT; + } + else { + /* duplicate or misordered packet */ + result = RTP_SEQ_MISORDER; + } + receiver->stat.received_packets++; + + if(receiver->stat.received_packets - receiver->periodic_history.received_prior >= 50) { + receiver->periodic_history.received_prior = receiver->stat.received_packets; + receiver->periodic_history.discarded_prior = receiver->stat.discarded_packets; + receiver->periodic_history.jitter_min = receiver->stat.jitter; + receiver->periodic_history.jitter_max = receiver->stat.jitter; + } + return result; +} + +typedef enum { + RTP_TS_UPDATE, + RTP_TS_DRIFT +} rtp_ts_result_e; + +static APR_INLINE rtp_ts_result_e rtp_rx_ts_update(rtp_receiver_t *receiver, mpf_codec_descriptor_t *descriptor, apr_time_t *time, apr_uint32_t ts) +{ + apr_int32_t deviation; + + /* arrival time diff in samples */ + deviation = (apr_int32_t)apr_time_as_msec(*time - receiver->history.time_last) * + descriptor->channel_count * descriptor->sampling_rate / 1000; + /* arrival timestamp diff */ + deviation -= ts - receiver->history.ts_last; + + if(deviation < 0) { + deviation = -deviation; + } + + if(deviation > DEVIATION_THRESHOLD) { + return RTP_TS_DRIFT; + } + + receiver->stat.jitter += deviation - ((receiver->stat.jitter + 8) >> 4); +#if PRINT_RTP_PACKET_STAT + printf("jitter=%d deviation=%d\n",receiver->stat.jitter,deviation); +#endif + receiver->history.time_last = *time; + receiver->history.ts_last = ts; + + if(receiver->stat.jitter < receiver->periodic_history.jitter_min) { + receiver->periodic_history.jitter_min = receiver->stat.jitter; + } + if(receiver->stat.jitter > receiver->periodic_history.jitter_max) { + receiver->periodic_history.jitter_max = receiver->stat.jitter; + } + return RTP_TS_UPDATE; +} + +static APR_INLINE void rtp_rx_failure_threshold_check(rtp_receiver_t *receiver) +{ + apr_uint32_t received; + apr_uint32_t discarded; + received = receiver->stat.received_packets - receiver->periodic_history.received_prior; + discarded = receiver->stat.discarded_packets - receiver->periodic_history.discarded_prior; + + if(discarded * 100 > received * DISCARDED_TO_RECEIVED_RATIO_THRESHOLD) { + /* failure threshold hired, restart */ + rtp_rx_restart(receiver); + } +} + +static apt_bool_t rtp_rx_packet_receive(rtp_receiver_t *receiver, mpf_codec_t *codec, void *buffer, apr_size_t size) +{ + apr_time_t time; + rtp_ssrc_result_e ssrc_result; + rtp_header_t *header = rtp_rx_header_skip(&buffer,&size); + if(!header) { + /* invalid RTP packet */ + receiver->stat.invalid_packets++; + return FALSE; + } + + header->sequence = ntohs((apr_uint16_t)header->sequence); + header->timestamp = ntohl(header->timestamp); + header->ssrc = ntohl(header->ssrc); + + time = apr_time_now(); + +#if PRINT_RTP_PACKET_STAT + printf("RTP time=%6lu ssrc=%8lx pt=%3u %cts=%9lu seq=%5u size=%lu\n", + (apr_uint32_t)apr_time_usec(time), + header->ssrc, header->type, header->marker ? '*' : ' ', + header->timestamp, header->sequence, size); +#endif + if(!receiver->stat.received_packets) { + /* initialization */ + rtp_rx_stat_init(receiver,header,&time); + } + + ssrc_result = rtp_rx_ssrc_update(receiver,header->ssrc); + if(ssrc_result == RTP_SSRC_PROBATION) { + receiver->stat.invalid_packets++; + return FALSE; + } + else if(ssrc_result == RTP_SSRC_RESTART) { + rtp_rx_restart(receiver); + rtp_rx_stat_init(receiver,header,&time); + } + + rtp_rx_seq_update(receiver,(apr_uint16_t)header->sequence); + + if(rtp_rx_ts_update(receiver,codec->descriptor,&time,header->timestamp) == RTP_TS_DRIFT) { + rtp_rx_restart(receiver); + return FALSE; + } + + if(header->type == codec->descriptor->payload_type) { + /* codec */ + if(mpf_jitter_buffer_write(receiver->jb,codec,buffer,size,header->timestamp) != JB_OK) { + receiver->stat.discarded_packets++; + rtp_rx_failure_threshold_check(receiver); + } + } + else if(header->type == receiver->event_pt && receiver->event_pt != 0) { + /* named event */ + mpf_named_event_frame_t *named_event = (mpf_named_event_frame_t *)buffer; + if(mpf_jitter_buffer_write_named_event(receiver->jb,named_event,header->timestamp) != JB_OK) { + receiver->stat.discarded_packets++; + rtp_rx_failure_threshold_check(receiver); + } + } + else if(header->type == 13 || header->type == 19) { + /* CN packet*/ + receiver->stat.ignored_packets++; + } + else { + /* invalid payload type */ + receiver->stat.ignored_packets++; + } + + return TRUE; +} + +static apt_bool_t rtp_rx_process(mpf_rtp_stream_t *rtp_stream) +{ + char buffer[1500]; + apr_size_t size = sizeof(buffer); + apr_size_t max_count = 5; + while(max_count && apr_socket_recvfrom(rtp_stream->remote_sockaddr,rtp_stream->socket,0,buffer,&size) == APR_SUCCESS) { + rtp_rx_packet_receive(&rtp_stream->receiver,rtp_stream->base->rx_codec,buffer,size); + + size = sizeof(buffer); + max_count--; + } + return TRUE; +} + +static apt_bool_t mpf_rtp_stream_receive(mpf_audio_stream_t *stream, mpf_frame_t *frame) +{ + mpf_rtp_stream_t *rtp_stream = stream->obj; + rtp_rx_process(rtp_stream); + + return mpf_jitter_buffer_read(rtp_stream->receiver.jb,frame); +} + + + + + +static apt_bool_t mpf_rtp_tx_stream_open(mpf_audio_stream_t *stream) +{ + apr_size_t frame_size; + mpf_rtp_stream_t *rtp_stream = stream->obj; + rtp_transmitter_t *transmitter = &rtp_stream->transmitter; + if(!rtp_stream->socket || !rtp_stream->remote_media) { + return FALSE; + } + + if(!transmitter->ptime) { + if(rtp_stream->config && rtp_stream->config->ptime) { + transmitter->ptime = rtp_stream->config->ptime; + } + else { + transmitter->ptime = 20; + } + } + transmitter->packet_frames = transmitter->ptime / CODEC_FRAME_TIME_BASE; + transmitter->current_frames = 0; + + frame_size = mpf_codec_frame_size_calculate( + stream->tx_codec->descriptor, + stream->tx_codec->attribs); + transmitter->packet_data = apr_palloc( + rtp_stream->pool, + sizeof(rtp_header_t) + transmitter->packet_frames * frame_size); + + transmitter->inactivity = 1; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Open RTP Transmit %s:%hu -> %s:%hu", + rtp_stream->local_media->base.ip.buf, + rtp_stream->local_media->base.port, + rtp_stream->remote_media->base.ip.buf, + rtp_stream->remote_media->base.port); + return TRUE; +} + +static apt_bool_t mpf_rtp_tx_stream_close(mpf_audio_stream_t *stream) +{ + mpf_rtp_stream_t *rtp_stream = stream->obj; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Close RTP Transmit %s:%hu -> %s:%hu [s:%lu]", + rtp_stream->local_media->base.ip.buf, + rtp_stream->local_media->base.port, + rtp_stream->remote_media->base.ip.buf, + rtp_stream->remote_media->base.port, + rtp_stream->transmitter.stat.sent_packets); + return TRUE; +} + + +static APR_INLINE void rtp_header_prepare(rtp_transmitter_t *transmitter, apr_byte_t payload_type) +{ + rtp_header_t *header = (rtp_header_t*)transmitter->packet_data; + +#if PRINT_RTP_PACKET_STAT + printf("> RTP time=%6lu ssrc=%8lx pt=%3u %cts=%9lu seq=%5u\n", + (apr_uint32_t)apr_time_usec(apr_time_now()), + transmitter->ssrc, payload_type, transmitter->inactivity ? '*' : ' ', + transmitter->timestamp, transmitter->last_seq_num); +#endif + header->version = RTP_VERSION; + header->padding = 0; + header->extension = 0; + header->count = 0; + header->marker = transmitter->inactivity; + header->type = payload_type; + header->sequence = htons(++transmitter->last_seq_num); + header->timestamp = htonl(transmitter->timestamp); + header->ssrc = htonl(transmitter->ssrc); + + if(transmitter->inactivity) { + transmitter->inactivity = 0; + } + + transmitter->packet_size = sizeof(rtp_header_t); +} + +static apt_bool_t mpf_rtp_stream_transmit(mpf_audio_stream_t *stream, const mpf_frame_t *frame) +{ + apt_bool_t status = TRUE; + mpf_rtp_stream_t *rtp_stream = stream->obj; + rtp_transmitter_t *transmitter = &rtp_stream->transmitter; + + transmitter->timestamp += transmitter->samples_per_frame; + + if(transmitter->current_frames == 0) { + if(frame->type == MEDIA_FRAME_TYPE_NONE) { + transmitter->inactivity = 1; + } + else { + rtp_header_prepare(transmitter,stream->tx_codec->descriptor->payload_type); + } + } + + if(!transmitter->inactivity) { + memcpy( + transmitter->packet_data + transmitter->packet_size, + frame->codec_frame.buffer, + frame->codec_frame.size); + transmitter->packet_size += frame->codec_frame.size; + + if(++transmitter->current_frames == transmitter->packet_frames) { + if(apr_socket_sendto( + rtp_stream->socket, + rtp_stream->remote_sockaddr, + 0, + transmitter->packet_data, + &transmitter->packet_size) == APR_SUCCESS) { + transmitter->stat.sent_packets++; + } + else { + status = FALSE; + } + transmitter->current_frames = 0; + } + } + + return status; +} + +static apt_bool_t mpf_rtp_socket_create(mpf_rtp_stream_t *stream, mpf_rtp_media_descriptor_t *local_media) +{ + if(stream->socket) { + apr_socket_close(stream->socket); + stream->socket = NULL; + } + + stream->local_sockaddr = NULL; + apr_sockaddr_info_get( + &stream->local_sockaddr, + local_media->base.ip.buf, + APR_INET, + local_media->base.port, + 0, + stream->pool); + if(!stream->local_sockaddr) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Failed to Get Sockaddr %s:%hu", + local_media->base.ip.buf, + local_media->base.port); + return FALSE; + } + if(apr_socket_create(&stream->socket,stream->local_sockaddr->family,SOCK_DGRAM,0,stream->pool) != APR_SUCCESS) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Failed to Create Socket %s:%hu", + local_media->base.ip.buf, + local_media->base.port); + return FALSE; + } + + apr_socket_opt_set(stream->socket,APR_SO_NONBLOCK,1); + apr_socket_timeout_set(stream->socket,0); + apr_socket_opt_set(stream->socket,APR_SO_REUSEADDR,1); + + if(apr_socket_bind(stream->socket,stream->local_sockaddr) != APR_SUCCESS) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Failed to Bind Socket to %s:%hu", + local_media->base.ip.buf, + local_media->base.port); + apr_socket_close(stream->socket); + stream->socket = NULL; + return FALSE; + } + return TRUE; +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_rtp_termination_factory.c b/libs/unimrcp/libs/mpf/src/mpf_rtp_termination_factory.c new file mode 100644 index 0000000000..5f5e058f6a --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_rtp_termination_factory.c @@ -0,0 +1,76 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mpf_termination.h" +#include "mpf_rtp_termination_factory.h" +#include "mpf_rtp_stream.h" +#include "apt_log.h" + +typedef struct rtp_termination_factory_t rtp_termination_factory_t; +struct rtp_termination_factory_t { + mpf_termination_factory_t base; + mpf_rtp_config_t *config; +}; + +static apt_bool_t mpf_rtp_termination_destroy(mpf_termination_t *termination) +{ + return TRUE; +} + +static apt_bool_t mpf_rtp_termination_modify(mpf_termination_t *termination, void *descriptor) +{ + mpf_rtp_termination_descriptor_t *rtp_descriptor = descriptor; + mpf_audio_stream_t *audio_stream = termination->audio_stream; + if(!audio_stream) { + rtp_termination_factory_t *termination_factory = (rtp_termination_factory_t*)termination->termination_factory; + audio_stream = mpf_rtp_stream_create(termination,termination_factory->config,termination->pool); + if(!audio_stream) { + return FALSE; + } + termination->audio_stream = audio_stream; + } + + return mpf_rtp_stream_modify(audio_stream,&rtp_descriptor->audio); +} + +static const mpf_termination_vtable_t rtp_vtable = { + mpf_rtp_termination_destroy, + mpf_rtp_termination_modify, +}; + +static mpf_termination_t* mpf_rtp_termination_create(mpf_termination_factory_t *termination_factory, void *obj, apr_pool_t *pool) +{ + return mpf_termination_base_create(termination_factory,obj,&rtp_vtable,NULL,NULL,pool); +} + +MPF_DECLARE(mpf_termination_factory_t*) mpf_rtp_termination_factory_create( + mpf_rtp_config_t *rtp_config, + apr_pool_t *pool) +{ + rtp_termination_factory_t *rtp_termination_factory; + if(!rtp_config) { + return NULL; + } + rtp_config->rtp_port_cur = rtp_config->rtp_port_min; + rtp_termination_factory = apr_palloc(pool,sizeof(rtp_termination_factory_t)); + rtp_termination_factory->base.create_termination = mpf_rtp_termination_create; + rtp_termination_factory->config = rtp_config; + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create RTP Termination Factory %s:[%hu,%hu]", + rtp_config->ip.buf, + rtp_config->rtp_port_min, + rtp_config->rtp_port_max); + return &rtp_termination_factory->base; +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_termination.c b/libs/unimrcp/libs/mpf/src/mpf_termination.c new file mode 100644 index 0000000000..2c975442b6 --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_termination.c @@ -0,0 +1,132 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mpf_termination.h" +#include "mpf_stream.h" +#include "mpf_codec_manager.h" + +const mpf_codec_descriptor_t* l16_descriptor_get(); + +MPF_DECLARE(mpf_termination_t*) mpf_termination_base_create( + mpf_termination_factory_t *termination_factory, + void *obj, + const mpf_termination_vtable_t *vtable, + mpf_audio_stream_t *audio_stream, + mpf_video_stream_t *video_stream, + apr_pool_t *pool) +{ + mpf_termination_t *termination = apr_palloc(pool,sizeof(mpf_termination_t)); + termination->pool = pool; + termination->obj = obj; + termination->event_handler_obj = NULL; + termination->event_handler = NULL; + termination->codec_manager = NULL; + termination->termination_factory = termination_factory; + termination->vtable = vtable; + termination->slot = 0; + if(audio_stream) { + audio_stream->termination = termination; + } + if(video_stream) { + video_stream->termination = termination; + } + termination->audio_stream = audio_stream; + termination->video_stream = video_stream; + return termination; +} + +MPF_DECLARE(apt_bool_t) mpf_termination_destroy(mpf_termination_t *termination) +{ + if(termination->vtable && termination->vtable->destroy) { + termination->vtable->destroy(termination); + } + return TRUE; +} + +MPF_DECLARE(void*) mpf_termination_object_get(mpf_termination_t *termination) +{ + return termination->obj; +} + +MPF_DECLARE(apt_bool_t) mpf_termination_modify(mpf_termination_t *termination, void *descriptor) +{ + if(termination->vtable && termination->vtable->modify) { + termination->vtable->modify(termination,descriptor); + } + return TRUE; +} + +static mpf_codec_t* mpf_termination_default_codec_create(mpf_termination_t *termination) +{ + mpf_codec_t *codec; + const mpf_codec_descriptor_t *default_descriptor = l16_descriptor_get(); + mpf_codec_descriptor_t *descriptor = apr_palloc(termination->pool,sizeof(mpf_codec_descriptor_t)); + mpf_codec_descriptor_init(descriptor); + *descriptor = *default_descriptor; + codec = mpf_codec_manager_codec_get( + termination->codec_manager, + descriptor, + termination->pool); + return codec; +} + +MPF_DECLARE(apt_bool_t) mpf_termination_validate(mpf_termination_t *termination) +{ + mpf_audio_stream_t *audio_stream; + if(!termination) { + return FALSE; + } + audio_stream = termination->audio_stream; + if(audio_stream) { + if(!audio_stream->vtable) { + return FALSE; + } + if((audio_stream->mode & STREAM_MODE_RECEIVE) == STREAM_MODE_RECEIVE) { + if(!audio_stream->rx_codec) { + audio_stream->rx_codec = mpf_termination_default_codec_create(termination); + } + } + if((audio_stream->mode & STREAM_MODE_SEND) == STREAM_MODE_SEND) { + if(!audio_stream->tx_codec) { + audio_stream->tx_codec = mpf_termination_default_codec_create(termination); + } + } + } + return TRUE; +} + + +/** Create MPF termination by termination factory */ +MPF_DECLARE(mpf_termination_t*) mpf_termination_create( + mpf_termination_factory_t *termination_factory, + void *obj, + apr_pool_t *pool) +{ + if(termination_factory && termination_factory->create_termination) { + return termination_factory->create_termination(termination_factory,obj,pool); + } + return NULL; +} + +/** Create raw MPF termination. */ +MPF_DECLARE(mpf_termination_t*) mpf_raw_termination_create( + void *obj, + mpf_audio_stream_t *audio_stream, + mpf_video_stream_t *video_stream, + apr_pool_t *pool) +{ + return mpf_termination_base_create(NULL,obj,NULL,audio_stream,video_stream,pool); +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_timer.c b/libs/unimrcp/libs/mpf/src/mpf_timer.c new file mode 100644 index 0000000000..aaca492eb2 --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_timer.c @@ -0,0 +1,154 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mpf_timer.h" + +#ifdef WIN32 +#define ENABLE_MULTIMEDIA_TIMERS +#endif + +#ifdef ENABLE_MULTIMEDIA_TIMERS + +#pragma warning(disable:4201) +#include +#include + +#ifndef TIME_KILL_SYNCHRONOUS +#define TIME_KILL_SYNCHRONOUS 0x0100 +#endif + +struct mpf_timer_t { + unsigned int timer_id; + mpf_timer_proc_f timer_proc; + void *obj; +}; + +#define MAX_MEDIA_TIMERS 10 + +static mpf_timer_t media_timer_set[MAX_MEDIA_TIMERS]; + +static void CALLBACK mm_timer_proc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2); + +MPF_DECLARE(mpf_timer_t*) mpf_timer_start(unsigned long timeout, mpf_timer_proc_f timer_proc, void *obj, apr_pool_t *pool) +{ + mpf_timer_t *timer = NULL; + size_t i; + for(i = 0; itimer_proc = timer_proc; + timer->obj = obj; + timer->timer_id = timeSetEvent(timeout, 0, mm_timer_proc, i, + TIME_PERIODIC | TIME_CALLBACK_FUNCTION | TIME_KILL_SYNCHRONOUS); + if(!timer->timer_id) { + timer = NULL; + } + } + return timer; +} + +MPF_DECLARE(void) mpf_timer_stop(mpf_timer_t *timer) +{ + if(timer) { + timeKillEvent(timer->timer_id); + timer->timer_id = 0; + timer->timer_proc = NULL; + timer->obj = NULL; + } +} + +static void CALLBACK mm_timer_proc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) +{ + mpf_timer_t *timer; + if(dwUser >= MAX_MEDIA_TIMERS) { + return; + } + timer = &media_timer_set[dwUser]; + timer->timer_proc(timer,timer->obj); +} + +#else + +#include + +struct mpf_timer_t { + apr_thread_t *thread; + apr_byte_t running; + + unsigned long timeout; + mpf_timer_proc_f timer_proc; + void *obj; +}; + +static void* APR_THREAD_FUNC timer_thread_proc(apr_thread_t *thread, void *data); + +MPF_DECLARE(mpf_timer_t*) mpf_timer_start(unsigned long timeout, mpf_timer_proc_f timer_proc, void *obj, apr_pool_t *pool) +{ + mpf_timer_t *timer = apr_palloc(pool,sizeof(mpf_timer_t)); + timer->timeout = timeout; + timer->timer_proc = timer_proc; + timer->obj = obj; + timer->running = 1; + if(apr_thread_create(&timer->thread,NULL,timer_thread_proc,timer,pool) != APR_SUCCESS) { + return NULL; + } + return timer; +} + +MPF_DECLARE(void) mpf_timer_stop(mpf_timer_t *timer) +{ + if(timer) { + timer->running = 0; + if(timer->thread) { + apr_status_t s; + apr_thread_join(&s,timer->thread); + timer->thread = NULL; + } + } +} + +static void* APR_THREAD_FUNC timer_thread_proc(apr_thread_t *thread, void *data) +{ + mpf_timer_t *timer = data; + apr_interval_time_t timeout = timer->timeout * 1000; + apr_interval_time_t time_drift = 0; + apr_time_t time_now, time_last; + + time_now = apr_time_now(); + while(timer->running) { + time_last = time_now; + timer->timer_proc(timer,timer->obj); + + if(timeout > time_drift) { + apr_sleep(timeout - time_drift); + } + + time_now = apr_time_now(); + time_drift += time_now - time_last - timeout; +#if 0 + printf("time_drift=%d\n",time_drift); +#endif + } + + return NULL; +} + +#endif diff --git a/libs/unimrcp/libs/mrcp-client/Makefile.am b/libs/unimrcp/libs/mrcp-client/Makefile.am new file mode 100644 index 0000000000..ec749369be --- /dev/null +++ b/libs/unimrcp/libs/mrcp-client/Makefile.am @@ -0,0 +1,22 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_srcdir)/libs/mrcp-client/include \ + -I$(top_srcdir)/libs/mrcp-signaling/include \ + -I$(top_srcdir)/libs/mrcpv2-transport/include \ + -I$(top_srcdir)/libs/mrcp/include \ + -I$(top_srcdir)/libs/mrcp/message/include \ + -I$(top_srcdir)/libs/mrcp/control/include \ + -I$(top_srcdir)/libs/mrcp/resources/include \ + -I$(top_srcdir)/libs/mpf/include \ + -I$(top_srcdir)/libs/apr-toolkit/include \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) + +noinst_LTLIBRARIES = libmrcpclient.la + +include_HEADERS = include/mrcp_client_types.h \ + include/mrcp_client.h \ + include/mrcp_client_session.h \ + include/mrcp_application.h + +libmrcpclient_la_SOURCES = src/mrcp_client.c \ + src/mrcp_client_session.c diff --git a/libs/unimrcp/libs/mrcp-client/include/mrcp_application.h b/libs/unimrcp/libs/mrcp-client/include/mrcp_application.h new file mode 100644 index 0000000000..fbd4c425ea --- /dev/null +++ b/libs/unimrcp/libs/mrcp-client/include/mrcp_application.h @@ -0,0 +1,297 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_APPLICATION_H__ +#define __MRCP_APPLICATION_H__ + +/** + * @file mrcp_application.h + * @brief MRCP User Level Application Interface + */ + +#include "mrcp_client_types.h" +#include "mpf_rtp_descriptor.h" +#include "mpf_stream.h" + +APT_BEGIN_EXTERN_C + +/** MRCP application message declaration */ +typedef struct mrcp_app_message_t mrcp_app_message_t; + +/** MRCP signaling message declaration */ +typedef struct mrcp_sig_message_t mrcp_sig_message_t; + +/** MRCP application message dispatcher declaration */ +typedef struct mrcp_app_message_dispatcher_t mrcp_app_message_dispatcher_t; + +/** MRCP application message handler */ +typedef apt_bool_t (*mrcp_app_message_handler_f)(const mrcp_app_message_t *app_message); + +/** Enumeration of MRCP signaling message types */ +typedef enum { + MRCP_SIG_MESSAGE_TYPE_REQUEST, /**< request message */ + MRCP_SIG_MESSAGE_TYPE_RESPONSE, /**< response message */ + MRCP_SIG_MESSAGE_TYPE_EVENT /**< event message */ +} mrcp_sig_message_type_e; + +/** Enumeration of MRCP signaling status codes */ +typedef enum { + MRCP_SIG_STATUS_CODE_SUCCESS, /**< indicates success */ + MRCP_SIG_STATUS_CODE_FAILURE, /**< request failed */ + MRCP_SIG_STATUS_CODE_TERMINATE, /**< request failed, session/channel/connection unexpectedly terminated */ + MRCP_SIG_STATUS_CODE_CANCEL /**< request cancelled */ +} mrcp_sig_status_code_e; + + +/** Enumeration of MRCP signaling commands (requests/responses) */ +typedef enum { + MRCP_SIG_COMMAND_SESSION_UPDATE, + MRCP_SIG_COMMAND_SESSION_TERMINATE, + MRCP_SIG_COMMAND_CHANNEL_ADD, + MRCP_SIG_COMMAND_CHANNEL_REMOVE, + MRCP_SIG_COMMAND_RESOURCE_DISCOVER +} mrcp_sig_command_e; + +/** Enumeration of MRCP signaling events */ +typedef enum { + MRCP_SIG_EVENT_READY, + MRCP_SIG_EVENT_TERMINATE +} mrcp_sig_event_e; + + +/** Enumeration of MRCP application message types */ +typedef enum { + MRCP_APP_MESSAGE_TYPE_SIGNALING, /**< signaling message type */ + MRCP_APP_MESSAGE_TYPE_CONTROL /**< control message type */ +} mrcp_app_message_type_e; + +/** MRCP signaling message definition */ +struct mrcp_sig_message_t { + /** Message type (request/response/event) */ + mrcp_sig_message_type_e message_type; + /** Command (request/response) identifier */ + mrcp_sig_command_e command_id; + /** Event identifier */ + mrcp_sig_event_e event_id; + /** Status code used in response */ + mrcp_sig_status_code_e status; +}; + + +/** MRCP application message definition */ +struct mrcp_app_message_t { + /** Message type (signaling/control) */ + mrcp_app_message_type_e message_type; + + /** Application */ + mrcp_application_t *application; + /** Session */ + mrcp_session_t *session; + /** Channel */ + mrcp_channel_t *channel; + /** Session/resource descriptor */ + mrcp_session_descriptor_t *descriptor; + + /** MRCP signaling message (used if message_type == MRCP_APP_MESSAGE_SIGNALING) */ + mrcp_sig_message_t sig_message; + /** MRCP control message (used if message_type == MRCP_APP_MESSAGE_CONTROL) */ + mrcp_message_t *control_message; +}; + +/** MRCP application message dispatcher interface */ +struct mrcp_app_message_dispatcher_t { + /** Response to mrcp_application_session_update()request */ + apt_bool_t (*on_session_update)(mrcp_application_t *application, mrcp_session_t *session, mrcp_sig_status_code_e status); + /** Response to mrcp_application_session_terminate()request */ + apt_bool_t (*on_session_terminate)(mrcp_application_t *application, mrcp_session_t *session, mrcp_sig_status_code_e status); + + /** Response to mrcp_application_channel_add() request */ + apt_bool_t (*on_channel_add)(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_sig_status_code_e status); + /** Response to mrcp_application_channel_remove() request */ + apt_bool_t (*on_channel_remove)(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_sig_status_code_e status); + + /** Response (event) to mrcp_application_message_send() request */ + apt_bool_t (*on_message_receive)(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_message_t *message); + + /** Event indicating client stack is started and ready to process requests from the application */ + apt_bool_t (*on_ready)(mrcp_application_t *application, mrcp_sig_status_code_e status); + + /** Event indicating unexpected session/channel termination */ + apt_bool_t (*on_terminate_event)(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel); + + /** Response to mrcp_application_resource_discover() request */ + apt_bool_t (*on_resource_discover)(mrcp_application_t *application, mrcp_session_t *session, mrcp_session_descriptor_t *descriptor, mrcp_sig_status_code_e status); +}; + + + +/** + * Create application instance. + * @param handler the event handler + * @param obj the external object + * @param pool the pool to allocate memory from + */ +MRCP_DECLARE(mrcp_application_t*) mrcp_application_create(const mrcp_app_message_handler_f handler, void *obj, apr_pool_t *pool); + +/** + * Destroy application instance. + * @param application the application to destroy + */ +MRCP_DECLARE(apt_bool_t) mrcp_application_destroy(mrcp_application_t *application); + +/** + * Get external object associated with the application. + * @param application the application to get object from + */ +MRCP_DECLARE(void*) mrcp_application_object_get(mrcp_application_t *application); + +/** + * Get dir layout structure. + * @param application the application to get dir layout from + */ +MRCP_DECLARE(const apt_dir_layout_t*) mrcp_application_dir_layout_get(mrcp_application_t *application); + +/** + * Create session. + * @param application the entire application + * @param profile the name of the profile to use + * @param obj the external object + * @return the created session instance + */ +MRCP_DECLARE(mrcp_session_t*) mrcp_application_session_create(mrcp_application_t *application, const char *profile, void *obj); + +/** + * Get external object associated with the session. + * @param session the session to get object from + */ +MRCP_DECLARE(void*) mrcp_application_session_object_get(mrcp_session_t *session); + +/** + * Send session update request. + * @param session the session to update + */ +MRCP_DECLARE(apt_bool_t) mrcp_application_session_update(mrcp_session_t *session); + +/** + * Send session termination request. + * @param session the session to terminate + */ +MRCP_DECLARE(apt_bool_t) mrcp_application_session_terminate(mrcp_session_t *session); + +/** + * Destroy client session (session must be terminated prior to destroy). + * @param session the session to destroy + */ +MRCP_DECLARE(apt_bool_t) mrcp_application_session_destroy(mrcp_session_t *session); + + +/** + * Create control channel. + * @param session the session to create channel for + * @param resource_id the resource identifier of the channel + * @param termination the media termination + * @param rtp_descriptor the RTP termination descriptor (NULL by default) + * @param obj the external object + */ +MRCP_DECLARE(mrcp_channel_t*) mrcp_application_channel_create( + mrcp_session_t *session, + mrcp_resource_id resource_id, + mpf_termination_t *termination, + mpf_rtp_termination_descriptor_t *rtp_descriptor, + void *obj); + +/** + * Get external object associated with the channel. + * @param channel the channel to get object from + */ +MRCP_DECLARE(void*) mrcp_application_channel_object_get(mrcp_channel_t *channel); + +/** + * Get RTP termination descriptor. + * @param channel the channel to get descriptor from + */ +MRCP_DECLARE(mpf_rtp_termination_descriptor_t*) mrcp_application_rtp_descriptor_get(mrcp_channel_t *channel); + +/** + * Send channel add request. + * @param session the session to create channel for + * @param channel the control channel + */ +MRCP_DECLARE(apt_bool_t) mrcp_application_channel_add(mrcp_session_t *session, mrcp_channel_t *channel); + +/** + * Create MRCP message. + * @param session the session + * @param channel the control channel + * @param method_id the method identifier of MRCP message + */ +MRCP_DECLARE(mrcp_message_t*) mrcp_application_message_create(mrcp_session_t *session, mrcp_channel_t *channel, mrcp_method_id method_id); + +/** + * Send MRCP message. + * @param session the session + * @param channel the control channel + * @param message the MRCP message to send + */ +MRCP_DECLARE(apt_bool_t) mrcp_application_message_send(mrcp_session_t *session, mrcp_channel_t *channel, mrcp_message_t *message); + +/** + * Remove channel. + * @param session the session to remove channel from + * @param channel the control channel to remove + */ +MRCP_DECLARE(apt_bool_t) mrcp_application_channel_remove(mrcp_session_t *session, mrcp_channel_t *channel); + +/** + * Discover resources. + * @param session the session to use as communication object + */ +MRCP_DECLARE(apt_bool_t) mrcp_application_resource_discover(mrcp_session_t *session); + +/** + * Dispatch application message. + * @param dispatcher the dispatcher inteface + * @param app_message the message to dispatch + */ +MRCP_DECLARE(apt_bool_t) mrcp_application_message_dispatch(const mrcp_app_message_dispatcher_t *dispatcher, const mrcp_app_message_t *app_message); + + +/** Create source media termination + * @param session the session to create channel for + * @param stream_vtable the virtual table of audio stream + * @param codec_descriptor the descriptor of audio stream (NULL by default) + * @param obj the external object + */ +MRCP_DECLARE(mpf_termination_t*) mrcp_application_source_termination_create( + mrcp_session_t *session, + const mpf_audio_stream_vtable_t *stream_vtable, + mpf_codec_descriptor_t *codec_descriptor, + void *obj); +/** Create sink media termination + * @param session the session to create channel for + * @param stream_vtable the virtual table of audio stream + * @param codec_descriptor the descriptor of audio stream (NULL by default) + * @param obj the external object + */ +MRCP_DECLARE(mpf_termination_t*) mrcp_application_sink_termination_create( + mrcp_session_t *session, + const mpf_audio_stream_vtable_t *stream_vtable, + mpf_codec_descriptor_t *codec_descriptor, + void *obj); + +APT_END_EXTERN_C + +#endif /*__MRCP_APPLICATION_H__*/ diff --git a/libs/unimrcp/libs/mrcp-client/include/mrcp_client.h b/libs/unimrcp/libs/mrcp-client/include/mrcp_client.h new file mode 100644 index 0000000000..e155b7653e --- /dev/null +++ b/libs/unimrcp/libs/mrcp-client/include/mrcp_client.h @@ -0,0 +1,176 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_CLIENT_H__ +#define __MRCP_CLIENT_H__ + +/** + * @file mrcp_client.h + * @brief MRCP Client + */ + +#include "mrcp_client_types.h" +#include "apt_task.h" + +APT_BEGIN_EXTERN_C + +/** + * Create MRCP client instance. + * @return the created client instance + */ +MRCP_DECLARE(mrcp_client_t*) mrcp_client_create(apt_dir_layout_t *dir_layout); + +/** + * Start message processing loop. + * @param client the MRCP client to start + * @return the created client instance + */ +MRCP_DECLARE(apt_bool_t) mrcp_client_start(mrcp_client_t *client); + +/** + * Shutdown message processing loop. + * @param client the MRCP client to shutdown + */ +MRCP_DECLARE(apt_bool_t) mrcp_client_shutdown(mrcp_client_t *client); + +/** + * Destroy MRCP client. + * @param client the MRCP client to destroy + */ +MRCP_DECLARE(apt_bool_t) mrcp_client_destroy(mrcp_client_t *client); + + +/** + * Register MRCP resource factory. + * @param client the MRCP client to set resource factory for + * @param resource_factory the resource factory to set + */ +MRCP_DECLARE(apt_bool_t) mrcp_client_resource_factory_register(mrcp_client_t *client, mrcp_resource_factory_t *resource_factory); + +/** + * Register codec manager. + * @param client the MRCP client to set codec manager for + * @param codec_manager the codec manager to set + */ +MRCP_DECLARE(apt_bool_t) mrcp_client_codec_manager_register(mrcp_client_t *client, mpf_codec_manager_t *codec_manager); + +/** + * Get registered codec manager. + * @param client the MRCP client to get codec manager from + */ +MRCP_DECLARE(const mpf_codec_manager_t*) mrcp_client_codec_manager_get(mrcp_client_t *client); + +/** + * Register media engine. + * @param client the MRCP client to set media engine for + * @param media_engine the media engine to set + * @param name the name of the media engine + */ +MRCP_DECLARE(apt_bool_t) mrcp_client_media_engine_register(mrcp_client_t *client, mpf_engine_t *media_engine, const char *name); + +/** + * Register RTP termination factory. + * @param client the MRCP client to set termination factory for + * @param rtp_termination_factory the termination factory + * @param name the name of the factory + */ +MRCP_DECLARE(apt_bool_t) mrcp_client_rtp_factory_register(mrcp_client_t *client, mpf_termination_factory_t *rtp_termination_factory, const char *name); + +/** + * Register MRCP signaling agent. + * @param client the MRCP client to set signaling agent for + * @param signaling_agent the signaling agent to set + * @param name the name of the agent + */ +MRCP_DECLARE(apt_bool_t) mrcp_client_signaling_agent_register(mrcp_client_t *client, mrcp_sig_agent_t *signaling_agent, const char *name); + +/** + * Register MRCP connection agent (MRCPv2 only). + * @param client the MRCP client to set connection agent for + * @param connection_agent the connection agent to set + * @param name the name of the agent + */ +MRCP_DECLARE(apt_bool_t) mrcp_client_connection_agent_register(mrcp_client_t *client, mrcp_connection_agent_t *connection_agent, const char *name); + +/** Create MRCP profile */ +MRCP_DECLARE(mrcp_profile_t*) mrcp_client_profile_create( + mrcp_resource_factory_t *resource_factory, + mrcp_sig_agent_t *signaling_agent, + mrcp_connection_agent_t *connection_agent, + mpf_engine_t *media_engine, + mpf_termination_factory_t *rtp_factory, + apr_pool_t *pool); + +/** + * Register MRCP profile. + * @param client the MRCP client to set profile for + * @param profile the profile to set + * @param name the name of the profile + */ +MRCP_DECLARE(apt_bool_t) mrcp_client_profile_register(mrcp_client_t *client, mrcp_profile_t *profile, const char *name); + +/** + * Register MRCP application. + * @param client the MRCP client to set application for + * @param application the application to set + * @param name the name of the application + */ +MRCP_DECLARE(apt_bool_t) mrcp_client_application_register(mrcp_client_t *client, mrcp_application_t *application, const char *name); + +/** + * Get memory pool. + * @param client the MRCP client to get memory pool from + */ +MRCP_DECLARE(apr_pool_t*) mrcp_client_memory_pool_get(mrcp_client_t *client); + +/** + * Get media engine by name. + * @param client the MRCP client to get media engine from + * @param name the name of the media engine to lookup + */ +MRCP_DECLARE(mpf_engine_t*) mrcp_client_media_engine_get(mrcp_client_t *client, const char *name); + +/** + * Get RTP termination factory by name. + * @param client the MRCP client to get from + * @param name the name to lookup + */ +MRCP_DECLARE(mpf_termination_factory_t*) mrcp_client_rtp_factory_get(mrcp_client_t *client, const char *name); + +/** + * Get signaling agent by name. + * @param client the MRCP client to get from + * @param name the name to lookup + */ +MRCP_DECLARE(mrcp_sig_agent_t*) mrcp_client_signaling_agent_get(mrcp_client_t *client, const char *name); + +/** + * Get connection agent by name. + * @param client the MRCP client to get from + * @param name the name to lookup + */ +MRCP_DECLARE(mrcp_connection_agent_t*) mrcp_client_connection_agent_get(mrcp_client_t *client, const char *name); + +/** + * Get profile by name. + * @param client the MRCP client to get from + * @param name the name to lookup + */ +MRCP_DECLARE(mrcp_profile_t*) mrcp_client_profile_get(mrcp_client_t *client, const char *name); + +APT_END_EXTERN_C + +#endif /*__MRCP_CLIENT_H__*/ diff --git a/libs/unimrcp/libs/mrcp-client/include/mrcp_client_session.h b/libs/unimrcp/libs/mrcp-client/include/mrcp_client_session.h new file mode 100644 index 0000000000..c12820119d --- /dev/null +++ b/libs/unimrcp/libs/mrcp-client/include/mrcp_client_session.h @@ -0,0 +1,193 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_CLIENT_SESSION_H__ +#define __MRCP_CLIENT_SESSION_H__ + +/** + * @file mrcp_client_session.h + * @brief MRCP Client Session + */ + +#include "mrcp_client_types.h" +#include "mrcp_application.h" +#include "mrcp_session.h" +#include "mpf_message.h" +#include "apt_task_msg.h" +#include "apt_obj_list.h" + +APT_BEGIN_EXTERN_C + +/** RTP termination slot declaration */ +typedef struct rtp_termination_slot_t rtp_termination_slot_t; + +/** MRCP client session declaration */ +typedef struct mrcp_client_session_t mrcp_client_session_t; + +/** MRCP client session */ +struct mrcp_client_session_t { + /** Session base */ + mrcp_session_t base; + /** Application session belongs to */ + mrcp_application_t *application; + /** External object associated with session */ + void *app_obj; + /** Profile to use */ + mrcp_profile_t *profile; + + /** Media context */ + mpf_context_t *context; + /** Codec manager */ + const mpf_codec_manager_t *codec_manager; + + + /** RTP termination array (mrcp_termination_slot_t) */ + apr_array_header_t *terminations; + /** MRCP control channel array (mrcp_channel_t*) */ + apr_array_header_t *channels; + + /** Indicates whether session is already added to session table */ + apt_bool_t registered; + + /** In-progress offer */ + mrcp_session_descriptor_t *offer; + /** In-progress answer */ + mrcp_session_descriptor_t *answer; + + /** MRCP application active request */ + const mrcp_app_message_t *active_request; + /** MRCP application request queue */ + apt_obj_list_t *request_queue; + + /** Number of in-progress offer requests (flags) */ + apr_size_t offer_flag_count; + /** Number of in-progress answer requests (flags) */ + apr_size_t answer_flag_count; + /** Number of in-progress terminate requests (flags) */ + apr_size_t terminate_flag_count; +}; + +/** MRCP channel */ +struct mrcp_channel_t { + /** Memory pool */ + apr_pool_t *pool; + /** External object associated with channel */ + void *obj; + /** MRCP resource identifier */ + mrcp_resource_id resource_id; + /** MRCP resource name */ + const apt_str_t *resource_name; + /** MRCP resource */ + mrcp_resource_t *resource; + /** MRCP session entire channel belongs to */ + mrcp_session_t *session; + /** MRCP control channel */ + mrcp_control_channel_t *control_channel; + /** Media termination */ + mpf_termination_t *termination; + /** Associated RTP termination slot */ + rtp_termination_slot_t *rtp_termination_slot; + + /** waiting state of control channel */ + apt_bool_t waiting_for_channel; + /** waiting state of media termination */ + apt_bool_t waiting_for_termination; +}; + +/** RTP termination slot */ +struct rtp_termination_slot_t { + /** waiting state */ + apt_bool_t waiting; + /** RTP termination */ + mpf_termination_t *termination; + /** RTP termination descriptor */ + mpf_rtp_termination_descriptor_t *descriptor; +}; + + +/** MRCP profile */ +struct mrcp_profile_t { + /** MRCP resource factory */ + mrcp_resource_factory_t *resource_factory; + /** Media processing engine */ + mpf_engine_t *media_engine; + /** RTP termination factory */ + mpf_termination_factory_t *rtp_termination_factory; + /** Signaling agent */ + mrcp_sig_agent_t *signaling_agent; + /** Connection agent */ + mrcp_connection_agent_t *connection_agent; +}; + +/** MRCP application */ +struct mrcp_application_t { + /** External object associated with the application */ + void *obj; + /** Application message handler */ + mrcp_app_message_handler_f handler; + /** MRCP client */ + mrcp_client_t *client; + /** Application task message pool */ + apt_task_msg_pool_t *msg_pool; +}; + +/** Create client session */ +mrcp_client_session_t* mrcp_client_session_create(mrcp_application_t *application, void *obj); +/** Create channel */ +mrcp_channel_t* mrcp_client_channel_create( + mrcp_session_t *session, + mrcp_resource_id resource_id, + mpf_termination_t *termination, + mpf_rtp_termination_descriptor_t *rtp_descriptor, + void *obj); + +/** Create signaling app_message_t request */ +mrcp_app_message_t* mrcp_client_app_signaling_request_create(mrcp_sig_command_e command_id, apr_pool_t *pool); +/** Create signaling app_message_t event */ +mrcp_app_message_t* mrcp_client_app_signaling_event_create(mrcp_sig_event_e event_id, apr_pool_t *pool); +/** Create control app_message_t */ +mrcp_app_message_t* mrcp_client_app_control_message_create(apr_pool_t *pool); +/** Create response to app_message_t request */ +mrcp_app_message_t* mrcp_client_app_response_create(const mrcp_app_message_t *app_request, mrcp_sig_status_code_e status, apr_pool_t *pool); + +/** Process application message */ +apt_bool_t mrcp_client_app_message_process(mrcp_app_message_t *app_message); +/** Process MPF message */ +apt_bool_t mrcp_client_mpf_message_process(mpf_message_t *mpf_message); + +/** Process session answer */ +apt_bool_t mrcp_client_session_answer_process(mrcp_client_session_t *session, mrcp_session_descriptor_t *descriptor); +/** Process session termination response */ +apt_bool_t mrcp_client_session_terminate_response_process(mrcp_client_session_t *session); +/** Process session control response */ +apt_bool_t mrcp_client_session_control_response_process(mrcp_client_session_t *session, mrcp_message_t *message); +/** Process resource discovery response */ +apt_bool_t mrcp_client_session_discover_response_process(mrcp_client_session_t *session, mrcp_session_descriptor_t *descriptor); +/** Process session termination event */ +apt_bool_t mrcp_client_session_terminate_event_process(mrcp_client_session_t *session); + +/** Process channel add event */ +apt_bool_t mrcp_client_on_channel_add(mrcp_channel_t *channel, mrcp_control_descriptor_t *descriptor, apt_bool_t status); +/** Process channel modify event */ +apt_bool_t mrcp_client_on_channel_modify(mrcp_channel_t *channel, mrcp_control_descriptor_t *descriptor, apt_bool_t status); +/** Process channel remove event */ +apt_bool_t mrcp_client_on_channel_remove(mrcp_channel_t *channel, apt_bool_t status); +/** Process message receive event */ +apt_bool_t mrcp_client_on_message_receive(mrcp_channel_t *channel, mrcp_message_t *message); + +APT_END_EXTERN_C + +#endif /*__MRCP_CLIENT_SESSION_H__*/ diff --git a/libs/unimrcp/libs/mrcp-client/include/mrcp_client_types.h b/libs/unimrcp/libs/mrcp-client/include/mrcp_client_types.h new file mode 100644 index 0000000000..4da19f0081 --- /dev/null +++ b/libs/unimrcp/libs/mrcp-client/include/mrcp_client_types.h @@ -0,0 +1,46 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_CLIENT_TYPES_H__ +#define __MRCP_CLIENT_TYPES_H__ + +/** + * @file mrcp_client_types.h + * @brief MRCP Client Types + */ + +#include "mrcp_sig_types.h" +#include "mrcp_connection_types.h" +#include "mpf_types.h" + +APT_BEGIN_EXTERN_C + +/** Opaque MRCP client declaration */ +typedef struct mrcp_client_t mrcp_client_t; + +/** Opaque MRCP profile declaration */ +typedef struct mrcp_profile_t mrcp_profile_t; + +/** Opaque MRCP application declaration */ +typedef struct mrcp_application_t mrcp_application_t; + +/** Opaque MRCP channel declaration */ +typedef struct mrcp_channel_t mrcp_channel_t; + + +APT_END_EXTERN_C + +#endif /*__MRCP_CLIENT_TYPES_H__*/ diff --git a/libs/unimrcp/libs/mrcp-client/mrcpclient.vcproj b/libs/unimrcp/libs/mrcp-client/mrcpclient.vcproj new file mode 100644 index 0000000000..ed211a043a --- /dev/null +++ b/libs/unimrcp/libs/mrcp-client/mrcpclient.vcproj @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/libs/mrcp-client/src/mrcp_client.c b/libs/unimrcp/libs/mrcp-client/src/mrcp_client.c new file mode 100644 index 0000000000..e2e303fc1e --- /dev/null +++ b/libs/unimrcp/libs/mrcp-client/src/mrcp_client.c @@ -0,0 +1,983 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include "mrcp_client.h" +#include "mrcp_resource_factory.h" +#include "mrcp_sig_agent.h" +#include "mrcp_client_session.h" +#include "mrcp_client_connection.h" +#include "mrcp_message.h" +#include "mpf_engine.h" +#include "mpf_termination.h" +#include "mpf_codec_manager.h" +#include "apt_pool.h" +#include "apt_consumer_task.h" +#include "apt_log.h" + +#define CLIENT_TASK_NAME "MRCP Client" + +/** MRCP client */ +struct mrcp_client_t { + /** Main message processing task */ + apt_consumer_task_t *task; + + /** MRCP resource factory */ + mrcp_resource_factory_t *resource_factory; + /** Codec manager */ + mpf_codec_manager_t *codec_manager; + /** Table of media processing engines (mpf_engine_t*) */ + apr_hash_t *media_engine_table; + /** Table of RTP termination factories (mpf_termination_factory_t*) */ + apr_hash_t *rtp_factory_table; + /** Table of signaling agents (mrcp_sig_agent_t*) */ + apr_hash_t *sig_agent_table; + /** Table of connection agents (mrcp_connection_agent_t*) */ + apr_hash_t *cnt_agent_table; + /** Table of profiles (mrcp_profile_t*) */ + apr_hash_t *profile_table; + + /** Table of applications (mrcp_application_t*) */ + apr_hash_t *app_table; + + /** Table of sessions/handles */ + apr_hash_t *session_table; + + /** Connection task message pool */ + apt_task_msg_pool_t *cnt_msg_pool; + /** Dir layout structure */ + apt_dir_layout_t *dir_layout; + /** Memory pool */ + apr_pool_t *pool; +}; + + +typedef enum { + MRCP_CLIENT_SIGNALING_TASK_MSG = TASK_MSG_USER, + MRCP_CLIENT_CONNECTION_TASK_MSG, + MRCP_CLIENT_MEDIA_TASK_MSG, + MRCP_CLIENT_APPLICATION_TASK_MSG +} mrcp_client_task_msg_type_e; + +/* Signaling agent interface */ +typedef enum { + SIG_AGENT_TASK_MSG_ANSWER, + SIG_AGENT_TASK_MSG_TERMINATE_RESPONSE, + SIG_AGENT_TASK_MSG_CONTROL_RESPONSE, + SIG_AGENT_TASK_MSG_DISCOVER_RESPONSE, + SIG_AGENT_TASK_MSG_TERMINATE_EVENT +} sig_agent_task_msg_type_e; + +typedef struct sig_agent_task_msg_data_t sig_agent_task_msg_data_t; +struct sig_agent_task_msg_data_t { + mrcp_client_session_t *session; + mrcp_session_descriptor_t *descriptor; + mrcp_message_t *message; +}; + +static apt_bool_t mrcp_client_answer_signal(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor); +static apt_bool_t mrcp_client_terminate_response_signal(mrcp_session_t *session); +static apt_bool_t mrcp_client_control_response_signal(mrcp_session_t *session, mrcp_message_t *message); +static apt_bool_t mrcp_client_discover_response_signal(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor); + +static apt_bool_t mrcp_client_terminate_event_signal(mrcp_session_t *session); + +static const mrcp_session_response_vtable_t session_response_vtable = { + mrcp_client_answer_signal, + mrcp_client_terminate_response_signal, + mrcp_client_control_response_signal, + mrcp_client_discover_response_signal +}; + +static const mrcp_session_event_vtable_t session_event_vtable = { + mrcp_client_terminate_event_signal +}; + +/* Connection agent interface */ +typedef enum { + CONNECTION_AGENT_TASK_MSG_ADD_CHANNEL, + CONNECTION_AGENT_TASK_MSG_MODIFY_CHANNEL, + CONNECTION_AGENT_TASK_MSG_REMOVE_CHANNEL, + CONNECTION_AGENT_TASK_MSG_RECEIVE_MESSAGE, +} connection_agent_task_msg_type_e ; + +typedef struct connection_agent_task_msg_data_t connection_agent_task_msg_data_t; +struct connection_agent_task_msg_data_t { + mrcp_channel_t *channel; + mrcp_control_descriptor_t *descriptor; + mrcp_message_t *message; + apt_bool_t status; +}; + +static apt_bool_t mrcp_client_channel_add_signal(mrcp_control_channel_t *channel, mrcp_control_descriptor_t *descriptor, apt_bool_t status); +static apt_bool_t mrcp_client_channel_modify_signal(mrcp_control_channel_t *channel, mrcp_control_descriptor_t *descriptor, apt_bool_t status); +static apt_bool_t mrcp_client_channel_remove_signal(mrcp_control_channel_t *channel, apt_bool_t status); +static apt_bool_t mrcp_client_message_signal(mrcp_control_channel_t *channel, mrcp_message_t *message); + +static const mrcp_connection_event_vtable_t connection_method_vtable = { + mrcp_client_channel_add_signal, + mrcp_client_channel_modify_signal, + mrcp_client_channel_remove_signal, + mrcp_client_message_signal +}; + +/* Task interface */ +static void mrcp_client_on_start_complete(apt_task_t *task); +static void mrcp_client_on_terminate_complete(apt_task_t *task); +static apt_bool_t mrcp_client_msg_process(apt_task_t *task, apt_task_msg_t *msg); +static apt_bool_t mrcp_app_signaling_task_msg_signal(mrcp_sig_command_e command_id, mrcp_session_t *session, mrcp_channel_t *channel); +static apt_bool_t mrcp_app_control_task_msg_signal(mrcp_session_t *session, mrcp_channel_t *channel, mrcp_message_t *message); + + +/** Create MRCP client instance */ +MRCP_DECLARE(mrcp_client_t*) mrcp_client_create(apt_dir_layout_t *dir_layout) +{ + mrcp_client_t *client; + apr_pool_t *pool; + apt_task_t *task; + apt_task_vtable_t *vtable; + apt_task_msg_pool_t *msg_pool; + + pool = apt_pool_create(); + if(!pool) { + return NULL; + } + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create "CLIENT_TASK_NAME); + client = apr_palloc(pool,sizeof(mrcp_client_t)); + client->pool = pool; + client->dir_layout = dir_layout; + client->resource_factory = NULL; + client->media_engine_table = NULL; + client->rtp_factory_table = NULL; + client->sig_agent_table = NULL; + client->cnt_agent_table = NULL; + client->profile_table = NULL; + client->app_table = NULL; + client->session_table = NULL; + client->cnt_msg_pool = NULL; + + msg_pool = apt_task_msg_pool_create_dynamic(0,pool); + client->task = apt_consumer_task_create(client,msg_pool,pool); + if(!client->task) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Client Task"); + return NULL; + } + task = apt_consumer_task_base_get(client->task); + apt_task_name_set(task,CLIENT_TASK_NAME); + vtable = apt_task_vtable_get(task); + if(vtable) { + vtable->process_msg = mrcp_client_msg_process; + vtable->on_start_complete = mrcp_client_on_start_complete; + vtable->on_terminate_complete = mrcp_client_on_terminate_complete; + } + + client->media_engine_table = apr_hash_make(client->pool); + client->rtp_factory_table = apr_hash_make(client->pool); + client->sig_agent_table = apr_hash_make(client->pool); + client->cnt_agent_table = apr_hash_make(client->pool); + client->profile_table = apr_hash_make(client->pool); + client->app_table = apr_hash_make(client->pool); + + client->session_table = apr_hash_make(client->pool); + return client; +} + +/** Start message processing loop */ +MRCP_DECLARE(apt_bool_t) mrcp_client_start(mrcp_client_t *client) +{ + apt_task_t *task; + if(!client || !client->task) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Invalid Client"); + return FALSE; + } + task = apt_consumer_task_base_get(client->task); + if(apt_task_start(task) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Start Client Task"); + return FALSE; + } + return TRUE; +} + +/** Shutdown message processing loop */ +MRCP_DECLARE(apt_bool_t) mrcp_client_shutdown(mrcp_client_t *client) +{ + apt_task_t *task; + if(!client || !client->task) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Invalid Client"); + return FALSE; + } + task = apt_consumer_task_base_get(client->task); + if(apt_task_terminate(task,TRUE) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Shutdown Client Task"); + return FALSE; + } + client->session_table = NULL; + return TRUE; +} + +/** Destroy MRCP client */ +MRCP_DECLARE(apt_bool_t) mrcp_client_destroy(mrcp_client_t *client) +{ + apt_task_t *task; + if(!client || !client->task) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Invalid Client"); + return FALSE; + } + task = apt_consumer_task_base_get(client->task); + apt_task_destroy(task); + + apr_pool_destroy(client->pool); + return TRUE; +} + + +/** Register MRCP resource factory */ +MRCP_DECLARE(apt_bool_t) mrcp_client_resource_factory_register(mrcp_client_t *client, mrcp_resource_factory_t *resource_factory) +{ + if(!resource_factory) { + return FALSE; + } + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Register Resource Factory"); + client->resource_factory = resource_factory; + return TRUE; +} + +/** Register codec manager */ +MRCP_DECLARE(apt_bool_t) mrcp_client_codec_manager_register(mrcp_client_t *client, mpf_codec_manager_t *codec_manager) +{ + if(!codec_manager) { + return FALSE; + } + client->codec_manager = codec_manager; + return TRUE; +} + +/** Get registered codec manager */ +MRCP_DECLARE(const mpf_codec_manager_t*) mrcp_client_codec_manager_get(mrcp_client_t *client) +{ + return client->codec_manager; +} + +/** Register media engine */ +MRCP_DECLARE(apt_bool_t) mrcp_client_media_engine_register(mrcp_client_t *client, mpf_engine_t *media_engine, const char *name) +{ + if(!media_engine || !name) { + return FALSE; + } + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Register Media Engine [%s]",name); + mpf_engine_codec_manager_register(media_engine,client->codec_manager); + apr_hash_set(client->media_engine_table,name,APR_HASH_KEY_STRING,media_engine); + mpf_engine_task_msg_type_set(media_engine,MRCP_CLIENT_MEDIA_TASK_MSG); + if(client->task) { + apt_task_t *media_task = mpf_task_get(media_engine); + apt_task_t *task = apt_consumer_task_base_get(client->task); + apt_task_add(task,media_task); + } + return TRUE; +} + +/** Get media engine by name */ +MRCP_DECLARE(mpf_engine_t*) mrcp_client_media_engine_get(mrcp_client_t *client, const char *name) +{ + return apr_hash_get(client->media_engine_table,name,APR_HASH_KEY_STRING); +} + +/** Register RTP termination factory */ +MRCP_DECLARE(apt_bool_t) mrcp_client_rtp_factory_register(mrcp_client_t *client, mpf_termination_factory_t *rtp_termination_factory, const char *name) +{ + if(!rtp_termination_factory || !name) { + return FALSE; + } + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Register RTP Termination Factory [%s]",name); + apr_hash_set(client->rtp_factory_table,name,APR_HASH_KEY_STRING,rtp_termination_factory); + return TRUE; +} + +/** Get RTP termination factory by name */ +MRCP_DECLARE(mpf_termination_factory_t*) mrcp_client_rtp_factory_get(mrcp_client_t *client, const char *name) +{ + return apr_hash_get(client->rtp_factory_table,name,APR_HASH_KEY_STRING); +} + +/** Register MRCP signaling agent */ +MRCP_DECLARE(apt_bool_t) mrcp_client_signaling_agent_register(mrcp_client_t *client, mrcp_sig_agent_t *signaling_agent, const char *name) +{ + if(!signaling_agent || !name) { + return FALSE; + } + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Register Signaling Agent [%s]",name); + signaling_agent->msg_pool = apt_task_msg_pool_create_dynamic(sizeof(sig_agent_task_msg_data_t),client->pool); + signaling_agent->parent = client; + signaling_agent->resource_factory = client->resource_factory; + apr_hash_set(client->sig_agent_table,name,APR_HASH_KEY_STRING,signaling_agent); + if(client->task) { + apt_task_t *task = apt_consumer_task_base_get(client->task); + apt_task_add(task,signaling_agent->task); + } + return TRUE; +} + +/** Get signaling agent by name */ +MRCP_DECLARE(mrcp_sig_agent_t*) mrcp_client_signaling_agent_get(mrcp_client_t *client, const char *name) +{ + return apr_hash_get(client->sig_agent_table,name,APR_HASH_KEY_STRING); +} + +/** Register MRCP connection agent (MRCPv2 only) */ +MRCP_DECLARE(apt_bool_t) mrcp_client_connection_agent_register(mrcp_client_t *client, mrcp_connection_agent_t *connection_agent, const char *name) +{ + if(!connection_agent || !name) { + return FALSE; + } + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Register Connection Agent [%s]",name); + mrcp_client_connection_resource_factory_set(connection_agent,client->resource_factory); + mrcp_client_connection_agent_handler_set(connection_agent,client,&connection_method_vtable); + client->cnt_msg_pool = apt_task_msg_pool_create_dynamic(sizeof(connection_agent_task_msg_data_t),client->pool); + apr_hash_set(client->cnt_agent_table,name,APR_HASH_KEY_STRING,connection_agent); + if(client->task) { + apt_task_t *task = apt_consumer_task_base_get(client->task); + apt_task_t *connection_task = mrcp_client_connection_agent_task_get(connection_agent); + apt_task_add(task,connection_task); + } + return TRUE; +} + +/** Get connection agent by name */ +MRCP_DECLARE(mrcp_connection_agent_t*) mrcp_client_connection_agent_get(mrcp_client_t *client, const char *name) +{ + return apr_hash_get(client->cnt_agent_table,name,APR_HASH_KEY_STRING); +} + +/** Create MRCP profile */ +MRCP_DECLARE(mrcp_profile_t*) mrcp_client_profile_create( + mrcp_resource_factory_t *resource_factory, + mrcp_sig_agent_t *signaling_agent, + mrcp_connection_agent_t *connection_agent, + mpf_engine_t *media_engine, + mpf_termination_factory_t *rtp_factory, + apr_pool_t *pool) +{ + mrcp_profile_t *profile = apr_palloc(pool,sizeof(mrcp_profile_t)); + profile->resource_factory = resource_factory; + profile->media_engine = media_engine; + profile->rtp_termination_factory = rtp_factory; + profile->signaling_agent = signaling_agent; + profile->connection_agent = connection_agent; + return profile; +} + +/** Register MRCP profile */ +MRCP_DECLARE(apt_bool_t) mrcp_client_profile_register(mrcp_client_t *client, mrcp_profile_t *profile, const char *name) +{ + if(!profile || !name) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Register Profile: no name",name); + return FALSE; + } + if(!profile->resource_factory) { + profile->resource_factory = client->resource_factory; + } + if(!profile->signaling_agent) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Register Profile [%s]: missing signaling agent",name); + return FALSE; + } + if(profile->signaling_agent->mrcp_version == MRCP_VERSION_2 && + !profile->connection_agent) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Register Profile [%s]: missing connection agent",name); + return FALSE; + } + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Register Profile [%s]",name); + apr_hash_set(client->profile_table,name,APR_HASH_KEY_STRING,profile); + return TRUE; +} + +/** Get profile by name */ +MRCP_DECLARE(mrcp_profile_t*) mrcp_client_profile_get(mrcp_client_t *client, const char *name) +{ + return apr_hash_get(client->profile_table,name,APR_HASH_KEY_STRING); +} + +/** Register MRCP application */ +MRCP_DECLARE(apt_bool_t) mrcp_client_application_register(mrcp_client_t *client, mrcp_application_t *application, const char *name) +{ + if(!application || !name) { + return FALSE; + } + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Register Application [%s]",name); + application->client = client; + application->msg_pool = apt_task_msg_pool_create_dynamic(sizeof(mrcp_app_message_t*),client->pool); + apr_hash_set(client->app_table,name,APR_HASH_KEY_STRING,application); + return TRUE; +} + +/** Get memory pool */ +MRCP_DECLARE(apr_pool_t*) mrcp_client_memory_pool_get(mrcp_client_t *client) +{ + return client->pool; +} + + +/** Create application instance */ +MRCP_DECLARE(mrcp_application_t*) mrcp_application_create(const mrcp_app_message_handler_f handler, void *obj, apr_pool_t *pool) +{ + mrcp_application_t *application; + if(!handler) { + return FALSE; + } + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create Application"); + application = apr_palloc(pool,sizeof(mrcp_application_t)); + application->obj = obj; + application->handler = handler; + application->client = NULL; + return application; +} + +/** Destroy application instance */ +MRCP_DECLARE(apt_bool_t) mrcp_application_destroy(mrcp_application_t *application) +{ + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Destroy Application"); + return TRUE; +} + +/** Get external object associated with the application */ +MRCP_DECLARE(void*) mrcp_application_object_get(mrcp_application_t *application) +{ + return application->obj; +} + +/** Get dir layout structure */ +MRCP_DECLARE(const apt_dir_layout_t*) mrcp_application_dir_layout_get(mrcp_application_t *application) +{ + return application->client->dir_layout; +} + + + +/** Create client session */ +MRCP_DECLARE(mrcp_session_t*) mrcp_application_session_create(mrcp_application_t *application, const char *profile_name, void *obj) +{ + mrcp_profile_t *profile; + mrcp_client_session_t *session; + if(!application || !application->client) { + return NULL; + } + + profile = mrcp_client_profile_get(application->client,profile_name); + if(!profile) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No Such Profile [%s]",profile_name); + return NULL; + } + + session = mrcp_client_session_create(application,obj); + if(!session) { + return NULL; + } + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create MRCP Handle "APT_PTR_FMT" [%s]", + MRCP_SESSION_PTR(session), + profile_name); + session->profile = profile; + session->codec_manager = application->client->codec_manager; + session->base.response_vtable = &session_response_vtable; + session->base.event_vtable = &session_event_vtable; + return &session->base; +} + +/** Get external object associated with the session */ +MRCP_DECLARE(void*) mrcp_application_session_object_get(mrcp_session_t *session) +{ + mrcp_client_session_t *client_session = (mrcp_client_session_t*)session; + if(!client_session) { + return NULL; + } + return client_session->app_obj; +} + + +/** Send session update request */ +MRCP_DECLARE(apt_bool_t) mrcp_application_session_update(mrcp_session_t *session) +{ + if(!session) { + return FALSE; + } + return mrcp_app_signaling_task_msg_signal(MRCP_SIG_COMMAND_SESSION_UPDATE,session,NULL); +} + +/** Send session termination request */ +MRCP_DECLARE(apt_bool_t) mrcp_application_session_terminate(mrcp_session_t *session) +{ + if(!session) { + return FALSE; + } + return mrcp_app_signaling_task_msg_signal(MRCP_SIG_COMMAND_SESSION_TERMINATE,session,NULL); +} + +/** Destroy client session (session must be terminated prior to destroy) */ +MRCP_DECLARE(apt_bool_t) mrcp_application_session_destroy(mrcp_session_t *session) +{ + if(!session) { + return FALSE; + } + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Destroy MRCP Handle "APT_PTR_FMT,MRCP_SESSION_PTR(session)); + mrcp_session_destroy(session); + return TRUE; +} + + +/** Create control channel */ +MRCP_DECLARE(mrcp_channel_t*) mrcp_application_channel_create( + mrcp_session_t *session, + mrcp_resource_id resource_id, + mpf_termination_t *termination, + mpf_rtp_termination_descriptor_t *rtp_descriptor, + void *obj) +{ + mrcp_client_session_t *client_session = (mrcp_client_session_t*)session; + if(!client_session || !client_session->profile) { + /* Invalid params */ + return FALSE; + } + if(termination) { + /* Media engine and RTP factory must be specified in this case */ + if(!client_session->profile->media_engine || !client_session->profile->rtp_termination_factory) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Channel: invalid profile"); + return FALSE; + } + } + else { + /* Either termination or rtp_descriptor must be specified */ + if(!rtp_descriptor) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Channel: missing both termination and RTP descriptor"); + return FALSE; + } + } + return mrcp_client_channel_create(session,resource_id,termination,rtp_descriptor,obj); +} + +/** Get external object associated with the channel */ +MRCP_DECLARE(void*) mrcp_application_channel_object_get(mrcp_channel_t *channel) +{ + if(!channel) { + return FALSE; + } + return channel->obj; +} + +/** Get RTP termination descriptor */ +MRCP_DECLARE(mpf_rtp_termination_descriptor_t*) mrcp_application_rtp_descriptor_get(mrcp_channel_t *channel) +{ + if(!channel || !channel->rtp_termination_slot) { + return FALSE; + } + return channel->rtp_termination_slot->descriptor; +} + +/** Send channel add request */ +MRCP_DECLARE(apt_bool_t) mrcp_application_channel_add(mrcp_session_t *session, mrcp_channel_t *channel) +{ + if(!session || !channel) { + return FALSE; + } + return mrcp_app_signaling_task_msg_signal(MRCP_SIG_COMMAND_CHANNEL_ADD,session,channel); +} + +/** Send channel removal request */ +MRCP_DECLARE(apt_bool_t) mrcp_application_channel_remove(mrcp_session_t *session, mrcp_channel_t *channel) +{ + if(!session || !channel) { + return FALSE; + } + return mrcp_app_signaling_task_msg_signal(MRCP_SIG_COMMAND_CHANNEL_REMOVE,session,channel); +} + +/** Send resource discovery request */ +MRCP_DECLARE(apt_bool_t) mrcp_application_resource_discover(mrcp_session_t *session) +{ + if(!session) { + return FALSE; + } + return mrcp_app_signaling_task_msg_signal(MRCP_SIG_COMMAND_RESOURCE_DISCOVER,session,NULL); +} + +/** Create MRCP message */ +MRCP_DECLARE(mrcp_message_t*) mrcp_application_message_create(mrcp_session_t *session, mrcp_channel_t *channel, mrcp_method_id method_id) +{ + mrcp_message_t *mrcp_message; + mrcp_profile_t *profile; + mrcp_client_session_t *client_session = (mrcp_client_session_t*)session; + if(!client_session || !channel) { + return NULL; + } + profile = client_session->profile; + if(!profile || !profile->resource_factory) { + return NULL; + } + mrcp_message = mrcp_request_create(channel->resource_id,method_id,session->pool); + if(mrcp_message) { + mrcp_message->start_line.version = profile->signaling_agent->mrcp_version; + mrcp_message_resourcify_by_id(profile->resource_factory,mrcp_message); + } + return mrcp_message; +} + +/** Send MRCP message */ +MRCP_DECLARE(apt_bool_t) mrcp_application_message_send(mrcp_session_t *session, mrcp_channel_t *channel, mrcp_message_t *message) +{ + if(!session || !channel || !message) { + return FALSE; + } + return mrcp_app_control_task_msg_signal(session,channel,message); +} + +/** Create source media termination */ +MRCP_DECLARE(mpf_termination_t*) mrcp_application_source_termination_create( + mrcp_session_t *session, + const mpf_audio_stream_vtable_t *stream_vtable, + mpf_codec_descriptor_t *codec_descriptor, + void *obj) +{ + mpf_audio_stream_t *audio_stream; + /* create audio stream */ + audio_stream = mpf_audio_stream_create( + obj, /* object to associate */ + stream_vtable, /* virtual methods table of audio stream */ + STREAM_MODE_RECEIVE, /* stream mode/direction */ + session->pool); /* memory pool to allocate memory from */ + + if(codec_descriptor) { + mrcp_client_session_t *client_session = (mrcp_client_session_t*)session; + audio_stream->rx_codec = mpf_codec_manager_codec_get(client_session->codec_manager,codec_descriptor,session->pool); + } + + /* create raw termination */ + return mpf_raw_termination_create( + NULL, /* no object to associate */ + audio_stream, /* audio stream */ + NULL, /* no video stream */ + session->pool); /* memory pool to allocate memory from */ +} + +MRCP_DECLARE(mpf_termination_t*) mrcp_application_sink_termination_create( + mrcp_session_t *session, + const mpf_audio_stream_vtable_t *stream_vtable, + mpf_codec_descriptor_t *codec_descriptor, + void *obj) +{ + mpf_audio_stream_t *audio_stream; + /* create audio stream */ + audio_stream = mpf_audio_stream_create( + obj, /* object to associate */ + stream_vtable, /* virtual methods table of audio stream */ + STREAM_MODE_SEND, /* stream mode/direction */ + session->pool); /* memory pool to allocate memory from */ + + if(codec_descriptor) { + mrcp_client_session_t *client_session = (mrcp_client_session_t*)session; + audio_stream->tx_codec = mpf_codec_manager_codec_get(client_session->codec_manager,codec_descriptor,session->pool); + } + + /* create raw termination */ + return mpf_raw_termination_create( + NULL, /* no object to associate */ + audio_stream, /* audio stream */ + NULL, /* no video stream */ + session->pool); /* memory pool to allocate memory from */ +} + +void mrcp_client_session_add(mrcp_client_t *client, mrcp_client_session_t *session) +{ + if(session) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Add MRCP Handle "APT_PTR_FMT,MRCP_SESSION_PTR(session)); + apr_hash_set(client->session_table,session,sizeof(session),session); + } +} + +void mrcp_client_session_remove(mrcp_client_t *client, mrcp_client_session_t *session) +{ + if(session) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Remove MRCP Handle "APT_PTR_FMT,MRCP_SESSION_PTR(session)); + apr_hash_set(client->session_table,session,sizeof(session),NULL); + } +} + +static void mrcp_client_on_start_complete(apt_task_t *task) +{ + apt_consumer_task_t *consumer_task = apt_task_object_get(task); + mrcp_client_t *client = apt_consumer_task_object_get(consumer_task); + void *val; + mrcp_application_t *application; + mrcp_app_message_t *app_message; + apr_hash_index_t *it; + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,CLIENT_TASK_NAME" Started"); + it = apr_hash_first(client->pool,client->app_table); + for(; it; it = apr_hash_next(it)) { + apr_hash_this(it,NULL,NULL,&val); + application = val; + if(!application) continue; + + /* raise one-time application-ready event */ + app_message = mrcp_client_app_signaling_event_create(MRCP_SIG_EVENT_READY,client->pool); + app_message->sig_message.status = MRCP_SIG_STATUS_CODE_SUCCESS; + app_message->application = application; + application->handler(app_message); + } +} + +static void mrcp_client_on_terminate_complete(apt_task_t *task) +{ + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,CLIENT_TASK_NAME" Terminated"); +} + + +static apt_bool_t mrcp_client_msg_process(apt_task_t *task, apt_task_msg_t *msg) +{ + apt_consumer_task_t *consumer_task = apt_task_object_get(task); + mrcp_client_t *client = apt_consumer_task_object_get(consumer_task); + if(!client) { + return FALSE; + } + switch(msg->type) { + case MRCP_CLIENT_SIGNALING_TASK_MSG: + { + const sig_agent_task_msg_data_t *sig_message = (const sig_agent_task_msg_data_t*)msg->data; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Receive Signaling Task Message [%d]", msg->sub_type); + switch(msg->sub_type) { + case SIG_AGENT_TASK_MSG_ANSWER: + mrcp_client_session_answer_process(sig_message->session,sig_message->descriptor); + break; + case SIG_AGENT_TASK_MSG_TERMINATE_RESPONSE: + mrcp_client_session_terminate_response_process(sig_message->session); + break; + case SIG_AGENT_TASK_MSG_CONTROL_RESPONSE: + mrcp_client_session_control_response_process(sig_message->session,sig_message->message); + break; + case SIG_AGENT_TASK_MSG_DISCOVER_RESPONSE: + mrcp_client_session_discover_response_process(sig_message->session,sig_message->descriptor); + break; + case SIG_AGENT_TASK_MSG_TERMINATE_EVENT: + mrcp_client_session_terminate_event_process(sig_message->session); + break; + default: + break; + } + break; + } + case MRCP_CLIENT_CONNECTION_TASK_MSG: + { + const connection_agent_task_msg_data_t *data = (const connection_agent_task_msg_data_t*)msg->data; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Receive Connection Task Message [%d]", msg->sub_type); + switch(msg->sub_type) { + case CONNECTION_AGENT_TASK_MSG_ADD_CHANNEL: + mrcp_client_on_channel_add(data->channel,data->descriptor,data->status); + break; + case CONNECTION_AGENT_TASK_MSG_MODIFY_CHANNEL: + mrcp_client_on_channel_modify(data->channel,data->descriptor,data->status); + break; + case CONNECTION_AGENT_TASK_MSG_REMOVE_CHANNEL: + mrcp_client_on_channel_remove(data->channel,data->status); + break; + case CONNECTION_AGENT_TASK_MSG_RECEIVE_MESSAGE: + mrcp_client_on_message_receive(data->channel,data->message); + break; + default: + break; + } + break; + } + case MRCP_CLIENT_MEDIA_TASK_MSG: + { + mpf_message_t *mpf_message = (mpf_message_t*) msg->data; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Receive Media Task Message [%d]", mpf_message->command_id); + mrcp_client_mpf_message_process(mpf_message); + break; + } + case MRCP_CLIENT_APPLICATION_TASK_MSG: + { + mrcp_app_message_t **app_message = (mrcp_app_message_t**) msg->data; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Receive Application Task Message [%d]", (*app_message)->message_type); + mrcp_client_app_message_process(*app_message); + break; + } + default: + { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Receive Unknown Task Message [%d]", msg->type); + break; + } + } + return TRUE; +} + +static apt_bool_t mrcp_app_signaling_task_msg_signal(mrcp_sig_command_e command_id, mrcp_session_t *session, mrcp_channel_t *channel) +{ + mrcp_client_session_t *client_session = (mrcp_client_session_t*)session; + mrcp_application_t *application = client_session->application; + apt_task_t *task = apt_consumer_task_base_get(application->client->task); + apt_task_msg_t *task_msg = apt_task_msg_acquire(application->msg_pool); + if(task_msg) { + mrcp_app_message_t **slot = ((mrcp_app_message_t**)task_msg->data); + mrcp_app_message_t *app_message; + task_msg->type = MRCP_CLIENT_APPLICATION_TASK_MSG; + + app_message = mrcp_client_app_signaling_request_create(command_id,session->pool); + app_message->application = client_session->application; + app_message->session = session; + app_message->channel = channel; + app_message->control_message = NULL; + app_message->descriptor = NULL; + *slot = app_message; + } + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Signal Application Task Message"); + return apt_task_msg_signal(task,task_msg); +} + +static apt_bool_t mrcp_app_control_task_msg_signal(mrcp_session_t *session, mrcp_channel_t *channel, mrcp_message_t *message) +{ + mrcp_client_session_t *client_session = (mrcp_client_session_t*)session; + mrcp_application_t *application = client_session->application; + apt_task_t *task = apt_consumer_task_base_get(application->client->task); + apt_task_msg_t *task_msg = apt_task_msg_acquire(application->msg_pool); + if(task_msg) { + mrcp_app_message_t **slot = ((mrcp_app_message_t**)task_msg->data); + mrcp_app_message_t *app_message; + task_msg->type = MRCP_CLIENT_APPLICATION_TASK_MSG; + + app_message = mrcp_client_app_control_message_create(session->pool); + app_message->application = client_session->application; + app_message->session = session; + app_message->channel = channel; + app_message->control_message = message; + *slot = app_message; + } + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Signal Application Task Message"); + return apt_task_msg_signal(task,task_msg); +} + +static apt_bool_t mrcp_client_signaling_task_msg_signal(sig_agent_task_msg_type_e type, mrcp_session_t *session, mrcp_session_descriptor_t *descriptor, mrcp_message_t *message) +{ + sig_agent_task_msg_data_t *data; + apt_task_msg_t *task_msg = apt_task_msg_acquire(session->signaling_agent->msg_pool); + task_msg->type = MRCP_CLIENT_SIGNALING_TASK_MSG; + task_msg->sub_type = type; + data = (sig_agent_task_msg_data_t*) task_msg->data; + data->session = (mrcp_client_session_t*)session; + data->descriptor = descriptor; + data->message = message; + + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Signal Signaling Task Message"); + return apt_task_msg_parent_signal(session->signaling_agent->task,task_msg); +} + +static apt_bool_t mrcp_client_connection_task_msg_signal( + connection_agent_task_msg_type_e type, + mrcp_connection_agent_t *agent, + mrcp_control_channel_t *channel, + mrcp_control_descriptor_t *descriptor, + mrcp_message_t *message, + apt_bool_t status) +{ + apt_task_t *task; + apt_task_msg_t *task_msg; + connection_agent_task_msg_data_t *data; + mrcp_client_t *client = mrcp_client_connection_agent_object_get(agent); + if(!client || !client->cnt_msg_pool) { + return FALSE; + } + task = apt_consumer_task_base_get(client->task); + task_msg = apt_task_msg_acquire(client->cnt_msg_pool); + task_msg->type = MRCP_CLIENT_CONNECTION_TASK_MSG; + task_msg->sub_type = type; + data = (connection_agent_task_msg_data_t*) task_msg->data; + data->channel = channel ? channel->obj : NULL; + data->descriptor = descriptor; + data->message = message; + data->status = status; + + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Signal Connection Task Message"); + return apt_task_msg_signal(task,task_msg); +} + + + +static apt_bool_t mrcp_client_answer_signal(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor) +{ + return mrcp_client_signaling_task_msg_signal(SIG_AGENT_TASK_MSG_ANSWER,session,descriptor,NULL); +} + +static apt_bool_t mrcp_client_terminate_response_signal(mrcp_session_t *session) +{ + return mrcp_client_signaling_task_msg_signal(SIG_AGENT_TASK_MSG_TERMINATE_RESPONSE,session,NULL,NULL); +} + +static apt_bool_t mrcp_client_control_response_signal(mrcp_session_t *session, mrcp_message_t *message) +{ + return mrcp_client_signaling_task_msg_signal(SIG_AGENT_TASK_MSG_CONTROL_RESPONSE,session,NULL,message); +} + +static apt_bool_t mrcp_client_discover_response_signal(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor) +{ + return mrcp_client_signaling_task_msg_signal(SIG_AGENT_TASK_MSG_DISCOVER_RESPONSE,session,descriptor,NULL); +} + +static apt_bool_t mrcp_client_terminate_event_signal(mrcp_session_t *session) +{ + return mrcp_client_signaling_task_msg_signal(SIG_AGENT_TASK_MSG_TERMINATE_EVENT,session,NULL,NULL); +} + + +static apt_bool_t mrcp_client_channel_add_signal(mrcp_control_channel_t *channel, mrcp_control_descriptor_t *descriptor, apt_bool_t status) +{ + return mrcp_client_connection_task_msg_signal( + CONNECTION_AGENT_TASK_MSG_ADD_CHANNEL, + channel->agent, + channel, + descriptor, + NULL, + status); +} + +static apt_bool_t mrcp_client_channel_modify_signal(mrcp_control_channel_t *channel, mrcp_control_descriptor_t *descriptor, apt_bool_t status) +{ + return mrcp_client_connection_task_msg_signal( + CONNECTION_AGENT_TASK_MSG_MODIFY_CHANNEL, + channel->agent, + channel, + descriptor, + NULL, + status); +} + +static apt_bool_t mrcp_client_channel_remove_signal(mrcp_control_channel_t *channel, apt_bool_t status) +{ + return mrcp_client_connection_task_msg_signal( + CONNECTION_AGENT_TASK_MSG_REMOVE_CHANNEL, + channel->agent, + channel, + NULL, + NULL, + status); +} + +static apt_bool_t mrcp_client_message_signal(mrcp_control_channel_t *channel, mrcp_message_t *mrcp_message) +{ + return mrcp_client_connection_task_msg_signal( + CONNECTION_AGENT_TASK_MSG_RECEIVE_MESSAGE, + channel->agent, + channel, + NULL, + mrcp_message, + TRUE); +} diff --git a/libs/unimrcp/libs/mrcp-client/src/mrcp_client_session.c b/libs/unimrcp/libs/mrcp-client/src/mrcp_client_session.c new file mode 100644 index 0000000000..0832fc85b9 --- /dev/null +++ b/libs/unimrcp/libs/mrcp-client/src/mrcp_client_session.c @@ -0,0 +1,1220 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mrcp_client_session.h" +#include "mrcp_resource.h" +#include "mrcp_resource_factory.h" +#include "mrcp_sig_agent.h" +#include "mrcp_client_connection.h" +#include "mrcp_session.h" +#include "mrcp_session_descriptor.h" +#include "mrcp_control_descriptor.h" +#include "mrcp_message.h" +#include "mpf_termination.h" +#include "mpf_stream.h" +#include "mpf_engine.h" +#include "mpf_user.h" +#include "apt_consumer_task.h" +#include "apt_obj_list.h" +#include "apt_log.h" + + +void mrcp_client_session_add(mrcp_client_t *client, mrcp_client_session_t *session); +void mrcp_client_session_remove(mrcp_client_t *client, mrcp_client_session_t *session); + +static apt_bool_t mrcp_client_session_offer_send(mrcp_client_session_t *session); + +static apt_bool_t mrcp_app_session_terminate_raise(mrcp_client_session_t *session, mrcp_sig_status_code_e status); +static apt_bool_t mrcp_app_sig_response_raise(mrcp_client_session_t *session, mrcp_sig_status_code_e status, apt_bool_t process_pending_requests); +static apt_bool_t mrcp_app_sig_event_raise(mrcp_client_session_t *session, mrcp_channel_t *channel); +static apt_bool_t mrcp_app_control_message_raise(mrcp_client_session_t *session, mrcp_channel_t *channel, mrcp_message_t *mrcp_message); +static apt_bool_t mrcp_app_request_dispatch(mrcp_client_session_t *session, const mrcp_app_message_t *app_message); + +static apt_bool_t mrcp_client_resource_answer_process(mrcp_client_session_t *session, mrcp_session_descriptor_t *descriptor); +static apt_bool_t mrcp_client_control_media_answer_process(mrcp_client_session_t *session, mrcp_session_descriptor_t *descriptor); +static apt_bool_t mrcp_client_av_media_answer_process(mrcp_client_session_t *session, mrcp_session_descriptor_t *descriptor); + +static mrcp_channel_t* mrcp_client_channel_find_by_name(mrcp_client_session_t *session, const apt_str_t *resource_name); + +static apt_bool_t mrcp_client_mpf_request_send( + mpf_engine_t *engine, + mpf_command_type_e command_id, + mpf_context_t *context, + mpf_termination_t *termination, + void *descriptor); + +mrcp_client_session_t* mrcp_client_session_create(mrcp_application_t *application, void *obj) +{ + apr_pool_t *pool; + mrcp_client_session_t *session = (mrcp_client_session_t*) mrcp_session_create(sizeof(mrcp_client_session_t)-sizeof(mrcp_session_t)); + pool = session->base.pool; + session->application = application; + session->codec_manager = NULL; + session->app_obj = obj; + session->profile = NULL; + session->context = NULL; + session->terminations = apr_array_make(pool,2,sizeof(rtp_termination_slot_t)); + session->channels = apr_array_make(pool,2,sizeof(mrcp_channel_t*)); + session->registered = FALSE; + session->offer = NULL; + session->answer = NULL; + session->active_request = NULL; + session->request_queue = apt_list_create(pool); + session->offer_flag_count = 0; + session->answer_flag_count = 0; + session->terminate_flag_count = 0; + return session; +} + +mrcp_channel_t* mrcp_client_channel_create( + mrcp_session_t *session, + mrcp_resource_id resource_id, + mpf_termination_t *termination, + mpf_rtp_termination_descriptor_t *rtp_descriptor, + void *obj) +{ + mrcp_channel_t *channel = apr_palloc(session->pool,sizeof(mrcp_channel_t)); + channel->pool = session->pool; + channel->obj = obj; + channel->session = session; + channel->resource_id = resource_id; + channel->resource_name = NULL; + channel->control_channel = NULL; + channel->termination = termination; + channel->rtp_termination_slot = NULL; + channel->resource = NULL; + channel->waiting_for_channel = FALSE; + channel->waiting_for_termination = FALSE; + + if(rtp_descriptor) { + channel->rtp_termination_slot = apr_palloc(session->pool,sizeof(rtp_termination_slot_t)); + channel->rtp_termination_slot->descriptor = rtp_descriptor; + channel->rtp_termination_slot->termination = NULL; + channel->rtp_termination_slot->waiting = FALSE; + } + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Create Channel "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(session)); + return channel; +} + +apt_bool_t mrcp_client_session_answer_process(mrcp_client_session_t *session, mrcp_session_descriptor_t *descriptor) +{ + mrcp_sig_status_code_e status_code = MRCP_SIG_STATUS_CODE_SUCCESS; + if(!session->offer) { + return FALSE; + } + if(!descriptor) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Receive Answer "APT_PTRSID_FMT" [null descriptor]", MRCP_SESSION_PTRSID(&session->base)); + status_code = MRCP_SIG_STATUS_CODE_FAILURE; + /* raise app response */ + return mrcp_app_sig_response_raise(session,status_code,TRUE); + } + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Receive Answer "APT_PTRSID_FMT" [c:%d a:%d v:%d]", + MRCP_SESSION_PTRSID(&session->base), + descriptor->control_media_arr->nelts, + descriptor->audio_media_arr->nelts, + descriptor->video_media_arr->nelts); + + if(session->base.signaling_agent->mrcp_version == MRCP_VERSION_1) { + if(mrcp_client_resource_answer_process(session,descriptor) != TRUE) { + status_code = MRCP_SIG_STATUS_CODE_FAILURE; + } + } + else { + mrcp_client_control_media_answer_process(session,descriptor); + mrcp_client_av_media_answer_process(session,descriptor); + } + + /* store received answer */ + session->answer = descriptor; + + if(!session->answer_flag_count) { + /* raise app response */ + mrcp_app_sig_response_raise(session,status_code,TRUE); + } + + return TRUE; +} + +apt_bool_t mrcp_client_session_terminate_response_process(mrcp_client_session_t *session) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Receive Terminate Response "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base)); + + if(session->terminate_flag_count) { + session->terminate_flag_count--; + } + + if(!session->terminate_flag_count) { + mrcp_app_session_terminate_raise(session,MRCP_SIG_STATUS_CODE_SUCCESS); + } + return TRUE; +} + +apt_bool_t mrcp_client_session_terminate_event_process(mrcp_client_session_t *session) +{ + if(session->active_request) { + /* raise app response */ + mrcp_app_sig_response_raise(session,MRCP_SIG_STATUS_CODE_TERMINATE,FALSE); + + /* cancel remaing requests (if any) */ + do { + session->active_request = apt_list_pop_front(session->request_queue); + if(session->active_request) { + mrcp_app_sig_response_raise(session,MRCP_SIG_STATUS_CODE_CANCEL,FALSE); + } + } + while(session->active_request); + } + else { + /* raise app event */ + mrcp_app_sig_event_raise(session,NULL); + } + return TRUE; +} + +apt_bool_t mrcp_client_session_control_response_process(mrcp_client_session_t *session, mrcp_message_t *message) +{ + mrcp_channel_t *channel = mrcp_client_channel_find_by_name(session,&message->channel_id.resource_name); + if(!channel) { + return FALSE; + } + return mrcp_app_control_message_raise(session,channel,message); +} + +apt_bool_t mrcp_client_session_discover_response_process(mrcp_client_session_t *session, mrcp_session_descriptor_t *descriptor) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Receive Resource Discovery Response "APT_PTR_FMT, MRCP_SESSION_PTR(&session->base)); + if(!session->active_request) { + return FALSE; + } + + if(!descriptor) { + /* raise app response */ + return mrcp_app_sig_response_raise(session,MRCP_SIG_STATUS_CODE_FAILURE,TRUE); + } + + if(session->base.signaling_agent->mrcp_version == MRCP_VERSION_1) { + if(descriptor->resource_state == TRUE) { + mrcp_control_descriptor_t *control_media; + if(!session->answer) { + session->answer = descriptor; + } + control_media = apr_palloc(session->base.pool,sizeof(mrcp_control_descriptor_t)); + mrcp_control_descriptor_init(control_media); + control_media->id = mrcp_session_control_media_add(session->answer,control_media); + control_media->resource_name = descriptor->resource_name; + } + } + + if(session->answer_flag_count) { + session->answer_flag_count--; + } + + if(!session->answer_flag_count) { + mrcp_app_message_t *response; + response = mrcp_client_app_response_create(session->active_request,MRCP_SIG_STATUS_CODE_SUCCESS,session->base.pool); + response->descriptor = session->answer; + session->answer = NULL; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Raise App Resource Discovery Response "APT_PTR_FMT, MRCP_SESSION_PTR(&session->base)); + session->application->handler(response); + + session->active_request = apt_list_pop_front(session->request_queue); + if(session->active_request) { + mrcp_app_request_dispatch(session,session->active_request); + } + } + return TRUE; +} + +apt_bool_t mrcp_client_on_channel_add(mrcp_channel_t *channel, mrcp_control_descriptor_t *descriptor, apt_bool_t status) +{ + mrcp_client_session_t *session = (mrcp_client_session_t*)channel->session; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"On Control Channel Add "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base)); + if(!channel->waiting_for_channel) { + return FALSE; + } + channel->waiting_for_channel = FALSE; + if(session->offer_flag_count) { + session->offer_flag_count--; + if(!session->offer_flag_count) { + /* send offer to server */ + mrcp_client_session_offer_send(session); + } + } + return TRUE; +} + +apt_bool_t mrcp_client_on_channel_modify(mrcp_channel_t *channel, mrcp_control_descriptor_t *descriptor, apt_bool_t status) +{ + mrcp_client_session_t *session = (mrcp_client_session_t*)channel->session; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"On Control Channel Modify "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base)); + if(!channel->waiting_for_channel) { + return FALSE; + } + channel->waiting_for_channel = FALSE; + if(session->answer_flag_count) { + session->answer_flag_count--; + if(!session->answer_flag_count) { + /* raise app response */ + mrcp_app_sig_response_raise( + session, + status == TRUE ? MRCP_SIG_STATUS_CODE_SUCCESS : MRCP_SIG_STATUS_CODE_FAILURE, + TRUE); + } + } + return TRUE; +} + +apt_bool_t mrcp_client_on_channel_remove(mrcp_channel_t *channel, apt_bool_t status) +{ + mrcp_client_session_t *session = (mrcp_client_session_t*)channel->session; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"On Control Channel Remove "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base)); + if(!channel->waiting_for_channel) { + return FALSE; + } + channel->waiting_for_channel = FALSE; + if(session->terminate_flag_count) { + session->terminate_flag_count--; + if(!session->terminate_flag_count) { + mrcp_app_session_terminate_raise( + session, + status == TRUE ? MRCP_SIG_STATUS_CODE_SUCCESS : MRCP_SIG_STATUS_CODE_FAILURE); + } + } + return TRUE; +} + +apt_bool_t mrcp_client_on_message_receive(mrcp_channel_t *channel, mrcp_message_t *message) +{ + mrcp_client_session_t *session = (mrcp_client_session_t*)channel->session; + return mrcp_app_control_message_raise(session,channel,message); +} + +mrcp_app_message_t* mrcp_client_app_signaling_request_create(mrcp_sig_command_e command_id, apr_pool_t *pool) +{ + mrcp_app_message_t *app_message = apr_palloc(pool,sizeof(mrcp_app_message_t)); + app_message->message_type = MRCP_APP_MESSAGE_TYPE_SIGNALING; + app_message->sig_message.message_type = MRCP_SIG_MESSAGE_TYPE_REQUEST; + app_message->sig_message.command_id = command_id; + return app_message; +} + +mrcp_app_message_t* mrcp_client_app_signaling_event_create(mrcp_sig_event_e event_id, apr_pool_t *pool) +{ + mrcp_app_message_t *app_message = apr_palloc(pool,sizeof(mrcp_app_message_t)); + app_message->message_type = MRCP_APP_MESSAGE_TYPE_SIGNALING; + app_message->sig_message.message_type = MRCP_SIG_MESSAGE_TYPE_EVENT; + app_message->sig_message.event_id = event_id; + return app_message; +} + +mrcp_app_message_t* mrcp_client_app_control_message_create(apr_pool_t *pool) +{ + mrcp_app_message_t *app_message = apr_palloc(pool,sizeof(mrcp_app_message_t)); + app_message->message_type = MRCP_APP_MESSAGE_TYPE_CONTROL; + return app_message; +} + +mrcp_app_message_t* mrcp_client_app_response_create(const mrcp_app_message_t *app_request, mrcp_sig_status_code_e status, apr_pool_t *pool) +{ + mrcp_app_message_t *app_response = apr_palloc(pool,sizeof(mrcp_app_message_t)); + *app_response = *app_request; + app_response->sig_message.message_type = MRCP_SIG_MESSAGE_TYPE_RESPONSE; + app_response->sig_message.status = status; + return app_response; +} + +apt_bool_t mrcp_client_app_message_process(mrcp_app_message_t *app_message) +{ + mrcp_client_session_t *session = (mrcp_client_session_t*)app_message->session; + if(app_message->message_type == MRCP_APP_MESSAGE_TYPE_SIGNALING) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Receive App Request "APT_PTRSID_FMT" [%d]", + MRCP_SESSION_PTRSID(&session->base), + app_message->sig_message.command_id); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Receive App MRCP Request "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base)); + } + + if(session->active_request) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Push Request to Queue "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base)); + apt_list_push_back(session->request_queue,app_message,session->base.pool); + return TRUE; + } + + session->active_request = app_message; + mrcp_app_request_dispatch(session,app_message); + return TRUE; +} + +static apt_bool_t mrcp_client_session_offer_send(mrcp_client_session_t *session) +{ + mrcp_session_descriptor_t *descriptor = session->offer; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Send Offer "APT_PTRSID_FMT" [c:%d a:%d v:%d]", + MRCP_SESSION_PTRSID(&session->base), + descriptor->control_media_arr->nelts, + descriptor->audio_media_arr->nelts, + descriptor->video_media_arr->nelts); + return mrcp_session_offer(&session->base,descriptor); +} + +static apt_bool_t mrcp_app_session_terminate_raise(mrcp_client_session_t *session, mrcp_sig_status_code_e status) +{ + int i; + mrcp_channel_t *channel; + for(i=0; ichannels->nelts; i++) { + channel = ((mrcp_channel_t**)session->channels->elts)[i]; + if(!channel) continue; + + if(channel->control_channel) { + mrcp_client_control_channel_destroy(channel->control_channel); + channel->control_channel = NULL; + } + } + + mrcp_client_session_remove(session->application->client,session); + /* raise app response */ + return mrcp_app_sig_response_raise(session,status,FALSE); +} + +static apt_bool_t mrcp_app_sig_response_raise(mrcp_client_session_t *session, mrcp_sig_status_code_e status, apt_bool_t process_pending_requests) +{ + mrcp_app_message_t *response; + const mrcp_app_message_t *request = session->active_request; + if(!request) { + return FALSE; + } + session->active_request = NULL; + response = mrcp_client_app_response_create(request,status,session->base.pool); + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Raise App Response "APT_PTRSID_FMT" [%d] %s [%d]", + MRCP_SESSION_PTRSID(&session->base), + response->sig_message.command_id, + status == MRCP_SIG_STATUS_CODE_SUCCESS ? "SUCCESS" : "FAILURE", + status); + session->application->handler(response); + + if(process_pending_requests) { + session->active_request = apt_list_pop_front(session->request_queue); + if(session->active_request) { + mrcp_app_request_dispatch(session,session->active_request); + } + } + return TRUE; +} + +static apt_bool_t mrcp_app_sig_event_raise(mrcp_client_session_t *session, mrcp_channel_t *channel) +{ + mrcp_app_message_t *app_event; + if(!session) { + return FALSE; + } + app_event = mrcp_client_app_signaling_event_create(MRCP_SIG_EVENT_TERMINATE,session->base.pool); + app_event->application = session->application; + app_event->session = &session->base; + app_event->channel = channel; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Raise App Event "APT_PTRSID_FMT" [%d]", + MRCP_SESSION_PTRSID(&session->base), + app_event->sig_message.event_id); + return session->application->handler(app_event); +} + +static apt_bool_t mrcp_app_control_message_raise(mrcp_client_session_t *session, mrcp_channel_t *channel, mrcp_message_t *mrcp_message) +{ + if(mrcp_message->start_line.message_type == MRCP_MESSAGE_TYPE_RESPONSE) { + mrcp_app_message_t *response; + mrcp_message_t *mrcp_request; + if(!session->active_request || !session->active_request->control_message) { + return FALSE; + } + response = mrcp_client_app_response_create(session->active_request,0,session->base.pool); + mrcp_request = session->active_request->control_message; + mrcp_message->start_line.method_id = mrcp_request->start_line.method_id; + mrcp_message->start_line.method_name = mrcp_request->start_line.method_name; + response->control_message = mrcp_message; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Raise App MRCP Response "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base)); + session->application->handler(response); + + session->active_request = apt_list_pop_front(session->request_queue); + if(session->active_request) { + mrcp_app_request_dispatch(session,session->active_request); + } + } + else if(mrcp_message->start_line.message_type == MRCP_MESSAGE_TYPE_EVENT) { + mrcp_app_message_t *app_message; + app_message = mrcp_client_app_control_message_create(session->base.pool); + app_message->control_message = mrcp_message; + app_message->application = session->application; + app_message->session = &session->base; + app_message->channel = channel; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Raise App MRCP Event "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base)); + session->application->handler(app_message); + } + return TRUE; +} + +static apt_bool_t mrcp_client_channel_find(mrcp_client_session_t *session, mrcp_channel_t *channel, int *index) +{ + int i; + for(i=0; ichannels->nelts; i++) { + mrcp_channel_t *existing_channel = ((mrcp_channel_t**)session->channels->elts)[i]; + if(existing_channel == channel) { + if(index) { + *index = i; + } + return TRUE; + } + } + return FALSE; +} + +static rtp_termination_slot_t* mrcp_client_rtp_termination_find(mrcp_client_session_t *session, mpf_termination_t *termination) +{ + int i; + rtp_termination_slot_t *slot; + for(i=0; iterminations->nelts; i++) { + slot = &((rtp_termination_slot_t*)session->terminations->elts)[i]; + if(slot && slot->termination == termination) { + return slot; + } + } + return NULL; +} + +static int mrcp_client_audio_media_find_by_mid(const mrcp_session_descriptor_t *descriptor, apr_size_t mid) +{ + int i; + mpf_rtp_media_descriptor_t *media; + for(i=0; iaudio_media_arr->nelts; i++) { + media = ((mpf_rtp_media_descriptor_t**)descriptor->audio_media_arr->elts)[i]; + if(media->mid == mid) { + return i; + } + } + return -1; +} + +static mrcp_channel_t* mrcp_client_channel_termination_find(mrcp_client_session_t *session, mpf_termination_t *termination) +{ + int i; + mrcp_channel_t *channel; + for(i=0; ichannels->nelts; i++) { + channel = ((mrcp_channel_t**)session->channels->elts)[i]; + if(!channel) continue; + + if(channel->termination == termination) { + return channel; + } + } + return NULL; +} + +static mrcp_channel_t* mrcp_client_channel_find_by_name(mrcp_client_session_t *session, const apt_str_t *resource_name) +{ + int i; + mrcp_channel_t *channel; + for(i=0; ichannels->nelts; i++) { + channel = ((mrcp_channel_t**)session->channels->elts)[i]; + if(!channel) continue; + + if(apt_string_compare(channel->resource_name,resource_name) == TRUE) { + return channel; + } + } + return NULL; +} + +static apt_bool_t mrcp_client_message_send(mrcp_client_session_t *session, mrcp_channel_t *channel, mrcp_message_t *message) +{ + if(!session->base.id.length) { + mrcp_message_t *response = mrcp_response_create(message,message->pool); + response->start_line.status_code = MRCP_STATUS_CODE_METHOD_FAILED; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Raise App Failure MRCP Response "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base)); + mrcp_app_control_message_raise(session,channel,response); + return TRUE; + } + + message->channel_id.session_id = session->base.id; + message->start_line.request_id = ++session->base.last_request_id; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Send MRCP Request "APT_PTRSIDRES_FMT" [%d]", + MRCP_SESSION_PTRSID(&session->base), + channel->resource_name->buf, + message->start_line.request_id); + + if(channel->control_channel) { + /* MRCPv2 */ + mrcp_client_control_message_send(channel->control_channel,message); + } + else { + /* MRCPv1 */ + mrcp_session_control_request(channel->session,message); + } + + return TRUE; +} + +static apt_bool_t mrcp_client_channel_modify(mrcp_client_session_t *session, mrcp_channel_t *channel, apt_bool_t enable) +{ + int index; + if(!session->offer) { + return FALSE; + } + if(!channel->resource_name) { + return FALSE; + } + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Modify Control Channel "APT_PTRSIDRES_FMT" [%d]", + MRCP_SESSION_PTRSID(&session->base), + channel->resource_name->buf, + enable); + if(mrcp_client_channel_find(session,channel,&index) == TRUE) { + mrcp_control_descriptor_t *control_media = mrcp_session_control_media_get(session->offer,(apr_size_t)index); + if(control_media) { + control_media->port = (enable == TRUE) ? 9 : 0; + if(channel->termination && channel->termination->audio_stream) { + int i = mrcp_client_audio_media_find_by_mid(session->offer,control_media->cmid); + if(i >= 0) { + mpf_stream_mode_e mode = mpf_stream_mode_negotiate(channel->termination->audio_stream->mode); + mpf_rtp_media_descriptor_t *audio_media = mrcp_session_audio_media_get(session->offer,(apr_size_t)i); + if(audio_media) { + if(enable == TRUE) { + audio_media->mode |= mode; + } + else { + audio_media->mode &= ~mode; + } + audio_media->base.state = (audio_media->mode != STREAM_MODE_NONE) ? MPF_MEDIA_ENABLED : MPF_MEDIA_DISABLED; + } + } + } + } + } + + session->offer->resource_name = *channel->resource_name; + session->offer->resource_state = enable; + return mrcp_client_session_offer_send(session); +} + +static apt_bool_t mrcp_client_channel_add(mrcp_client_session_t *session, mrcp_channel_t *channel) +{ + mrcp_channel_t **channel_slot; + mrcp_control_descriptor_t *control_media; + mpf_rtp_termination_descriptor_t *rtp_descriptor = NULL; + rtp_termination_slot_t *termination_slot; + apr_pool_t *pool = session->base.pool; + mrcp_profile_t *profile = session->profile; + if(mrcp_client_channel_find(session,channel,NULL) == TRUE) { + /* update */ + return mrcp_client_channel_modify(session,channel,TRUE); + } + + if(!session->offer) { + session->offer = mrcp_session_descriptor_create(pool); + session->context = mpf_context_create(session,5,pool); + } + if(!channel->resource) { + channel->resource = mrcp_resource_get(profile->resource_factory,channel->resource_id); + if(!channel->resource) { + return FALSE; + } + channel->resource_name = mrcp_resource_name_get(profile->resource_factory,channel->resource_id); + if(!channel->resource_name) { + return FALSE; + } + } + if(session->base.signaling_agent->mrcp_version == MRCP_VERSION_1) { + session->offer->resource_name = *channel->resource_name; + session->offer->resource_state = TRUE; + } + else { + if(!channel->control_channel) { + channel->control_channel = mrcp_client_control_channel_create(profile->connection_agent,channel,pool); + } + control_media = mrcp_control_offer_create(pool); + control_media->id = mrcp_session_control_media_add(session->offer,control_media); + control_media->cmid = session->offer->control_media_arr->nelts; + control_media->resource_name = *channel->resource_name; + if(mrcp_client_control_channel_add(channel->control_channel,control_media) == TRUE) { + channel->waiting_for_channel = TRUE; + session->offer_flag_count++; + } + } + + /* add to channel array */ + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Add Control Channel "APT_PTRSIDRES_FMT, + MRCP_SESSION_PTRSID(&session->base), + channel->resource_name->buf); + channel_slot = apr_array_push(session->channels); + *channel_slot = channel; + + if(channel->termination) { + if(mrcp_client_mpf_request_send(profile->media_engine,MPF_COMMAND_ADD,session->context,channel->termination,NULL) == TRUE) { + channel->waiting_for_termination = TRUE; + session->offer_flag_count++; + } + } + + if(channel->rtp_termination_slot) { + rtp_descriptor = channel->rtp_termination_slot->descriptor; + } + /* add to rtp termination array */ + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Add RTP Termination "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base)); + termination_slot = apr_array_push(session->terminations); + termination_slot->waiting = FALSE; + termination_slot->termination = NULL; + termination_slot->descriptor = NULL; + if(rtp_descriptor) { + if(rtp_descriptor->audio.local) { + session->offer->ip = rtp_descriptor->audio.local->base.ip; + session->offer->ext_ip = rtp_descriptor->audio.local->base.ext_ip; + rtp_descriptor->audio.local->base.id = mrcp_session_audio_media_add(session->offer,rtp_descriptor->audio.local); + rtp_descriptor->audio.local->mid = session->offer->audio_media_arr->nelts; + } + } + else { + /* create rtp termination */ + mpf_termination_t *termination = mpf_termination_create(profile->rtp_termination_factory,session,session->base.pool); + termination_slot->termination = termination; + + /* initialize rtp descriptor */ + rtp_descriptor = apr_palloc(pool,sizeof(mpf_rtp_termination_descriptor_t)); + mpf_rtp_termination_descriptor_init(rtp_descriptor); + if(channel->termination && channel->termination->audio_stream) { + mpf_rtp_media_descriptor_t *media; + media = apr_palloc(pool,sizeof(mpf_rtp_media_descriptor_t)); + mpf_rtp_media_descriptor_init(media); + media->base.state = MPF_MEDIA_ENABLED; + media->mode = mpf_stream_mode_negotiate(channel->termination->audio_stream->mode); + rtp_descriptor->audio.local = media; + } + /* send add termination request (add to media context) */ + if(mrcp_client_mpf_request_send(profile->media_engine,MPF_COMMAND_ADD,session->context,termination,rtp_descriptor) == TRUE) { + termination_slot->waiting = TRUE; + session->offer_flag_count++; + } + } + termination_slot->descriptor = rtp_descriptor; + channel->rtp_termination_slot = termination_slot; + + if(!session->offer_flag_count) { + /* send offer to server */ + mrcp_client_session_offer_send(session); + } + return TRUE; +} + +static apt_bool_t mrcp_client_session_update(mrcp_client_session_t *session) +{ + if(!session->offer) { + return FALSE; + } + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Update Session "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base)); + return mrcp_client_session_offer_send(session); +} + +static apt_bool_t mrcp_client_session_terminate(mrcp_client_session_t *session) +{ + mrcp_profile_t *profile; + mrcp_channel_t *channel; + rtp_termination_slot_t *slot; + int i; + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Terminate Session "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base)); + profile = session->profile; + /* remove existing control channels */ + for(i=0; ichannels->nelts; i++) { + /* get existing channel */ + channel = *((mrcp_channel_t**)session->channels->elts + i); + if(!channel) continue; + + if(channel->control_channel) { + /* remove channel */ + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Remove Control Channel "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base)); + if(mrcp_client_control_channel_remove(channel->control_channel) == TRUE) { + channel->waiting_for_channel = TRUE; + session->terminate_flag_count++; + } + } + + if(channel->termination) { + /* send subtract termination request */ + if(channel->termination) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Subtract Channel Termination "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base)); + if(mrcp_client_mpf_request_send(profile->media_engine,MPF_COMMAND_SUBTRACT,session->context,channel->termination,NULL) == TRUE) { + channel->waiting_for_termination = TRUE; + session->terminate_flag_count++; + } + } + } + } + + /* subtract existing terminations */ + for(i=0; iterminations->nelts; i++) { + /* get existing termination */ + slot = &((rtp_termination_slot_t*)session->terminations->elts)[i]; + if(!slot || !slot->termination) continue; + + /* send subtract termination request */ + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Subtract Termination "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base)); + if(mrcp_client_mpf_request_send(profile->media_engine,MPF_COMMAND_SUBTRACT,session->context,slot->termination,NULL) == TRUE) { + slot->waiting = TRUE; + session->terminate_flag_count++; + } + } + + session->terminate_flag_count++; + mrcp_session_terminate_request(&session->base); + return TRUE; +} + +static apt_bool_t mrcp_client_resource_discover(mrcp_client_session_t *session) +{ + mrcp_session_descriptor_t *descriptor = NULL; + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Send Resource Discovery Request "APT_PTR_FMT, MRCP_SESSION_PTR(&session->base)); + session->answer = NULL; + if(session->base.signaling_agent->mrcp_version == MRCP_VERSION_1) { + const apt_str_t *resource_name; + mrcp_resource_id i; + + for(i=0; iprofile->resource_factory,i); + if(!resource_name) continue; + + descriptor = mrcp_session_descriptor_create(session->base.pool); + apt_string_copy(&descriptor->resource_name,resource_name,session->base.pool); + if(mrcp_session_discover_request(&session->base,descriptor) == TRUE) { + session->answer_flag_count++; + } + } + } + else { + if(mrcp_session_discover_request(&session->base,descriptor) == TRUE) { + session->answer_flag_count++; + } + } + + if(session->answer_flag_count == 0) { + mrcp_app_sig_response_raise(session,MRCP_SIG_STATUS_CODE_FAILURE,TRUE); + } + return TRUE; +} + +static apt_bool_t mrcp_client_on_termination_add(mrcp_client_session_t *session, mpf_message_t *mpf_message) +{ + rtp_termination_slot_t *termination_slot; + if(!session) { + return FALSE; + } + termination_slot = mrcp_client_rtp_termination_find(session,mpf_message->termination); + if(termination_slot) { + /* rtp termination */ + mpf_rtp_termination_descriptor_t *rtp_descriptor; + if(termination_slot->waiting == FALSE) { + return FALSE; + } + termination_slot->waiting = FALSE; + rtp_descriptor = mpf_message->descriptor; + if(rtp_descriptor->audio.local) { + session->offer->ip = rtp_descriptor->audio.local->base.ip; + session->offer->ext_ip = rtp_descriptor->audio.local->base.ext_ip; + rtp_descriptor->audio.local->base.id = mrcp_session_audio_media_add(session->offer,rtp_descriptor->audio.local); + rtp_descriptor->audio.local->mid = session->offer->audio_media_arr->nelts; + } + if(session->offer_flag_count) { + session->offer_flag_count--; + if(!session->offer_flag_count) { + /* send offer to server */ + mrcp_client_session_offer_send(session); + } + } + } + else { + /* channel termination */ + mrcp_channel_t *channel = mrcp_client_channel_termination_find(session,mpf_message->termination); + if(channel && channel->waiting_for_termination == TRUE) { + channel->waiting_for_termination = FALSE; + if(session->offer_flag_count) { + session->offer_flag_count--; + if(!session->offer_flag_count) { + /* send offer to server */ + mrcp_client_session_offer_send(session); + } + } + } + } + return TRUE; +} + +static apt_bool_t mrcp_client_on_termination_modify(mrcp_client_session_t *session, mpf_message_t *mpf_message) +{ + rtp_termination_slot_t *termination_slot; + if(!session) { + return FALSE; + } + termination_slot = mrcp_client_rtp_termination_find(session,mpf_message->termination); + if(termination_slot) { + /* rtp termination */ + if(termination_slot->waiting == FALSE) { + return FALSE; + } + termination_slot->waiting = FALSE; + termination_slot->descriptor = mpf_message->descriptor;; + + if(session->offer_flag_count) { + session->offer_flag_count--; + if(!session->offer_flag_count) { + /* send offer to server */ + mrcp_client_session_offer_send(session); + } + } + if(session->answer_flag_count) { + session->answer_flag_count--; + if(!session->answer_flag_count) { + /* raise app response */ + mrcp_app_sig_response_raise(session,MRCP_SIG_STATUS_CODE_SUCCESS,TRUE); + } + } + } + return TRUE; +} + +static apt_bool_t mrcp_client_on_termination_subtract(mrcp_client_session_t *session, mpf_message_t *mpf_message) +{ + rtp_termination_slot_t *termination_slot; + if(!session) { + return FALSE; + } + termination_slot = mrcp_client_rtp_termination_find(session,mpf_message->termination); + if(termination_slot) { + /* rtp termination */ + if(termination_slot->waiting == FALSE) { + return FALSE; + } + termination_slot->waiting = FALSE; + if(session->terminate_flag_count) { + session->terminate_flag_count--; + if(!session->terminate_flag_count) { + mrcp_app_session_terminate_raise(session,MRCP_SIG_STATUS_CODE_SUCCESS); + } + } + } + else { + /* channel termination */ + mrcp_channel_t *channel = mrcp_client_channel_termination_find(session,mpf_message->termination); + if(channel && channel->waiting_for_termination == TRUE) { + channel->waiting_for_termination = FALSE; + if(session->terminate_flag_count) { + session->terminate_flag_count--; + if(!session->terminate_flag_count) { + /* raise app response */ + mrcp_app_sig_response_raise(session,MRCP_SIG_STATUS_CODE_SUCCESS,TRUE); + } + } + } + } + return TRUE; +} + +apt_bool_t mrcp_client_mpf_message_process(mpf_message_t *mpf_message) +{ + mrcp_client_session_t *session = NULL; + if(mpf_message->context) { + session = mpf_context_object_get(mpf_message->context); + } + if(!session) { + return FALSE; + } + if(mpf_message->message_type == MPF_MESSAGE_TYPE_RESPONSE) { + switch(mpf_message->command_id) { + case MPF_COMMAND_ADD: + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"On Termination Add "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base)); + mrcp_client_on_termination_add(session,mpf_message); + break; + case MPF_COMMAND_MODIFY: + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"On Termination Modify "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base)); + mrcp_client_on_termination_modify(session,mpf_message); + break; + case MPF_COMMAND_SUBTRACT: + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"On Termination Subtract "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base)); + mrcp_client_on_termination_subtract(session,mpf_message); + break; + default: + break; + } + } + else if(mpf_message->message_type == MPF_MESSAGE_TYPE_EVENT) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Process MPF Event "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base)); + } + return TRUE; +} + +static apt_bool_t mrcp_client_resource_answer_process(mrcp_client_session_t *session, mrcp_session_descriptor_t *descriptor) +{ + apt_bool_t status = TRUE; + if(session->offer->resource_state == TRUE) { + if(descriptor->resource_state == TRUE) { + mrcp_client_av_media_answer_process(session,descriptor); + } + else { + status = FALSE; + } + } + return status; +} + +static apt_bool_t mrcp_client_control_media_answer_process(mrcp_client_session_t *session, mrcp_session_descriptor_t *descriptor) +{ + mrcp_channel_t *channel; + mrcp_control_descriptor_t *control_descriptor; + int i; + int count = session->channels->nelts; + if(count != descriptor->control_media_arr->nelts) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Number of control channels [%d] != Number of control media in answer [%d]", + count,descriptor->control_media_arr->nelts); + count = descriptor->control_media_arr->nelts; + } + + if(!session->base.id.length) { + /* initial answer received, store session id and add to session's table */ + control_descriptor = mrcp_session_control_media_get(descriptor,0); + if(control_descriptor) { + session->base.id = control_descriptor->session_id; + } + } + + /* update existing control channels */ + for(i=0; ichannels->elts + i); + if(!channel) continue; + + /* get control descriptor */ + control_descriptor = mrcp_session_control_media_get(descriptor,i); + /* modify channel */ + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Modify Control Channel "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base)); + if(mrcp_client_control_channel_modify(channel->control_channel,control_descriptor) == TRUE) { + channel->waiting_for_channel = TRUE; + session->answer_flag_count++; + } + } + return TRUE; +} + +static apt_bool_t mrcp_client_av_media_answer_process(mrcp_client_session_t *session, mrcp_session_descriptor_t *descriptor) +{ + rtp_termination_slot_t *slot; + int i; + int count = session->terminations->nelts; + if(count != descriptor->audio_media_arr->nelts) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Number of terminations [%d] != Number of audio media in answer [%d]", + count,descriptor->audio_media_arr->nelts); + count = descriptor->audio_media_arr->nelts; + } + + /* update existing terminations */ + for(i=0; iterminations->elts)[i]; + if(!slot) continue; + + remote_media = mrcp_session_audio_media_get(descriptor,i); + if(slot->descriptor) { + slot->descriptor->audio.remote = remote_media; + } + if(slot->termination) { + /* construct termination descriptor */ + rtp_descriptor = apr_palloc(session->base.pool,sizeof(mpf_rtp_termination_descriptor_t)); + mpf_rtp_termination_descriptor_init(rtp_descriptor); + rtp_descriptor->audio.local = NULL; + rtp_descriptor->audio.remote = remote_media; + + /* send modify termination request */ + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Modify Termination "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base)); + if(mrcp_client_mpf_request_send(session->profile->media_engine,MPF_COMMAND_MODIFY,session->context,slot->termination,rtp_descriptor) == TRUE) { + slot->waiting = TRUE; + session->answer_flag_count++; + } + } + } + return TRUE; +} + +static apt_bool_t mrcp_app_request_dispatch(mrcp_client_session_t *session, const mrcp_app_message_t *app_message) +{ + if(session->registered == FALSE) { + session->base.signaling_agent = session->profile->signaling_agent; + session->base.signaling_agent->create_client_session(&session->base); + + mrcp_client_session_add(session->application->client,session); + session->registered = TRUE; + } + switch(app_message->message_type) { + case MRCP_APP_MESSAGE_TYPE_SIGNALING: + { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Dispatch Application Request "APT_PTRSID_FMT" [%d]", + MRCP_SESSION_PTRSID(&session->base), + app_message->sig_message.command_id); + switch(app_message->sig_message.command_id) { + case MRCP_SIG_COMMAND_SESSION_UPDATE: + mrcp_client_session_update(session); + break; + case MRCP_SIG_COMMAND_SESSION_TERMINATE: + mrcp_client_session_terminate(session); + break; + case MRCP_SIG_COMMAND_CHANNEL_ADD: + mrcp_client_channel_add(session,app_message->channel); + break; + case MRCP_SIG_COMMAND_CHANNEL_REMOVE: + mrcp_client_channel_modify(session,app_message->channel,FALSE); + break; + case MRCP_SIG_COMMAND_RESOURCE_DISCOVER: + mrcp_client_resource_discover(session); + break; + default: + break; + } + break; + } + case MRCP_APP_MESSAGE_TYPE_CONTROL: + { + mrcp_client_message_send(session,app_message->channel,app_message->control_message); + break; + } + } + return TRUE; +} + +/** Dispatch application message */ +MRCP_DECLARE(apt_bool_t) mrcp_application_message_dispatch(const mrcp_app_message_dispatcher_t *dispatcher, const mrcp_app_message_t *app_message) +{ + apt_bool_t status = FALSE; + switch(app_message->message_type) { + case MRCP_APP_MESSAGE_TYPE_SIGNALING: + { + if(app_message->sig_message.message_type == MRCP_SIG_MESSAGE_TYPE_RESPONSE) { + switch(app_message->sig_message.command_id) { + case MRCP_SIG_COMMAND_SESSION_UPDATE: + if(dispatcher->on_session_update) { + status = dispatcher->on_session_update( + app_message->application, + app_message->session, + app_message->sig_message.status); + } + break; + case MRCP_SIG_COMMAND_SESSION_TERMINATE: + if(dispatcher->on_session_terminate) { + status = dispatcher->on_session_terminate( + app_message->application, + app_message->session, + app_message->sig_message.status); + } + break; + case MRCP_SIG_COMMAND_CHANNEL_ADD: + if(dispatcher->on_channel_add) { + status = dispatcher->on_channel_add( + app_message->application, + app_message->session, + app_message->channel, + app_message->sig_message.status); + } + break; + case MRCP_SIG_COMMAND_CHANNEL_REMOVE: + if(dispatcher->on_channel_remove) { + status = dispatcher->on_channel_remove( + app_message->application, + app_message->session, + app_message->channel, + app_message->sig_message.status); + } + break; + case MRCP_SIG_COMMAND_RESOURCE_DISCOVER: + if(dispatcher->on_resource_discover) { + status = dispatcher->on_resource_discover( + app_message->application, + app_message->session, + app_message->descriptor, + app_message->sig_message.status); + } + break; + default: + break; + } + } + else if(app_message->sig_message.message_type == MRCP_SIG_MESSAGE_TYPE_EVENT) { + switch(app_message->sig_message.event_id) { + case MRCP_SIG_EVENT_READY: + if(dispatcher->on_ready) { + status = dispatcher->on_ready( + app_message->application, + app_message->sig_message.status); + } + break; + case MRCP_SIG_EVENT_TERMINATE: + if(dispatcher->on_terminate_event) { + status = dispatcher->on_terminate_event( + app_message->application, + app_message->session, + app_message->channel); + } + break; + default: + break; + } + } + break; + } + case MRCP_APP_MESSAGE_TYPE_CONTROL: + { + if(dispatcher->on_message_receive) { + status = dispatcher->on_message_receive( + app_message->application, + app_message->session, + app_message->channel, + app_message->control_message); + } + break; + } + } + return status; +} + +static apt_bool_t mrcp_client_mpf_request_send( + mpf_engine_t *engine, + mpf_command_type_e command_id, + mpf_context_t *context, + mpf_termination_t *termination, + void *descriptor) +{ + apt_task_t *media_task; + apt_task_msg_t *msg; + mpf_message_t *mpf_message; + if(!engine) { + return FALSE; + } + media_task = mpf_task_get(engine); + msg = apt_task_msg_get(media_task); + msg->type = TASK_MSG_USER; + mpf_message = (mpf_message_t*) msg->data; + + mpf_message->message_type = MPF_MESSAGE_TYPE_REQUEST; + mpf_message->command_id = command_id; + mpf_message->context = context; + mpf_message->termination = termination; + mpf_message->descriptor = descriptor; + return apt_task_msg_signal(media_task,msg); +} diff --git a/libs/unimrcp/libs/mrcp-engine/Makefile.am b/libs/unimrcp/libs/mrcp-engine/Makefile.am new file mode 100644 index 0000000000..3dfab1b177 --- /dev/null +++ b/libs/unimrcp/libs/mrcp-engine/Makefile.am @@ -0,0 +1,17 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_srcdir)/libs/mrcp-engine/include \ + -I$(top_srcdir)/libs/mrcp/include \ + -I$(top_srcdir)/libs/mrcp/message/include \ + -I$(top_srcdir)/libs/mrcp/control/include \ + -I$(top_srcdir)/libs/mrcp/resources/include \ + -I$(top_srcdir)/libs/mpf/include \ + -I$(top_srcdir)/libs/apr-toolkit/include \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) + +noinst_LTLIBRARIES = libmrcpengine.la + +include_HEADERS = include/mrcp_resource_plugin.h \ + include/mrcp_resource_engine.h + +libmrcpengine_la_SOURCES = src/mrcp_resource_engine.c diff --git a/libs/unimrcp/libs/mrcp-engine/include/mrcp_resource_engine.h b/libs/unimrcp/libs/mrcp-engine/include/mrcp_resource_engine.h new file mode 100644 index 0000000000..3df40cc60b --- /dev/null +++ b/libs/unimrcp/libs/mrcp-engine/include/mrcp_resource_engine.h @@ -0,0 +1,213 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_RESOURCE_ENGINE_H__ +#define __MRCP_RESOURCE_ENGINE_H__ + +/** + * @file mrcp_resource_engine.h + * @brief MRCP Resource Engine Interface + */ + +#include "mrcp_types.h" +#include "mpf_termination.h" +#include "mpf_stream.h" +#include "mrcp_resource_plugin.h" + +APT_BEGIN_EXTERN_C + +/** MRCP resource engine vtable declaration */ +typedef struct mrcp_engine_method_vtable_t mrcp_engine_method_vtable_t; +/** MRCP engine channel declaration */ +typedef struct mrcp_engine_channel_t mrcp_engine_channel_t; +/** MRCP engine channel virtual method table declaration */ +typedef struct mrcp_engine_channel_method_vtable_t mrcp_engine_channel_method_vtable_t; +/** MRCP engine channel virtual event table declaration */ +typedef struct mrcp_engine_channel_event_vtable_t mrcp_engine_channel_event_vtable_t; + +/** Table of channel virtual methods */ +struct mrcp_engine_channel_method_vtable_t { + /** Virtual destroy */ + apt_bool_t (*destroy)(mrcp_engine_channel_t *channel); + /** Virtual open */ + apt_bool_t (*open)(mrcp_engine_channel_t *channel); + /** Virtual close */ + apt_bool_t (*close)(mrcp_engine_channel_t *channel); + /** Virtual process_request */ + apt_bool_t (*process_request)(mrcp_engine_channel_t *channel, mrcp_message_t *request); +}; + +/** Table of channel virtual event handlers */ +struct mrcp_engine_channel_event_vtable_t { + /** Open event handler */ + apt_bool_t (*on_open)(mrcp_engine_channel_t *channel, apt_bool_t status); + /** Close event handler */ + apt_bool_t (*on_close)(mrcp_engine_channel_t *channel); + /** Message event handler */ + apt_bool_t (*on_message)(mrcp_engine_channel_t *channel, mrcp_message_t *message); +}; + +/** MRCP engine channel declaration */ +struct mrcp_engine_channel_t { + /** Table of virtual methods */ + const mrcp_engine_channel_method_vtable_t *method_vtable; + /** External object used with virtual methods */ + void *method_obj; + /** Table of virtual event handlers */ + const mrcp_engine_channel_event_vtable_t *event_vtable; + /** External object used with event handlers */ + void *event_obj; + /** Media termination */ + mpf_termination_t *termination; + /** Back pointer to resource engine */ + mrcp_resource_engine_t *engine; + /** Pool to allocate memory from */ + apr_pool_t *pool; +}; + +/** Table of MRCP engine virtual methods */ +struct mrcp_engine_method_vtable_t { + /** Virtual destroy */ + apt_bool_t (*destroy)(mrcp_resource_engine_t *engine); + /** Virtual open */ + apt_bool_t (*open)(mrcp_resource_engine_t *engine); + /** Virtual close */ + apt_bool_t (*close)(mrcp_resource_engine_t *engine); + /** Virtual channel create */ + mrcp_engine_channel_t* (*create_channel)(mrcp_resource_engine_t *engine, apr_pool_t *pool); +}; + +/** MRCP resource engine */ +struct mrcp_resource_engine_t { + /** Plugin version */ + mrcp_plugin_version_t plugin_version; + /** Resource identifier */ + mrcp_resource_id resource_id; + /** External object associated with engine */ + void *obj; + /** Table of virtual methods */ + const mrcp_engine_method_vtable_t *method_vtable; + /** Codec manager */ + const mpf_codec_manager_t *codec_manager; + /** Dir layout structure */ + const apt_dir_layout_t *dir_layout; + /** Pool to allocate memory from */ + apr_pool_t *pool; +}; + +/** Create resource engine */ +mrcp_resource_engine_t* mrcp_resource_engine_create( + mrcp_resource_id resource_id, + void *obj, + const mrcp_engine_method_vtable_t *vtable, + apr_pool_t *pool); + +/** Destroy resource engine */ +static APR_INLINE apt_bool_t mrcp_resource_engine_destroy(mrcp_resource_engine_t *engine) +{ + return engine->method_vtable->destroy(engine); +} + +/** Open resource engine */ +static APR_INLINE apt_bool_t mrcp_resource_engine_open(mrcp_resource_engine_t *engine) +{ + return engine->method_vtable->open(engine); +} + +/** Close resource engine */ +static APR_INLINE apt_bool_t mrcp_resource_engine_close(mrcp_resource_engine_t *engine) +{ + return engine->method_vtable->close(engine); +} + +/** Create engine channel */ +mrcp_engine_channel_t* mrcp_engine_channel_create( + mrcp_resource_engine_t *engine, + const mrcp_engine_channel_method_vtable_t *method_vtable, + void *method_obj, + mpf_termination_t *termination, + apr_pool_t *pool); + +/** Create engine channel and source media termination */ +mrcp_engine_channel_t* mrcp_engine_source_channel_create( + mrcp_resource_engine_t *engine, + const mrcp_engine_channel_method_vtable_t *channel_vtable, + const mpf_audio_stream_vtable_t *stream_vtable, + void *method_obj, + mpf_codec_descriptor_t *codec_descriptor, + apr_pool_t *pool); + +/** Create engine channel and sink media termination */ +mrcp_engine_channel_t* mrcp_engine_sink_channel_create( + mrcp_resource_engine_t *engine, + const mrcp_engine_channel_method_vtable_t *channel_vtable, + const mpf_audio_stream_vtable_t *stream_vtable, + void *method_obj, + mpf_codec_descriptor_t *codec_descriptor, + apr_pool_t *pool); + +/** Destroy engine channel */ +static APR_INLINE apt_bool_t mrcp_engine_channel_destroy(mrcp_engine_channel_t *channel) +{ + return channel->method_vtable->destroy(channel); +} + +/** Open engine channel */ +static APR_INLINE apt_bool_t mrcp_engine_channel_open(mrcp_engine_channel_t *channel) +{ + return channel->method_vtable->open(channel); +} + +/** Close engine channel */ +static APR_INLINE apt_bool_t mrcp_engine_channel_close(mrcp_engine_channel_t *channel) +{ + return channel->method_vtable->close(channel); +} + +/** Process request */ +static APR_INLINE apt_bool_t mrcp_engine_channel_request_process(mrcp_engine_channel_t *channel, mrcp_message_t *message) +{ + return channel->method_vtable->process_request(channel,message); +} + +/** Send channel open response */ +static APR_INLINE apt_bool_t mrcp_engine_channel_open_respond(mrcp_engine_channel_t *channel, apt_bool_t status) +{ + return channel->event_vtable->on_open(channel,status); +} + +/** Send channel close response */ +static APR_INLINE apt_bool_t mrcp_engine_channel_close_respond(mrcp_engine_channel_t *channel) +{ + return channel->event_vtable->on_close(channel); +} + +/** Send response/event message */ +static APR_INLINE apt_bool_t mrcp_engine_channel_message_send(mrcp_engine_channel_t *channel, mrcp_message_t *message) +{ + return channel->event_vtable->on_message(channel,message); +} + +/** Get codec of the audio source stream */ +mpf_codec_t* mrcp_engine_source_stream_codec_get(mrcp_engine_channel_t *channel); + +/** Get codec of the audio sink stream */ +mpf_codec_t* mrcp_engine_sink_stream_codec_get(mrcp_engine_channel_t *channel); + + +APT_END_EXTERN_C + +#endif /*__MRCP_RESOURCE_ENGINE_H__*/ diff --git a/libs/unimrcp/libs/mrcp-engine/include/mrcp_resource_plugin.h b/libs/unimrcp/libs/mrcp-engine/include/mrcp_resource_plugin.h new file mode 100644 index 0000000000..4dcc3be182 --- /dev/null +++ b/libs/unimrcp/libs/mrcp-engine/include/mrcp_resource_plugin.h @@ -0,0 +1,110 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_RESOURCE_PLUGIN_H__ +#define __MRCP_RESOURCE_PLUGIN_H__ + +/** + * @file mrcp_resource_plugin.h + * @brief MRCP Resource Engine Plugin + */ + +#include "apr_version.h" +#include "apt_log.h" + +APT_BEGIN_EXTERN_C + +/** Plugin export defines */ +#ifdef WIN32 +#define MRCP_PLUGIN_DECLARE(type) EXTERN_C __declspec(dllexport) type +#else +#define MRCP_PLUGIN_DECLARE(type) type +#endif + +/** Symbol name of the main entry point in plugin DSO */ +#define MRCP_PLUGIN_ENGINE_SYM_NAME "mrcp_plugin_create" +/** Symbol name of the log accessor entry point in plugin DSO */ +#define MRCP_PLUGIN_LOGGER_SYM_NAME "mrcp_plugin_logger_set" + +/** MRCP resource engine declaration */ +typedef struct mrcp_resource_engine_t mrcp_resource_engine_t; +/** Prototype of resource engine creator (entry point of plugin DSO) */ +typedef mrcp_resource_engine_t* (*mrcp_plugin_creator_f)(apr_pool_t *pool); + +/** Prototype of resource engine creator (entry point of plugin DSO) */ +typedef apt_bool_t (*mrcp_plugin_log_accessor_f)(apt_logger_t *logger); + +/** Declare this macro in plugins to use log routine of the server */ +#define MRCP_PLUGIN_LOGGER_IMPLEMENT \ + MRCP_PLUGIN_DECLARE(apt_bool_t) mrcp_plugin_logger_set(apt_logger_t *logger) \ + { return apt_log_instance_set(logger); } + + +/** major version + * Major API changes that could cause compatibility problems for older + * plugins such as structure size changes. No binary compatibility is + * possible across a change in the major version. + */ +#define PLUGIN_MAJOR_VERSION 0 + +/** minor version + * Minor API changes that do not cause binary compatibility problems. + * Reset to 0 when upgrading PLUGIN_MAJOR_VERSION + */ +#define PLUGIN_MINOR_VERSION 4 + +/** patch level + * The Patch Level never includes API changes, simply bug fixes. + * Reset to 0 when upgrading PLUGIN_MINOR_VERSION + */ +#define PLUGIN_PATCH_VERSION 0 + + +/** + * Check at compile time if the plugin version is at least a certain + * level. + */ +#define PLUGIN_VERSION_AT_LEAST(major,minor,patch) \ +(((major) < PLUGIN_MAJOR_VERSION) \ + || ((major) == PLUGIN_MAJOR_VERSION && (minor) < PLUGIN_MINOR_VERSION) \ + || ((major) == PLUGIN_MAJOR_VERSION && (minor) == PLUGIN_MINOR_VERSION && (patch) <= PLUGIN_PATCH_VERSION)) + +/** The formatted string of plugin's version */ +#define PLUGIN_VERSION_STRING \ + APR_STRINGIFY(PLUGIN_MAJOR_VERSION) "." \ + APR_STRINGIFY(PLUGIN_MINOR_VERSION) "." \ + APR_STRINGIFY(PLUGIN_PATCH_VERSION) + +/** Plugin version */ +typedef apr_version_t mrcp_plugin_version_t; + +/** Get plugin version */ +static APR_INLINE void mrcp_plugin_version_get(mrcp_plugin_version_t *version) +{ + version->major = PLUGIN_MAJOR_VERSION; + version->minor = PLUGIN_MINOR_VERSION; + version->patch = PLUGIN_PATCH_VERSION; +} + +/** Check plugin version */ +static APR_INLINE int mrcp_plugin_version_check(mrcp_plugin_version_t *version) +{ + return PLUGIN_VERSION_AT_LEAST(version->major,version->minor,version->patch); +} + +APT_END_EXTERN_C + +#endif /*__MRCP_RESOURCE_PLUGIN_H__*/ diff --git a/libs/unimrcp/libs/mrcp-engine/mrcpengine.vcproj b/libs/unimrcp/libs/mrcp-engine/mrcpengine.vcproj new file mode 100644 index 0000000000..1ac2946774 --- /dev/null +++ b/libs/unimrcp/libs/mrcp-engine/mrcpengine.vcproj @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/libs/mrcp-engine/src/mrcp_resource_engine.c b/libs/unimrcp/libs/mrcp-engine/src/mrcp_resource_engine.c new file mode 100644 index 0000000000..280e2a68cd --- /dev/null +++ b/libs/unimrcp/libs/mrcp-engine/src/mrcp_resource_engine.c @@ -0,0 +1,150 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mrcp_resource_engine.h" +#include "mpf_codec_manager.h" + +/** Create resource engine */ +mrcp_resource_engine_t* mrcp_resource_engine_create( + mrcp_resource_id resource_id, + void *obj, + const mrcp_engine_method_vtable_t *vtable, + apr_pool_t *pool) +{ + mrcp_resource_engine_t *engine = apr_palloc(pool,sizeof(mrcp_resource_engine_t)); + mrcp_plugin_version_get(&engine->plugin_version); + engine->resource_id = resource_id; + engine->obj = obj; + engine->method_vtable =vtable; + engine->codec_manager = NULL; + engine->dir_layout = NULL; + engine->pool = pool; + return engine; +} + +/** Create engine channel */ +mrcp_engine_channel_t* mrcp_engine_channel_create( + mrcp_resource_engine_t *engine, + const mrcp_engine_channel_method_vtable_t *method_vtable, + void *method_obj, + mpf_termination_t *termination, + apr_pool_t *pool) +{ + mrcp_engine_channel_t *channel = apr_palloc(pool,sizeof(mrcp_engine_channel_t)); + channel->method_vtable = method_vtable; + channel->method_obj = method_obj; + channel->event_vtable = NULL; + channel->event_obj = NULL; + channel->termination = termination; + channel->engine = engine; + channel->pool = pool; + return channel; +} + +/** Create engine channel and source media termination */ +mrcp_engine_channel_t* mrcp_engine_source_channel_create( + mrcp_resource_engine_t *engine, + const mrcp_engine_channel_method_vtable_t *channel_vtable, + const mpf_audio_stream_vtable_t *stream_vtable, + void *method_obj, + mpf_codec_descriptor_t *codec_descriptor, + apr_pool_t *pool) +{ + mpf_audio_stream_t *audio_stream; + mpf_termination_t *termination; + /* create audio stream */ + audio_stream = mpf_audio_stream_create( + method_obj, /* object to associate */ + stream_vtable, /* virtual methods table of audio stream */ + STREAM_MODE_RECEIVE, /* stream mode/direction */ + pool); /* pool to allocate memory from */ + + if(engine->codec_manager) { + audio_stream->rx_codec = mpf_codec_manager_codec_get(engine->codec_manager,codec_descriptor,pool); + } + + /* create media termination */ + termination = mpf_raw_termination_create( + NULL, /* no object to associate */ + audio_stream, /* audio stream */ + NULL, /* no video stream */ + pool); /* pool to allocate memory from */ + + /* create engine channel base */ + return mrcp_engine_channel_create( + engine, /* resource engine */ + channel_vtable, /* virtual methods table of engine channel */ + method_obj, /* object to associate */ + termination, /* media termination, used to terminate audio stream */ + pool); /* pool to allocate memory from */ +} + +/** Create engine channel and sink media termination */ +mrcp_engine_channel_t* mrcp_engine_sink_channel_create( + mrcp_resource_engine_t *engine, + const mrcp_engine_channel_method_vtable_t *channel_vtable, + const mpf_audio_stream_vtable_t *stream_vtable, + void *method_obj, + mpf_codec_descriptor_t *codec_descriptor, + apr_pool_t *pool) +{ + mpf_audio_stream_t *audio_stream; + mpf_termination_t *termination; + + /* create audio stream */ + audio_stream = mpf_audio_stream_create( + method_obj, /* object to associate */ + stream_vtable, /* virtual methods table of audio stream */ + STREAM_MODE_SEND, /* stream mode/direction */ + pool); /* pool to allocate memory from */ + + if(engine->codec_manager) { + audio_stream->tx_codec = mpf_codec_manager_codec_get(engine->codec_manager,codec_descriptor,pool); + } + + /* create media termination */ + termination = mpf_raw_termination_create( + NULL, /* no object to associate */ + audio_stream, /* audio stream */ + NULL, /* no video stream */ + pool); /* pool to allocate memory from */ + + /* create engine channel base */ + return mrcp_engine_channel_create( + engine, /* resource engine */ + channel_vtable, /* virtual methods table of engine channel */ + method_obj, /* object to associate */ + termination, /* media termination, used to terminate audio stream */ + pool); /* pool to allocate memory from */ +} + +/** Get codec of the audio source stream */ +mpf_codec_t* mrcp_engine_source_stream_codec_get(mrcp_engine_channel_t *channel) +{ + if(channel && channel->termination && channel->termination->audio_stream) { + return channel->termination->audio_stream->rx_codec; + } + return NULL; +} + +/** Get codec of the audio sink stream */ +mpf_codec_t* mrcp_engine_sink_stream_codec_get(mrcp_engine_channel_t *channel) +{ + if(channel && channel->termination && channel->termination->audio_stream) { + return channel->termination->audio_stream->tx_codec; + } + return NULL; +} diff --git a/libs/unimrcp/libs/mrcp-server/Makefile.am b/libs/unimrcp/libs/mrcp-server/Makefile.am new file mode 100644 index 0000000000..ba1c8ae83d --- /dev/null +++ b/libs/unimrcp/libs/mrcp-server/Makefile.am @@ -0,0 +1,22 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_srcdir)/libs/mrcp-server/include \ + -I$(top_srcdir)/libs/mrcp-engine/include \ + -I$(top_srcdir)/libs/mrcp-signaling/include \ + -I$(top_srcdir)/libs/mrcpv2-transport/include \ + -I$(top_srcdir)/libs/mrcp/include \ + -I$(top_srcdir)/libs/mrcp/message/include \ + -I$(top_srcdir)/libs/mrcp/control/include \ + -I$(top_srcdir)/libs/mrcp/resources/include \ + -I$(top_srcdir)/libs/mpf/include \ + -I$(top_srcdir)/libs/apr-toolkit/include \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) + +noinst_LTLIBRARIES = libmrcpserver.la + +include_HEADERS = include/mrcp_server_types.h \ + include/mrcp_server.h \ + include/mrcp_server_session.h + +libmrcpserver_la_SOURCES = src/mrcp_server.c \ + src/mrcp_server_session.c diff --git a/libs/unimrcp/libs/mrcp-server/include/mrcp_server.h b/libs/unimrcp/libs/mrcp-server/include/mrcp_server.h new file mode 100644 index 0000000000..e4946364b1 --- /dev/null +++ b/libs/unimrcp/libs/mrcp-server/include/mrcp_server.h @@ -0,0 +1,197 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_SERVER_H__ +#define __MRCP_SERVER_H__ + +/** + * @file mrcp_server.h + * @brief MRCP Server + */ + +#include "mrcp_server_types.h" +#include "mrcp_resource_engine.h" +#include "apt_task.h" + +APT_BEGIN_EXTERN_C + +/** + * Create MRCP server instance. + * @return the created server instance + */ +MRCP_DECLARE(mrcp_server_t*) mrcp_server_create(apt_dir_layout_t *dir_layout); + +/** + * Start message processing loop. + * @param server the MRCP server to start + * @return the created server instance + */ +MRCP_DECLARE(apt_bool_t) mrcp_server_start(mrcp_server_t *server); + +/** + * Shutdown message processing loop. + * @param server the MRCP server to shutdown + */ +MRCP_DECLARE(apt_bool_t) mrcp_server_shutdown(mrcp_server_t *server); + +/** + * Destroy MRCP server. + * @param server the MRCP server to destroy + */ +MRCP_DECLARE(apt_bool_t) mrcp_server_destroy(mrcp_server_t *server); + + +/** + * Register MRCP resource factory. + * @param server the MRCP server to set resource factory for + * @param resource_factory the resource factory to set + */ +MRCP_DECLARE(apt_bool_t) mrcp_server_resource_factory_register(mrcp_server_t *server, mrcp_resource_factory_t *resource_factory); + +/** + * Register MRCP resource engine. + * @param server the MRCP server to set engine for + * @param engine the resource engine to set + * @param name the name of the resource engine + */ +MRCP_DECLARE(apt_bool_t) mrcp_server_resource_engine_register(mrcp_server_t *server, mrcp_resource_engine_t *engine, const char *name); + +/** + * Register codec manager. + * @param server the MRCP server to set codec manager for + * @param codec_manager the codec manager to set + */ +MRCP_DECLARE(apt_bool_t) mrcp_server_codec_manager_register(mrcp_server_t *server, mpf_codec_manager_t *codec_manager); + +/** + * Get registered codec manager. + * @param server the MRCP server to get codec manager from + */ +MRCP_DECLARE(const mpf_codec_manager_t*) mrcp_server_codec_manager_get(mrcp_server_t *server); + +/** + * Register media engine. + * @param server the MRCP server to set media engine for + * @param media_engine the media engine to set + * @param name the name of the media engine + */ +MRCP_DECLARE(apt_bool_t) mrcp_server_media_engine_register(mrcp_server_t *server, mpf_engine_t *media_engine, const char *name); + +/** + * Register RTP termination factory. + * @param server the MRCP server to set termination factory for + * @param rtp_termination_factory the termination factory + * @param name the name of the factory + */ +MRCP_DECLARE(apt_bool_t) mrcp_server_rtp_factory_register(mrcp_server_t *server, mpf_termination_factory_t *rtp_termination_factory, const char *name); + +/** + * Register MRCP signaling agent. + * @param server the MRCP server to set signaling agent for + * @param signaling_agent the signaling agent to set + * @param name the name of the agent + */ +MRCP_DECLARE(apt_bool_t) mrcp_server_signaling_agent_register(mrcp_server_t *server, mrcp_sig_agent_t *signaling_agent, const char *name); + +/** + * Register MRCP connection agent (MRCPv2 only). + * @param server the MRCP server to set connection agent for + * @param connection_agent the connection agent to set + * @param name the name of the agent + */ +MRCP_DECLARE(apt_bool_t) mrcp_server_connection_agent_register(mrcp_server_t *server, mrcp_connection_agent_t *connection_agent, const char *name); + +/** Create MRCP profile */ +MRCP_DECLARE(mrcp_profile_t*) mrcp_server_profile_create( + mrcp_resource_factory_t *resource_factory, + mrcp_sig_agent_t *signaling_agent, + mrcp_connection_agent_t *connection_agent, + mpf_engine_t *media_engine, + mpf_termination_factory_t *rtp_factory, + apr_pool_t *pool); + +/** + * Register MRCP profile. + * @param server the MRCP server to set profile for + * @param profile the profile to set + * @param plugin_map the map of resource engines (plugins) + * @param name the name of the profile + */ +MRCP_DECLARE(apt_bool_t) mrcp_server_profile_register( + mrcp_server_t *server, + mrcp_profile_t *profile, + apr_table_t *plugin_map, + const char *name); + +/** + * Register resource engine plugin. + * @param server the MRCP server to set engine for + * @param path the path to plugin + * @param name the name of the plugin + */ +MRCP_DECLARE(apt_bool_t) mrcp_server_plugin_register(mrcp_server_t *server, const char *path, const char *name); + +/** + * Get memory pool. + * @param server the MRCP server to get memory pool from + */ +MRCP_DECLARE(apr_pool_t*) mrcp_server_memory_pool_get(mrcp_server_t *server); + +/** + * Get resource engine by name. + * @param server the MRCP server to get resource engine from + * @param name the name of the resource engine to lookup + */ +MRCP_DECLARE(mrcp_resource_engine_t*) mrcp_server_resource_engine_get(mrcp_server_t *server, const char *name); + +/** + * Get media engine by name. + * @param server the MRCP server to get media engine from + * @param name the name of the media engine to lookup + */ +MRCP_DECLARE(mpf_engine_t*) mrcp_server_media_engine_get(mrcp_server_t *server, const char *name); + +/** + * Get RTP termination factory by name. + * @param server the MRCP server to get from + * @param name the name to lookup + */ +MRCP_DECLARE(mpf_termination_factory_t*) mrcp_server_rtp_factory_get(mrcp_server_t *server, const char *name); + +/** + * Get signaling agent by name. + * @param server the MRCP server to get from + * @param name the name to lookup + */ +MRCP_DECLARE(mrcp_sig_agent_t*) mrcp_server_signaling_agent_get(mrcp_server_t *server, const char *name); + +/** + * Get connection agent by name. + * @param server the MRCP server to get from + * @param name the name to lookup + */ +MRCP_DECLARE(mrcp_connection_agent_t*) mrcp_server_connection_agent_get(mrcp_server_t *server, const char *name); + +/** + * Get profile by name. + * @param server the MRCP client to get from + * @param name the name to lookup + */ +MRCP_DECLARE(mrcp_profile_t*) mrcp_server_profile_get(mrcp_server_t *server, const char *name); + +APT_END_EXTERN_C + +#endif /*__MRCP_SERVER_H__*/ diff --git a/libs/unimrcp/libs/mrcp-server/include/mrcp_server_session.h b/libs/unimrcp/libs/mrcp-server/include/mrcp_server_session.h new file mode 100644 index 0000000000..dcc0544198 --- /dev/null +++ b/libs/unimrcp/libs/mrcp-server/include/mrcp_server_session.h @@ -0,0 +1,141 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_SERVER_SESSION_H__ +#define __MRCP_SERVER_SESSION_H__ + +/** + * @file mrcp_server_session.h + * @brief MRCP Server Session + */ + +#include +#include "mrcp_session.h" +#include "mpf_message.h" +#include "apt_task.h" +#include "apt_obj_list.h" + + +APT_BEGIN_EXTERN_C + +/** Opaque MRCP channel declaration */ +typedef struct mrcp_channel_t mrcp_channel_t; +/** MRCP server session declaration */ +typedef struct mrcp_server_session_t mrcp_server_session_t; +/** MRCP signaling message declaration */ +typedef struct mrcp_signaling_message_t mrcp_signaling_message_t; + +/** Enumeration of signaling task messages */ +typedef enum { + SIGNALING_MESSAGE_OFFER, + SIGNALING_MESSAGE_CONTROL, + SIGNALING_MESSAGE_TERMINATE, +} mrcp_signaling_message_type_e; + +/** MRCP signaling message */ +struct mrcp_signaling_message_t { + /** Signaling message type */ + mrcp_signaling_message_type_e type; + + /** Session */ + mrcp_server_session_t *session; + /** Descriptor */ + mrcp_session_descriptor_t *descriptor; + + /** Channel */ + mrcp_channel_t *channel; + /** MRCP message */ + mrcp_message_t *message; +}; + + +/** MRCP server session */ +struct mrcp_server_session_t { + /** Session base */ + mrcp_session_t base; + /** MRCP server */ + mrcp_server_t *server; + /** MRCP profile */ + mrcp_profile_t *profile; + + /** Media context */ + mpf_context_t *context; + + /** Media termination array */ + apr_array_header_t *terminations; + /** MRCP control channel array */ + apr_array_header_t *channels; + + /** In-progress signaling request */ + mrcp_signaling_message_t *active_request; + /** Signaling request queue */ + apt_obj_list_t *request_queue; + + /** In-progress offer */ + mrcp_session_descriptor_t *offer; + /** In-progres answer */ + mrcp_session_descriptor_t *answer; + + /** Number of in-progress answer requests (flags) */ + apr_size_t answer_flag_count; + /** Number of in-progress terminate requests (flags) */ + apr_size_t terminate_flag_count; +}; + +/** MRCP profile */ +struct mrcp_profile_t { + /** Table of resource engines (mrcp_resource_engine_t*) */ + apr_hash_t *engine_table; + /** MRCP resource factory */ + mrcp_resource_factory_t *resource_factory; + /** Media processing engine */ + mpf_engine_t *media_engine; + /** RTP termination factory */ + mpf_termination_factory_t *rtp_termination_factory; + /** Signaling agent */ + mrcp_sig_agent_t *signaling_agent; + /** Connection agent */ + mrcp_connection_agent_t *connection_agent; +}; + +/** Create server session */ +mrcp_server_session_t* mrcp_server_session_create(); + +/** Process signaling message */ +apt_bool_t mrcp_server_signaling_message_process(mrcp_signaling_message_t *signaling_message); +/** Process MPF message */ +apt_bool_t mrcp_server_mpf_message_process(mpf_message_t *mpf_message); + +/** Process channel modify event */ +apt_bool_t mrcp_server_on_channel_modify(mrcp_channel_t *channel, mrcp_control_descriptor_t *answer, apt_bool_t status); +/** Process channel remove event */ +apt_bool_t mrcp_server_on_channel_remove(mrcp_channel_t *channel, apt_bool_t status); +/** Process channel message receive */ +apt_bool_t mrcp_server_on_channel_message(mrcp_channel_t *channel, mrcp_message_t *message); + +/** Process channel open event */ +apt_bool_t mrcp_server_on_engine_channel_open(mrcp_channel_t *channel, apt_bool_t status); +/** Process channel close event */ +apt_bool_t mrcp_server_on_engine_channel_close(mrcp_channel_t *channel); +/** Process message receive event */ +apt_bool_t mrcp_server_on_engine_channel_message(mrcp_channel_t *channel, mrcp_message_t *message); + +/** Get session by channel */ +mrcp_session_t* mrcp_server_channel_session_get(mrcp_channel_t *channel); + +APT_END_EXTERN_C + +#endif /*__MRCP_SERVER_SESSION_H__*/ diff --git a/libs/unimrcp/libs/mrcp-server/include/mrcp_server_types.h b/libs/unimrcp/libs/mrcp-server/include/mrcp_server_types.h new file mode 100644 index 0000000000..ac640fc750 --- /dev/null +++ b/libs/unimrcp/libs/mrcp-server/include/mrcp_server_types.h @@ -0,0 +1,40 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_SERVER_TYPES_H__ +#define __MRCP_SERVER_TYPES_H__ + +/** + * @file mrcp_server_types.h + * @brief MRCP Server Types + */ + +#include "mrcp_sig_types.h" +#include "mrcp_connection_types.h" +#include "mpf_types.h" + +APT_BEGIN_EXTERN_C + +/** Opaque MRCP server declaration */ +typedef struct mrcp_server_t mrcp_server_t; + +/** Opaque MRCP profile declaration */ +typedef struct mrcp_profile_t mrcp_profile_t; + + +APT_END_EXTERN_C + +#endif /*__MRCP_SERVER_TYPES_H__*/ diff --git a/libs/unimrcp/libs/mrcp-server/mrcpserver.vcproj b/libs/unimrcp/libs/mrcp-server/mrcpserver.vcproj new file mode 100644 index 0000000000..9cd5de4f19 --- /dev/null +++ b/libs/unimrcp/libs/mrcp-server/mrcpserver.vcproj @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/libs/mrcp-server/src/mrcp_server.c b/libs/unimrcp/libs/mrcp-server/src/mrcp_server.c new file mode 100644 index 0000000000..6ef74b2c5b --- /dev/null +++ b/libs/unimrcp/libs/mrcp-server/src/mrcp_server.c @@ -0,0 +1,930 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include "mrcp_server.h" +#include "mrcp_server_session.h" +#include "mrcp_message.h" +#include "mrcp_resource_factory.h" +#include "mrcp_sig_agent.h" +#include "mrcp_server_connection.h" +#include "mpf_engine.h" +#include "apt_pool.h" +#include "apt_consumer_task.h" +#include "apt_obj_list.h" +#include "apt_log.h" + +#define SERVER_TASK_NAME "MRCP Server" + +/** MRCP server */ +struct mrcp_server_t { + /** Main message processing task */ + apt_consumer_task_t *task; + + /** MRCP resource factory */ + mrcp_resource_factory_t *resource_factory; + /** Codec manager */ + mpf_codec_manager_t *codec_manager; + /** Table of resource engines (mrcp_resource_engine_t*) */ + apr_hash_t *resource_engine_table; + /** Table of media processing engines (mpf_engine_t*) */ + apr_hash_t *media_engine_table; + /** Table of RTP termination factories (mpf_termination_factory_t*) */ + apr_hash_t *rtp_factory_table; + /** Table of signaling agents (mrcp_sig_agent_t*) */ + apr_hash_t *sig_agent_table; + /** Table of connection agents (mrcp_connection_agent_t*) */ + apr_hash_t *cnt_agent_table; + /** Table of profiles (mrcp_profile_t*) */ + apr_hash_t *profile_table; + /** Table of plugins (apr_dso_handle_t*) */ + apr_hash_t *plugin_table; + + /** Table of sessions */ + apr_hash_t *session_table; + + /** Connection task message pool */ + apt_task_msg_pool_t *connection_msg_pool; + /** Resource engine task message pool */ + apt_task_msg_pool_t *resource_engine_msg_pool; + + /** Dir layout structure */ + apt_dir_layout_t *dir_layout; + /** Time server started at */ + apr_time_t start_time; + /** Memory pool */ + apr_pool_t *pool; +}; + + +typedef enum { + MRCP_SERVER_SIGNALING_TASK_MSG = TASK_MSG_USER, + MRCP_SERVER_CONNECTION_TASK_MSG, + MRCP_SERVER_RESOURCE_ENGINE_TASK_MSG, + MRCP_SERVER_MEDIA_TASK_MSG +} mrcp_server_task_msg_type_e; + + +static apt_bool_t mrcp_server_offer_signal(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor); +static apt_bool_t mrcp_server_terminate_signal(mrcp_session_t *session); +static apt_bool_t mrcp_server_control_signal(mrcp_session_t *session, mrcp_message_t *message); + +static const mrcp_session_request_vtable_t session_request_vtable = { + mrcp_server_offer_signal, + mrcp_server_terminate_signal, + mrcp_server_control_signal +}; + + +/* Connection agent interface */ +typedef enum { + CONNECTION_AGENT_TASK_MSG_ADD_CHANNEL, + CONNECTION_AGENT_TASK_MSG_MODIFY_CHANNEL, + CONNECTION_AGENT_TASK_MSG_REMOVE_CHANNEL, + CONNECTION_AGENT_TASK_MSG_RECEIVE_MESSAGE, + CONNECTION_AGENT_TASK_MSG_TERMINATE +} connection_agent_task_msg_type_e; + +typedef struct connection_agent_task_msg_data_t connection_agent_task_msg_data_t; +struct connection_agent_task_msg_data_t { + mrcp_channel_t *channel; + mrcp_control_descriptor_t *descriptor; + mrcp_message_t *message; + apt_bool_t status; +}; + +static apt_bool_t mrcp_server_channel_add_signal(mrcp_control_channel_t *channel, mrcp_control_descriptor_t *descriptor, apt_bool_t status); +static apt_bool_t mrcp_server_channel_modify_signal(mrcp_control_channel_t *channel, mrcp_control_descriptor_t *descriptor, apt_bool_t status); +static apt_bool_t mrcp_server_channel_remove_signal(mrcp_control_channel_t *channel, apt_bool_t status); +static apt_bool_t mrcp_server_message_signal(mrcp_control_channel_t *channel, mrcp_message_t *message); + +static const mrcp_connection_event_vtable_t connection_method_vtable = { + mrcp_server_channel_add_signal, + mrcp_server_channel_modify_signal, + mrcp_server_channel_remove_signal, + mrcp_server_message_signal +}; + + +/* Resource engine interface */ +typedef enum { + RESOURCE_ENGINE_TASK_MSG_OPEN_CHANNEL, + RESOURCE_ENGINE_TASK_MSG_CLOSE_CHANNEL, + RESOURCE_ENGINE_TASK_MSG_MESSAGE +} resource_engine_task_msg_type_e; + +typedef struct resource_engine_task_msg_data_t resource_engine_task_msg_data_t; +struct resource_engine_task_msg_data_t { + mrcp_channel_t *channel; + apt_bool_t status; + mrcp_message_t *mrcp_message; +}; + +static apt_bool_t mrcp_server_channel_open_signal(mrcp_engine_channel_t *channel, apt_bool_t status); +static apt_bool_t mrcp_server_channel_close_signal(mrcp_engine_channel_t *channel); +static apt_bool_t mrcp_server_channel_message_signal(mrcp_engine_channel_t *channel, mrcp_message_t *message); + +const mrcp_engine_channel_event_vtable_t engine_channel_vtable = { + mrcp_server_channel_open_signal, + mrcp_server_channel_close_signal, + mrcp_server_channel_message_signal +}; + +/* Task interface */ +static void mrcp_server_on_start_complete(apt_task_t *task); +static void mrcp_server_on_terminate_complete(apt_task_t *task); +static apt_bool_t mrcp_server_msg_process(apt_task_t *task, apt_task_msg_t *msg); + +static mrcp_session_t* mrcp_server_sig_agent_session_create(mrcp_sig_agent_t *signaling_agent); + + +/** Create MRCP server instance */ +MRCP_DECLARE(mrcp_server_t*) mrcp_server_create(apt_dir_layout_t *dir_layout) +{ + mrcp_server_t *server; + apr_pool_t *pool; + apt_task_t *task; + apt_task_vtable_t *vtable; + apt_task_msg_pool_t *msg_pool; + + pool = apt_pool_create(); + if(!pool) { + return NULL; + } + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create "SERVER_TASK_NAME); + server = apr_palloc(pool,sizeof(mrcp_server_t)); + server->pool = pool; + server->dir_layout = dir_layout; + server->resource_factory = NULL; + server->resource_engine_table = NULL; + server->media_engine_table = NULL; + server->rtp_factory_table = NULL; + server->sig_agent_table = NULL; + server->cnt_agent_table = NULL; + server->profile_table = NULL; + server->plugin_table = NULL; + server->session_table = NULL; + server->connection_msg_pool = NULL; + server->resource_engine_msg_pool = NULL; + + msg_pool = apt_task_msg_pool_create_dynamic(0,pool); + + server->task = apt_consumer_task_create(server,msg_pool,pool); + if(!server->task) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Server Task"); + return NULL; + } + task = apt_consumer_task_base_get(server->task); + apt_task_name_set(task,SERVER_TASK_NAME); + vtable = apt_task_vtable_get(task); + if(vtable) { + vtable->process_msg = mrcp_server_msg_process; + vtable->on_start_complete = mrcp_server_on_start_complete; + vtable->on_terminate_complete = mrcp_server_on_terminate_complete; + } + + server->resource_engine_table = apr_hash_make(server->pool); + server->media_engine_table = apr_hash_make(server->pool); + server->rtp_factory_table = apr_hash_make(server->pool); + server->sig_agent_table = apr_hash_make(server->pool); + server->cnt_agent_table = apr_hash_make(server->pool); + + server->profile_table = apr_hash_make(server->pool); + server->plugin_table = apr_hash_make(server->pool); + + server->session_table = apr_hash_make(server->pool); + return server; +} + +/** Start message processing loop */ +MRCP_DECLARE(apt_bool_t) mrcp_server_start(mrcp_server_t *server) +{ + apt_task_t *task; + if(!server || !server->task) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Invalid Server"); + return FALSE; + } + server->start_time = apr_time_now(); + task = apt_consumer_task_base_get(server->task); + if(apt_task_start(task) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Start Server Task"); + return FALSE; + } + return TRUE; +} + +/** Shutdown message processing loop */ +MRCP_DECLARE(apt_bool_t) mrcp_server_shutdown(mrcp_server_t *server) +{ + apt_task_t *task; + apr_time_t uptime; + if(!server || !server->task) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Invalid Server"); + return FALSE; + } + task = apt_consumer_task_base_get(server->task); + if(apt_task_terminate(task,TRUE) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Shutdown Server Task"); + return FALSE; + } + server->session_table = NULL; + uptime = apr_time_now() - server->start_time; + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Server Uptime [%d sec]", apr_time_sec(uptime)); + return TRUE; +} + +/** Destroy MRCP server */ +MRCP_DECLARE(apt_bool_t) mrcp_server_destroy(mrcp_server_t *server) +{ + apt_task_t *task; + if(!server || !server->task) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Invalid Server"); + return FALSE; + } + task = apt_consumer_task_base_get(server->task); + apt_task_destroy(task); + + apr_pool_destroy(server->pool); + return TRUE; +} + +/** Register MRCP resource factory */ +MRCP_DECLARE(apt_bool_t) mrcp_server_resource_factory_register(mrcp_server_t *server, mrcp_resource_factory_t *resource_factory) +{ + if(!resource_factory) { + return FALSE; + } + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Register Resource Factory"); + server->resource_factory = resource_factory; + return TRUE; +} + +/** Register MRCP resource engine */ +MRCP_DECLARE(apt_bool_t) mrcp_server_resource_engine_register(mrcp_server_t *server, mrcp_resource_engine_t *engine, const char *name) +{ + if(!engine || !name) { + return FALSE; + } + if(!server->resource_engine_msg_pool) { + server->resource_engine_msg_pool = apt_task_msg_pool_create_dynamic(sizeof(resource_engine_task_msg_data_t),server->pool); + } + engine->codec_manager = server->codec_manager; + engine->dir_layout = server->dir_layout; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Register Resource Engine [%s]",name); + apr_hash_set(server->resource_engine_table,name,APR_HASH_KEY_STRING,engine); + return TRUE; +} + +/** Get resource engine by name */ +MRCP_DECLARE(mrcp_resource_engine_t*) mrcp_server_resource_engine_get(mrcp_server_t *server, const char *name) +{ + return apr_hash_get(server->resource_engine_table,name,APR_HASH_KEY_STRING); +} + +/** Register codec manager */ +MRCP_DECLARE(apt_bool_t) mrcp_server_codec_manager_register(mrcp_server_t *server, mpf_codec_manager_t *codec_manager) +{ + if(!codec_manager) { + return FALSE; + } + server->codec_manager = codec_manager; + return TRUE; +} + +/** Get registered codec manager */ +MRCP_DECLARE(const mpf_codec_manager_t*) mrcp_server_codec_manager_get(mrcp_server_t *server) +{ + return server->codec_manager; +} + +/** Register media engine */ +MRCP_DECLARE(apt_bool_t) mrcp_server_media_engine_register(mrcp_server_t *server, mpf_engine_t *media_engine, const char *name) +{ + if(!media_engine || !name) { + return FALSE; + } + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Register Media Engine [%s]",name); + mpf_engine_codec_manager_register(media_engine,server->codec_manager); + apr_hash_set(server->media_engine_table,name,APR_HASH_KEY_STRING,media_engine); + mpf_engine_task_msg_type_set(media_engine,MRCP_SERVER_MEDIA_TASK_MSG); + if(server->task) { + apt_task_t *media_task = mpf_task_get(media_engine); + apt_task_t *task = apt_consumer_task_base_get(server->task); + apt_task_add(task,media_task); + } + return TRUE; +} + +/** Get media engine by name */ +MRCP_DECLARE(mpf_engine_t*) mrcp_server_media_engine_get(mrcp_server_t *server, const char *name) +{ + return apr_hash_get(server->media_engine_table,name,APR_HASH_KEY_STRING); +} + +/** Register RTP termination factory */ +MRCP_DECLARE(apt_bool_t) mrcp_server_rtp_factory_register(mrcp_server_t *server, mpf_termination_factory_t *rtp_termination_factory, const char *name) +{ + if(!rtp_termination_factory || !name) { + return FALSE; + } + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Register RTP Termination Factory [%s]",name); + apr_hash_set(server->rtp_factory_table,name,APR_HASH_KEY_STRING,rtp_termination_factory); + return TRUE; +} + +/** Get RTP termination factory by name */ +MRCP_DECLARE(mpf_termination_factory_t*) mrcp_server_rtp_factory_get(mrcp_server_t *server, const char *name) +{ + return apr_hash_get(server->rtp_factory_table,name,APR_HASH_KEY_STRING); +} + +/** Register MRCP signaling agent */ +MRCP_DECLARE(apt_bool_t) mrcp_server_signaling_agent_register(mrcp_server_t *server, mrcp_sig_agent_t *signaling_agent, const char *name) +{ + if(!signaling_agent || !name) { + return FALSE; + } + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Register Signaling Agent [%s]",name); + signaling_agent->parent = server; + signaling_agent->resource_factory = server->resource_factory; + signaling_agent->create_server_session = mrcp_server_sig_agent_session_create; + signaling_agent->msg_pool = apt_task_msg_pool_create_dynamic(sizeof(mrcp_signaling_message_t*),server->pool); + apr_hash_set(server->sig_agent_table,name,APR_HASH_KEY_STRING,signaling_agent); + if(server->task) { + apt_task_t *task = apt_consumer_task_base_get(server->task); + apt_task_add(task,signaling_agent->task); + } + return TRUE; +} + +/** Get signaling agent by name */ +MRCP_DECLARE(mrcp_sig_agent_t*) mrcp_server_signaling_agent_get(mrcp_server_t *server, const char *name) +{ + return apr_hash_get(server->sig_agent_table,name,APR_HASH_KEY_STRING); +} + +/** Register MRCP connection agent (MRCPv2 only) */ +MRCP_DECLARE(apt_bool_t) mrcp_server_connection_agent_register(mrcp_server_t *server, mrcp_connection_agent_t *connection_agent, const char *name) +{ + if(!connection_agent || !name) { + return FALSE; + } + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Register Connection Agent [%s]",name); + mrcp_server_connection_resource_factory_set(connection_agent,server->resource_factory); + mrcp_server_connection_agent_handler_set(connection_agent,server,&connection_method_vtable); + server->connection_msg_pool = apt_task_msg_pool_create_dynamic(sizeof(connection_agent_task_msg_data_t),server->pool); + apr_hash_set(server->cnt_agent_table,name,APR_HASH_KEY_STRING,connection_agent); + if(server->task) { + apt_task_t *task = apt_consumer_task_base_get(server->task); + apt_task_t *connection_task = mrcp_server_connection_agent_task_get(connection_agent); + apt_task_add(task,connection_task); + } + return TRUE; +} + +/** Get connection agent by name */ +MRCP_DECLARE(mrcp_connection_agent_t*) mrcp_server_connection_agent_get(mrcp_server_t *server, const char *name) +{ + return apr_hash_get(server->cnt_agent_table,name,APR_HASH_KEY_STRING); +} + +/** Create MRCP profile */ +MRCP_DECLARE(mrcp_profile_t*) mrcp_server_profile_create( + mrcp_resource_factory_t *resource_factory, + mrcp_sig_agent_t *signaling_agent, + mrcp_connection_agent_t *connection_agent, + mpf_engine_t *media_engine, + mpf_termination_factory_t *rtp_factory, + apr_pool_t *pool) +{ + mrcp_profile_t *profile = apr_palloc(pool,sizeof(mrcp_profile_t)); + profile->resource_factory = resource_factory; + profile->engine_table = NULL; + profile->media_engine = media_engine; + profile->rtp_termination_factory = rtp_factory; + profile->signaling_agent = signaling_agent; + profile->connection_agent = connection_agent; + return profile; +} + +static apt_bool_t mrcp_server_engine_table_make(mrcp_server_t *server, mrcp_profile_t *profile, apr_table_t *plugin_map) +{ + int i; + const apt_str_t *resource_name; + const char *plugin_name = NULL; + mrcp_resource_engine_t *resource_engine; + + profile->engine_table = apr_hash_make(server->pool); + for(i=0; iresource_factory,i); + if(!resource_name) continue; + + resource_engine = NULL; + /* first, try to find engine by name specified in plugin map (if available) */ + if(plugin_map) { + plugin_name = apr_table_get(plugin_map,resource_name->buf); + if(plugin_name) { + resource_engine = mrcp_server_resource_engine_get(server,plugin_name); + } + } + + /* next, if no engine found, try to find the first available engine */ + if(!resource_engine) { + mrcp_resource_engine_t *cur_engine; + void *val; + apr_hash_index_t *it = apr_hash_first(server->pool,server->resource_engine_table); + /* walk through the list of engines */ + for(; it; it = apr_hash_next(it)) { + apr_hash_this(it,(void*)&plugin_name,NULL,&val); + cur_engine = val; + if(cur_engine && cur_engine->resource_id == (mrcp_resource_id)i) { + resource_engine = cur_engine; + break; + } + } + } + + if(resource_engine) { + if(plugin_name) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Assign Resource Engine [%s] [%s]",resource_name->buf,plugin_name); + } + apr_hash_set(profile->engine_table,resource_name->buf,resource_name->length,resource_engine); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No Resource Engine Available [%s]",resource_name->buf); + } + } + + return TRUE; +} + +/** Register MRCP profile */ +MRCP_DECLARE(apt_bool_t) mrcp_server_profile_register( + mrcp_server_t *server, + mrcp_profile_t *profile, + apr_table_t *plugin_map, + const char *name) +{ + if(!profile || !name) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Register Profile: no name"); + return FALSE; + } + if(!profile->resource_factory) { + profile->resource_factory = server->resource_factory; + } + mrcp_server_engine_table_make(server,profile,plugin_map); + + if(!profile->signaling_agent) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Register Profile [%s]: missing signaling agent",name); + return FALSE; + } + if(profile->signaling_agent->mrcp_version == MRCP_VERSION_2 && + !profile->connection_agent) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Register Profile [%s]: missing connection agent",name); + return FALSE; + } + if(!profile->media_engine) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Register Profile [%s]: missing media engine",name); + return FALSE; + } + if(!profile->rtp_termination_factory) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Register Profile [%s]: missing RTP factory",name); + return FALSE; + } + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Register Profile [%s]",name); + apr_hash_set(server->profile_table,name,APR_HASH_KEY_STRING,profile); + return TRUE; +} + +/** Get profile by name */ +MRCP_DECLARE(mrcp_profile_t*) mrcp_server_profile_get(mrcp_server_t *server, const char *name) +{ + return apr_hash_get(server->profile_table,name,APR_HASH_KEY_STRING); +} + +/** Register resource engine plugin */ +MRCP_DECLARE(apt_bool_t) mrcp_server_plugin_register(mrcp_server_t *server, const char *path, const char *name) +{ + apt_bool_t status = FALSE; + apr_dso_handle_t *plugin = NULL; + apr_dso_handle_sym_t func_handle = NULL; + mrcp_plugin_creator_f plugin_creator = NULL; + if(!path || !name) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Register Plugin: no name"); + return FALSE; + } + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Register Plugin [%s] [%s]",path,name); + if(apr_dso_load(&plugin,path,server->pool) == APR_SUCCESS) { + if(apr_dso_sym(&func_handle,plugin,MRCP_PLUGIN_ENGINE_SYM_NAME) == APR_SUCCESS) { + if(func_handle) { + plugin_creator = (mrcp_plugin_creator_f)(intptr_t)func_handle; + } + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Load DSO Symbol: "MRCP_PLUGIN_ENGINE_SYM_NAME); + apr_dso_unload(plugin); + return FALSE; + } + + if(apr_dso_sym(&func_handle,plugin,MRCP_PLUGIN_LOGGER_SYM_NAME) == APR_SUCCESS) { + if(func_handle) { + apt_logger_t *logger = apt_log_instance_get(); + mrcp_plugin_log_accessor_f log_accessor; + log_accessor = (mrcp_plugin_log_accessor_f)(intptr_t)func_handle; + log_accessor(logger); + } + } + } + else { + char derr[512] = ""; + apr_dso_error(plugin,derr,sizeof(derr)); + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Load DSO: %s", derr); + return FALSE; + } + + if(plugin_creator) { + mrcp_resource_engine_t *engine = plugin_creator(server->pool); + if(engine) { + if(mrcp_plugin_version_check(&engine->plugin_version)) { + status = TRUE; + mrcp_server_resource_engine_register(server,engine,name); + apr_hash_set(server->plugin_table,name,APR_HASH_KEY_STRING,plugin); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Incompatible Plugin Version [%d.%d.%d] < ["PLUGIN_VERSION_STRING"]", + engine->plugin_version.major, + engine->plugin_version.minor, + engine->plugin_version.patch); + } + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Resource Engine"); + } + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No Entry Point Found for Plugin"); + } + + if(status == FALSE) { + apr_dso_unload(plugin); + } + return status; +} + +MRCP_DECLARE(apr_pool_t*) mrcp_server_memory_pool_get(mrcp_server_t *server) +{ + return server->pool; +} + +void mrcp_server_session_add(mrcp_server_session_t *session) +{ + if(session->base.id.buf) { + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Add Session "APT_SID_FMT,session->base.id.buf); + apr_hash_set(session->server->session_table,session->base.id.buf,session->base.id.length,session); + } +} + +void mrcp_server_session_remove(mrcp_server_session_t *session) +{ + if(session->base.id.buf) { + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Remove Session "APT_SID_FMT,session->base.id.buf); + apr_hash_set(session->server->session_table,session->base.id.buf,session->base.id.length,NULL); + } +} + +static APR_INLINE mrcp_server_session_t* mrcp_server_session_find(mrcp_server_t *server, const apt_str_t *session_id) +{ + return apr_hash_get(server->session_table,session_id->buf,session_id->length); +} + +static void mrcp_server_on_start_complete(apt_task_t *task) +{ + apt_consumer_task_t *consumer_task = apt_task_object_get(task); + mrcp_server_t *server = apt_consumer_task_object_get(consumer_task); + mrcp_resource_engine_t *resource_engine; + apr_hash_index_t *it; + void *val; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Open Resource Engines"); + it = apr_hash_first(server->pool,server->resource_engine_table); + for(; it; it = apr_hash_next(it)) { + apr_hash_this(it,NULL,NULL,&val); + resource_engine = val; + if(resource_engine) { + mrcp_resource_engine_open(resource_engine); + } + } + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,SERVER_TASK_NAME" Started"); +} + +static void mrcp_server_on_terminate_complete(apt_task_t *task) +{ + apt_consumer_task_t *consumer_task = apt_task_object_get(task); + mrcp_server_t *server = apt_consumer_task_object_get(consumer_task); + mrcp_resource_engine_t *resource_engine; + apr_dso_handle_t *plugin; + apr_hash_index_t *it; + void *val; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Close Resource Engines"); + it=apr_hash_first(server->pool,server->resource_engine_table); + for(; it; it = apr_hash_next(it)) { + apr_hash_this(it,NULL,NULL,&val); + resource_engine = val; + if(resource_engine) { + mrcp_resource_engine_close(resource_engine); + } + } + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Unload Plugins"); + it=apr_hash_first(server->pool,server->plugin_table); + for(; it; it = apr_hash_next(it)) { + apr_hash_this(it,NULL,NULL,&val); + plugin = val; + if(plugin) { + apr_dso_unload(plugin); + } + } + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,SERVER_TASK_NAME" Terminated"); +} + +static apt_bool_t mrcp_server_msg_process(apt_task_t *task, apt_task_msg_t *msg) +{ + switch(msg->type) { + case MRCP_SERVER_SIGNALING_TASK_MSG: + { + mrcp_signaling_message_t **signaling_message = (mrcp_signaling_message_t**) msg->data; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Receive Signaling Task Message [%d]", (*signaling_message)->type); + mrcp_server_signaling_message_process(*signaling_message); + break; + } + case MRCP_SERVER_CONNECTION_TASK_MSG: + { + const connection_agent_task_msg_data_t *connection_message = (const connection_agent_task_msg_data_t*)msg->data; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Receive Connection Task Message [%d]", msg->sub_type); + switch(msg->sub_type) { + case CONNECTION_AGENT_TASK_MSG_ADD_CHANNEL: + { + mrcp_server_on_channel_modify(connection_message->channel,connection_message->descriptor,connection_message->status); + break; + } + case CONNECTION_AGENT_TASK_MSG_MODIFY_CHANNEL: + { + mrcp_server_on_channel_modify(connection_message->channel,connection_message->descriptor,connection_message->status); + break; + } + case CONNECTION_AGENT_TASK_MSG_REMOVE_CHANNEL: + { + mrcp_server_on_channel_remove(connection_message->channel,connection_message->status); + break; + } + case CONNECTION_AGENT_TASK_MSG_RECEIVE_MESSAGE: + { + mrcp_server_on_channel_message(connection_message->channel, connection_message->message); + break; + } + default: + break; + } + break; + } + case MRCP_SERVER_RESOURCE_ENGINE_TASK_MSG: + { + resource_engine_task_msg_data_t *data = (resource_engine_task_msg_data_t*)msg->data; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Receive Resource Engine Task Message [%d]", msg->sub_type); + switch(msg->sub_type) { + case RESOURCE_ENGINE_TASK_MSG_OPEN_CHANNEL: + mrcp_server_on_engine_channel_open(data->channel,data->status); + break; + case RESOURCE_ENGINE_TASK_MSG_CLOSE_CHANNEL: + mrcp_server_on_engine_channel_close(data->channel); + break; + case RESOURCE_ENGINE_TASK_MSG_MESSAGE: + mrcp_server_on_engine_channel_message(data->channel,data->mrcp_message); + break; + default: + break; + } + break; + } + case MRCP_SERVER_MEDIA_TASK_MSG: + { + mpf_message_t *mpf_message = (mpf_message_t*) msg->data; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Receive Media Task Message [%d]", mpf_message->command_id); + mrcp_server_mpf_message_process(mpf_message); + break; + } + default: + { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Receive Unknown Task Message [%d]", msg->type); + break; + } + } + return TRUE; +} + +static apt_bool_t mrcp_server_signaling_task_msg_signal(mrcp_signaling_message_type_e type, mrcp_session_t *session, mrcp_session_descriptor_t *descriptor, mrcp_message_t *message) +{ + mrcp_signaling_message_t *signaling_message; + apt_task_msg_t *task_msg = apt_task_msg_acquire(session->signaling_agent->msg_pool); + mrcp_signaling_message_t **slot = ((mrcp_signaling_message_t**)task_msg->data); + task_msg->type = MRCP_SERVER_SIGNALING_TASK_MSG; + task_msg->sub_type = type; + + signaling_message = apr_palloc(session->pool,sizeof(mrcp_signaling_message_t)); + signaling_message->type = type; + signaling_message->session = (mrcp_server_session_t*)session; + signaling_message->descriptor = descriptor; + signaling_message->channel = NULL; + signaling_message->message = message; + *slot = signaling_message; + + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Signal Signaling Task Message"); + return apt_task_msg_parent_signal(session->signaling_agent->task,task_msg); +} + +static apt_bool_t mrcp_server_connection_task_msg_signal( + connection_agent_task_msg_type_e type, + mrcp_connection_agent_t *agent, + mrcp_control_channel_t *channel, + mrcp_control_descriptor_t *descriptor, + mrcp_message_t *message, + apt_bool_t status) +{ + mrcp_server_t *server = mrcp_server_connection_agent_object_get(agent); + apt_task_t *task = apt_consumer_task_base_get(server->task); + connection_agent_task_msg_data_t *data; + apt_task_msg_t *task_msg = apt_task_msg_acquire(server->connection_msg_pool); + task_msg->type = MRCP_SERVER_CONNECTION_TASK_MSG; + task_msg->sub_type = type; + data = (connection_agent_task_msg_data_t*) task_msg->data; + data->channel = channel ? channel->obj : NULL; + data->descriptor = descriptor; + data->message = message; + data->status = status; + + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Signal Connection Task Message"); + return apt_task_msg_signal(task,task_msg); +} + +static apt_bool_t mrcp_server_engine_task_msg_signal( + resource_engine_task_msg_type_e type, + mrcp_engine_channel_t *engine_channel, + apt_bool_t status, + mrcp_message_t *message) +{ + mrcp_channel_t *channel = engine_channel->event_obj; + mrcp_session_t *session = mrcp_server_channel_session_get(channel); + mrcp_server_t *server = session->signaling_agent->parent; + apt_task_t *task = apt_consumer_task_base_get(server->task); + resource_engine_task_msg_data_t *data; + apt_task_msg_t *task_msg = apt_task_msg_acquire(server->resource_engine_msg_pool); + task_msg->type = MRCP_SERVER_RESOURCE_ENGINE_TASK_MSG; + task_msg->sub_type = type; + data = (resource_engine_task_msg_data_t*) task_msg->data; + data->channel = channel; + data->status = status; + data->mrcp_message = message; + + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Signal Resource Engine Task Message"); + return apt_task_msg_signal(task,task_msg); +} + +static mrcp_profile_t* mrcp_server_profile_get_by_agent(mrcp_server_t *server, mrcp_server_session_t *session, mrcp_sig_agent_t *signaling_agent) +{ + mrcp_profile_t *profile; + apr_hash_index_t *it; + void *val; + const void *key; + const char *name; + it = apr_hash_first(session->base.pool,server->profile_table); + for(; it; it = apr_hash_next(it)) { + apr_hash_this(it,&key,NULL,&val); + profile = val; + name = key; + if(profile && name && profile->signaling_agent == signaling_agent) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Found Profile [%s]",name); + return profile; + } + } + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot Find Profile by Agent "APT_SID_FMT,session->base.id.buf); + return NULL; +} + +static mrcp_session_t* mrcp_server_sig_agent_session_create(mrcp_sig_agent_t *signaling_agent) +{ + mrcp_server_t *server = signaling_agent->parent; + mrcp_server_session_t *session = mrcp_server_session_create(); + session->server = server; + session->profile = mrcp_server_profile_get_by_agent(server,session,signaling_agent); + if(!session->profile) { + mrcp_session_destroy(&session->base); + return NULL; + } + session->base.signaling_agent = signaling_agent; + session->base.request_vtable = &session_request_vtable; + return &session->base; +} + +static apt_bool_t mrcp_server_offer_signal(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor) +{ + return mrcp_server_signaling_task_msg_signal(SIGNALING_MESSAGE_OFFER,session,descriptor,NULL); +} + +static apt_bool_t mrcp_server_terminate_signal(mrcp_session_t *session) +{ + return mrcp_server_signaling_task_msg_signal(SIGNALING_MESSAGE_TERMINATE,session,NULL,NULL); +} + +static apt_bool_t mrcp_server_control_signal(mrcp_session_t *session, mrcp_message_t *message) +{ + return mrcp_server_signaling_task_msg_signal(SIGNALING_MESSAGE_CONTROL,session,NULL,message); +} + +static apt_bool_t mrcp_server_channel_add_signal(mrcp_control_channel_t *channel, mrcp_control_descriptor_t *descriptor, apt_bool_t status) +{ + mrcp_connection_agent_t *agent = channel->agent; + return mrcp_server_connection_task_msg_signal( + CONNECTION_AGENT_TASK_MSG_ADD_CHANNEL, + agent, + channel, + descriptor, + NULL, + status); +} + +static apt_bool_t mrcp_server_channel_modify_signal(mrcp_control_channel_t *channel, mrcp_control_descriptor_t *descriptor, apt_bool_t status) +{ + mrcp_connection_agent_t *agent = channel->agent; + return mrcp_server_connection_task_msg_signal( + CONNECTION_AGENT_TASK_MSG_MODIFY_CHANNEL, + agent, + channel, + descriptor, + NULL, + status); +} + +static apt_bool_t mrcp_server_channel_remove_signal(mrcp_control_channel_t *channel, apt_bool_t status) +{ + mrcp_connection_agent_t *agent = channel->agent; + return mrcp_server_connection_task_msg_signal( + CONNECTION_AGENT_TASK_MSG_REMOVE_CHANNEL, + agent, + channel, + NULL, + NULL, + status); +} + +static apt_bool_t mrcp_server_message_signal(mrcp_control_channel_t *channel, mrcp_message_t *message) +{ + mrcp_connection_agent_t *agent = channel->agent; + return mrcp_server_connection_task_msg_signal( + CONNECTION_AGENT_TASK_MSG_RECEIVE_MESSAGE, + agent, + channel, + NULL, + message, + TRUE); +} + +static apt_bool_t mrcp_server_channel_open_signal(mrcp_engine_channel_t *channel, apt_bool_t status) +{ + return mrcp_server_engine_task_msg_signal( + RESOURCE_ENGINE_TASK_MSG_OPEN_CHANNEL, + channel, + status, + NULL); +} + +static apt_bool_t mrcp_server_channel_close_signal(mrcp_engine_channel_t *channel) +{ + return mrcp_server_engine_task_msg_signal( + RESOURCE_ENGINE_TASK_MSG_CLOSE_CHANNEL, + channel, + TRUE, + NULL); +} + +static apt_bool_t mrcp_server_channel_message_signal(mrcp_engine_channel_t *channel, mrcp_message_t *message) +{ + return mrcp_server_engine_task_msg_signal( + RESOURCE_ENGINE_TASK_MSG_MESSAGE, + channel, + TRUE, + message); +} diff --git a/libs/unimrcp/libs/mrcp-server/src/mrcp_server_session.c b/libs/unimrcp/libs/mrcp-server/src/mrcp_server_session.c new file mode 100644 index 0000000000..26fbe9573a --- /dev/null +++ b/libs/unimrcp/libs/mrcp-server/src/mrcp_server_session.c @@ -0,0 +1,934 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mrcp_server.h" +#include "mrcp_server_session.h" +#include "mrcp_resource.h" +#include "mrcp_resource_factory.h" +#include "mrcp_resource_engine.h" +#include "mrcp_sig_agent.h" +#include "mrcp_server_connection.h" +#include "mrcp_session_descriptor.h" +#include "mrcp_control_descriptor.h" +#include "mrcp_state_machine.h" +#include "mrcp_message.h" +#include "mpf_user.h" +#include "mpf_termination.h" +#include "mpf_engine.h" +#include "apt_consumer_task.h" +#include "apt_log.h" + +#define MRCP_SESSION_ID_HEX_STRING_LENGTH 16 + +struct mrcp_channel_t { + /** Memory pool */ + apr_pool_t *pool; + /** MRCP resource */ + apt_str_t resource_name; + /** MRCP resource */ + mrcp_resource_t *resource; + /** MRCP session entire channel belongs to */ + mrcp_session_t *session; + /** MRCP control channel */ + mrcp_control_channel_t *control_channel; + /** MRCP resource engine channel */ + mrcp_engine_channel_t *engine_channel; + /** MRCP resource state machine */ + mrcp_state_machine_t *state_machine; + /** media descriptor id */ + apr_size_t id; + /** waiting state of control media */ + apt_bool_t waiting_for_channel; + /** waiting state of media termination */ + apt_bool_t waiting_for_termination; +}; + +typedef struct mrcp_termination_slot_t mrcp_termination_slot_t; + +struct mrcp_termination_slot_t { + /** RTP termination */ + mpf_termination_t *termination; + /** media descriptor id */ + apr_size_t id; + /** waiting state */ + apt_bool_t waiting; +}; + +extern const mrcp_engine_channel_event_vtable_t engine_channel_vtable; + +void mrcp_server_session_add(mrcp_server_session_t *session); +void mrcp_server_session_remove(mrcp_server_session_t *session); + +static apt_bool_t mrcp_server_signaling_message_dispatch(mrcp_server_session_t *session, mrcp_signaling_message_t *signaling_message); + +static apt_bool_t mrcp_server_resource_offer_process(mrcp_server_session_t *session, mrcp_session_descriptor_t *descriptor); +static apt_bool_t mrcp_server_control_media_offer_process(mrcp_server_session_t *session, mrcp_session_descriptor_t *descriptor); +static apt_bool_t mrcp_server_av_media_offer_process(mrcp_server_session_t *session, mrcp_session_descriptor_t *descriptor); + +static apt_bool_t mrcp_server_on_termination_modify(mrcp_server_session_t *session, const mpf_message_t *mpf_message); +static apt_bool_t mrcp_server_on_termination_subtract(mrcp_server_session_t *session, const mpf_message_t *mpf_message); + +static apt_bool_t mrcp_server_session_answer_send(mrcp_server_session_t *session); +static apt_bool_t mrcp_server_session_terminate_send(mrcp_server_session_t *session); + +static mrcp_channel_t* mrcp_server_channel_find(mrcp_server_session_t *session, const apt_str_t *resource_name); + +static apt_bool_t mrcp_server_mpf_request_send( + mrcp_server_session_t *session, + mpf_command_type_e command_id, + mpf_context_t *context, + mpf_termination_t *termination, + void *descriptor); + + +mrcp_server_session_t* mrcp_server_session_create() +{ + mrcp_server_session_t *session = (mrcp_server_session_t*) mrcp_session_create(sizeof(mrcp_server_session_t)-sizeof(mrcp_session_t)); + session->context = NULL; + session->terminations = apr_array_make(session->base.pool,2,sizeof(mrcp_termination_slot_t)); + session->channels = apr_array_make(session->base.pool,2,sizeof(mrcp_channel_t*)); + session->active_request = NULL; + session->request_queue = apt_list_create(session->base.pool); + session->offer = NULL; + session->answer = NULL; + session->answer_flag_count = 0; + session->terminate_flag_count = 0; + return session; +} + +static mrcp_engine_channel_t* mrcp_server_engine_channel_create(mrcp_server_session_t *session, const apt_str_t *resource_name) +{ + mrcp_resource_engine_t *resource_engine = apr_hash_get( + session->profile->engine_table, + resource_name->buf, + resource_name->length); + if(!resource_engine) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Find Resource Engine [%s]",resource_name->buf); + return NULL; + } + + return resource_engine->method_vtable->create_channel(resource_engine,session->base.pool); +} + +static apt_bool_t mrcp_server_message_dispatch(mrcp_state_machine_t *state_machine, mrcp_message_t *message) +{ + mrcp_channel_t *channel = state_machine->obj; + + if(message->start_line.message_type == MRCP_MESSAGE_TYPE_REQUEST) { + /* send request message to resource engine for actual processing */ + if(channel->engine_channel) { + mrcp_engine_channel_request_process(channel->engine_channel,message); + } + } + else if(message->start_line.message_type == MRCP_MESSAGE_TYPE_RESPONSE) { + mrcp_server_session_t *session = (mrcp_server_session_t*)channel->session; + /* send response message to client */ + if(channel->control_channel) { + /* MRCPv2 */ + mrcp_server_control_message_send(channel->control_channel,message); + } + else { + /* MRCPv1 */ + mrcp_session_control_response(channel->session,message); + } + + session->active_request = apt_list_pop_front(session->request_queue); + if(session->active_request) { + mrcp_server_signaling_message_dispatch(session,session->active_request); + } + } + else { + /* send event message to client */ + if(channel->control_channel) { + /* MRCPv2 */ + mrcp_server_control_message_send(channel->control_channel,message); + } + else { + /* MRCPv1 */ + mrcp_session_control_response(channel->session,message); + } + } + return TRUE; +} + +static mrcp_channel_t* mrcp_server_channel_create(mrcp_server_session_t *session, const apt_str_t *resource_name, apr_size_t id) +{ + mrcp_channel_t *channel; + apr_pool_t *pool = session->base.pool; + + channel = apr_palloc(pool,sizeof(mrcp_channel_t)); + channel->pool = pool; + channel->session = &session->base; + channel->resource = NULL; + channel->control_channel = NULL; + channel->state_machine = NULL; + channel->engine_channel = NULL; + channel->id = id; + channel->waiting_for_channel = FALSE; + channel->waiting_for_termination = FALSE; + apt_string_reset(&channel->resource_name); + + if(resource_name && resource_name->buf) { + mrcp_resource_id resource_id; + mrcp_resource_t *resource; + mrcp_engine_channel_t *engine_channel; + channel->resource_name = *resource_name; + resource_id = mrcp_resource_id_find( + session->profile->resource_factory, + resource_name); + resource = mrcp_resource_get(session->profile->resource_factory,resource_id); + if(resource) { + channel->resource = resource; + if(session->base.signaling_agent->mrcp_version == MRCP_VERSION_2) { + channel->control_channel = mrcp_server_control_channel_create( + session->profile->connection_agent, + channel, + pool); + } + channel->state_machine = resource->create_server_state_machine( + channel, + mrcp_server_message_dispatch, + session->base.signaling_agent->mrcp_version, + pool); + + engine_channel = mrcp_server_engine_channel_create(session,resource_name); + if(engine_channel) { + engine_channel->event_obj = channel; + engine_channel->event_vtable = &engine_channel_vtable; + channel->engine_channel = engine_channel; + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Resource Engine Channel [%s]",resource_name->buf); + session->answer->status = MRCP_SESSION_STATUS_UNACCEPTABLE_RESOURCE; + } + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No Such Resource [%s]",resource_name->buf); + session->answer->status = MRCP_SESSION_STATUS_NO_SUCH_RESOURCE; + } + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Invalid Resource Identifier"); + session->answer->status = MRCP_SESSION_STATUS_NO_SUCH_RESOURCE; + } + + return channel; +} + +mrcp_session_t* mrcp_server_channel_session_get(mrcp_channel_t *channel) +{ + return channel->session; +} + +apt_bool_t mrcp_server_signaling_message_process(mrcp_signaling_message_t *signaling_message) +{ + mrcp_server_session_t *session = signaling_message->session; + if(session->active_request) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Push Request to Queue"); + apt_list_push_back(session->request_queue,signaling_message,session->base.pool); + } + else { + session->active_request = signaling_message; + mrcp_server_signaling_message_dispatch(session,signaling_message); + } + return TRUE; +} + +apt_bool_t mrcp_server_on_channel_modify(mrcp_channel_t *channel, mrcp_control_descriptor_t *answer, apt_bool_t status) +{ + mrcp_server_session_t *session = (mrcp_server_session_t*)channel->session; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"On Control Channel Modify"); + if(!answer) { + return FALSE; + } + if(!channel->waiting_for_channel) { + return FALSE; + } + channel->waiting_for_channel = FALSE; + answer->session_id = session->base.id; + mrcp_session_control_media_set(session->answer,channel->id,answer); + if(session->answer_flag_count) { + session->answer_flag_count--; + if(!session->answer_flag_count) { + /* send answer to client */ + mrcp_server_session_answer_send(session); + } + } + return TRUE; +} + +apt_bool_t mrcp_server_on_channel_remove(mrcp_channel_t *channel, apt_bool_t status) +{ + mrcp_server_session_t *session = (mrcp_server_session_t*)channel->session; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"On Control Channel Remove"); + if(!channel->waiting_for_channel) { + return FALSE; + } + channel->waiting_for_channel = FALSE; + if(session->terminate_flag_count) { + session->terminate_flag_count--; + if(!session->terminate_flag_count) { + mrcp_server_session_terminate_send(session); + } + } + return TRUE; +} + +apt_bool_t mrcp_server_on_channel_message(mrcp_channel_t *channel, mrcp_message_t *message) +{ + mrcp_server_session_t *session = (mrcp_server_session_t*)channel->session; + mrcp_signaling_message_t *signaling_message; + signaling_message = apr_palloc(session->base.pool,sizeof(mrcp_signaling_message_t)); + signaling_message->type = SIGNALING_MESSAGE_CONTROL; + signaling_message->session = session; + signaling_message->descriptor = NULL; + signaling_message->channel = channel; + signaling_message->message = message; + return mrcp_server_signaling_message_process(signaling_message); +} + +apt_bool_t mrcp_server_on_engine_channel_open(mrcp_channel_t *channel, apt_bool_t status) +{ + mrcp_server_session_t *session = (mrcp_server_session_t*)channel->session; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"On Engine Channel Open [%s]", status == TRUE ? "OK" : "Failed"); + if(status == FALSE) { + session->answer->status = MRCP_SESSION_STATUS_UNAVAILABLE_RESOURCE; + } + if(session->answer_flag_count) { + session->answer_flag_count--; + if(!session->answer_flag_count) { + /* send answer to client */ + mrcp_server_session_answer_send(session); + } + } + return TRUE; +} + +apt_bool_t mrcp_server_on_engine_channel_close(mrcp_channel_t *channel) +{ + mrcp_server_session_t *session = (mrcp_server_session_t*)channel->session; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"On Engine Channel Close"); + if(session->terminate_flag_count) { + session->terminate_flag_count--; + if(!session->terminate_flag_count) { + mrcp_server_session_terminate_send(session); + } + } + return TRUE; +} + +apt_bool_t mrcp_server_on_engine_channel_message(mrcp_channel_t *channel, mrcp_message_t *message) +{ + if(!channel->state_machine) { + return FALSE; + } + /* update state machine */ + return mrcp_state_machine_update(channel->state_machine,message); +} + + +apt_bool_t mrcp_server_mpf_message_process(mpf_message_t *mpf_message) +{ + mrcp_server_session_t *session = NULL; + if(mpf_message->context) { + session = mpf_context_object_get(mpf_message->context); + } + if(mpf_message->message_type == MPF_MESSAGE_TYPE_RESPONSE) { + switch(mpf_message->command_id) { + case MPF_COMMAND_ADD: + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"On Termination Add"); + mrcp_server_on_termination_modify(session,mpf_message); + break; + case MPF_COMMAND_MODIFY: + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"On Termination Modify"); + mrcp_server_on_termination_modify(session,mpf_message); + break; + case MPF_COMMAND_SUBTRACT: + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"On Termination Subtract"); + mrcp_server_on_termination_subtract(session,mpf_message); + break; + default: + break; + } + } + else if(mpf_message->message_type == MPF_MESSAGE_TYPE_EVENT) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Process MPF Event"); + } + return TRUE; +} + +static mrcp_session_descriptor_t* mrcp_session_answer_create(mrcp_session_descriptor_t *offer, apr_pool_t *pool) +{ + int i; + void **control_slot; + mpf_rtp_media_descriptor_t **av_slot; + mrcp_session_descriptor_t *answer = apr_palloc(pool,sizeof(mrcp_session_descriptor_t)); + apt_string_reset(&answer->origin); + apt_string_reset(&answer->ip); + apt_string_reset(&answer->ext_ip); + answer->resource_name = offer->resource_name; + answer->resource_state = offer->resource_state; + answer->status = offer->status; + answer->control_media_arr = apr_array_make(pool,offer->control_media_arr->nelts,sizeof(void*)); + for(i=0; icontrol_media_arr->nelts; i++) { + control_slot = apr_array_push(answer->control_media_arr); + *control_slot = NULL; + } + answer->audio_media_arr = apr_array_make(pool,offer->audio_media_arr->nelts,sizeof(mpf_rtp_media_descriptor_t*)); + for(i=0; iaudio_media_arr->nelts; i++) { + av_slot = apr_array_push(answer->audio_media_arr); + *av_slot = NULL; + } + answer->video_media_arr = apr_array_make(pool,offer->video_media_arr->nelts,sizeof(mpf_rtp_media_descriptor_t*)); + for(i=0; ivideo_media_arr->nelts; i++) { + av_slot = apr_array_push(answer->video_media_arr); + *av_slot = NULL; + } + return answer; +} + +static apt_bool_t mrcp_server_session_offer_process(mrcp_server_session_t *session, mrcp_session_descriptor_t *descriptor) +{ + if(!session->context) { + /* initial offer received, generate session id and add to session's table */ + if(!session->base.id.length) { + apt_unique_id_generate(&session->base.id,MRCP_SESSION_ID_HEX_STRING_LENGTH,session->base.pool); + } + mrcp_server_session_add(session); + + session->context = mpf_context_create(session,5,session->base.pool); + } + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Receive Offer "APT_SID_FMT" [c:%d a:%d v:%d]", + session->base.id.buf, + descriptor->control_media_arr->nelts, + descriptor->audio_media_arr->nelts, + descriptor->video_media_arr->nelts); + + /* store received offer */ + session->offer = descriptor; + session->answer = mrcp_session_answer_create(descriptor,session->base.pool); + + if(session->base.signaling_agent->mrcp_version == MRCP_VERSION_1) { + if(mrcp_server_resource_offer_process(session,descriptor) == TRUE) { + mrcp_server_av_media_offer_process(session,descriptor); + } + else { + session->answer->resource_state = FALSE; + } + } + else { + mrcp_server_control_media_offer_process(session,descriptor); + mrcp_server_av_media_offer_process(session,descriptor); + } + + if(!session->answer_flag_count) { + /* send answer to client */ + mrcp_server_session_answer_send(session); + } + return TRUE; +} + +static apt_bool_t mrcp_server_session_terminate_process(mrcp_server_session_t *session) +{ + mrcp_channel_t *channel; + mrcp_termination_slot_t *slot; + int i; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Receive Terminate Request "APT_SID_FMT,session->base.id.buf); + for(i=0; ichannels->nelts; i++) { + channel = ((mrcp_channel_t**)session->channels->elts)[i]; + if(!channel) continue; + + /* send remove channel request */ + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Remove Control Channel [%d]",i); + if(channel->control_channel) { + if(mrcp_server_control_channel_remove(channel->control_channel) == TRUE) { + channel->waiting_for_channel = TRUE; + session->terminate_flag_count++; + } + } + + if(channel->engine_channel) { + mpf_termination_t *termination = channel->engine_channel->termination; + /* send subtract termination request */ + if(termination) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Subtract Channel Termination"); + if(mrcp_server_mpf_request_send(session,MPF_COMMAND_SUBTRACT,session->context,termination,NULL) == TRUE) { + channel->waiting_for_termination = TRUE; + session->terminate_flag_count++; + } + } + + /* close resource engine channel */ + if(mrcp_engine_channel_close(channel->engine_channel) == TRUE) { + session->terminate_flag_count++; + } + } + } + for(i=0; iterminations->nelts; i++) { + /* get existing termination */ + slot = &((mrcp_termination_slot_t*)session->terminations->elts)[i]; + if(!slot || !slot->termination) continue; + + /* send subtract termination request */ + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Subtract RTP Termination [%d]",i); + if(mrcp_server_mpf_request_send(session,MPF_COMMAND_SUBTRACT,session->context,slot->termination,NULL) == TRUE) { + slot->waiting = TRUE; + session->terminate_flag_count++; + } + } + mrcp_server_session_remove(session); + + if(!session->terminate_flag_count) { + mrcp_server_session_terminate_send(session); + } + + return TRUE; +} + +static apt_bool_t mrcp_server_on_message_receive(mrcp_server_session_t *session, mrcp_channel_t *channel, mrcp_message_t *message) +{ + if(!channel) { + channel = mrcp_server_channel_find(session,&message->channel_id.resource_name); + if(!channel) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No Such Channel"); + return FALSE; + } + } + if(!channel->resource || !channel->state_machine) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No Resource"); + return FALSE; + } + + /* update state machine */ + return mrcp_state_machine_update(channel->state_machine,message); +} + +static apt_bool_t mrcp_server_signaling_message_dispatch(mrcp_server_session_t *session, mrcp_signaling_message_t *signaling_message) +{ + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Dispatch Signaling Message [%d]",signaling_message->type); + switch(signaling_message->type) { + case SIGNALING_MESSAGE_OFFER: + mrcp_server_session_offer_process(signaling_message->session,signaling_message->descriptor); + break; + case SIGNALING_MESSAGE_CONTROL: + mrcp_server_on_message_receive(signaling_message->session,signaling_message->channel,signaling_message->message); + break; + case SIGNALING_MESSAGE_TERMINATE: + mrcp_server_session_terminate_process(signaling_message->session); + break; + default: + break; + } + return TRUE; +} + +static apt_bool_t mrcp_server_resource_offer_process(mrcp_server_session_t *session, mrcp_session_descriptor_t *descriptor) +{ + if(descriptor->resource_state == TRUE) { + /* setup */ + mrcp_channel_t *channel; + mrcp_channel_t **slot; + int count = session->channels->nelts; + channel = mrcp_server_channel_find(session,&descriptor->resource_name); + if(channel) { + /* channel already exists */ + return TRUE; + } + /* create new MRCP channel instance */ + channel = mrcp_server_channel_create(session,&descriptor->resource_name,count); + if(!channel || !channel->resource) { + return FALSE; + } + /* add to channel array */ + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Add Control Channel [%d]",count); + slot = apr_array_push(session->channels); + *slot = channel; + + if(channel->engine_channel) { + /* open resource engine channel */ + if(mrcp_engine_channel_open(channel->engine_channel) == TRUE) { + mpf_termination_t *termination = channel->engine_channel->termination; + session->answer_flag_count++; + + if(termination) { + /* send add termination request (add to media context) */ + if(mrcp_server_mpf_request_send(session,MPF_COMMAND_ADD,session->context,termination,NULL) == TRUE) { + channel->waiting_for_termination = TRUE; + session->answer_flag_count++; + } + + if(termination->audio_stream) { + mpf_rtp_media_descriptor_t *rtp_media_descriptor = mrcp_session_audio_media_get(descriptor,0); + if(rtp_media_descriptor) { + mpf_stream_mode_e mode = termination->audio_stream->mode; + rtp_media_descriptor->mode |= mode; + } + } + } + } + } + } + else { + /* teardown */ + } + return TRUE; +} + +static apt_bool_t mrcp_server_control_media_offer_process(mrcp_server_session_t *session, mrcp_session_descriptor_t *descriptor) +{ + mrcp_channel_t *channel; + mrcp_control_descriptor_t *control_descriptor; + int i; + int count = session->channels->nelts; + if(count > descriptor->control_media_arr->nelts) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Number of Control Channels [%d] > Number of Control Media in Offer [%d]", + count,descriptor->control_media_arr->nelts); + count = descriptor->control_media_arr->nelts; + } + + /* update existing control channels */ + for(i=0; ichannels->elts + i); + if(!channel) continue; + + channel->waiting_for_channel = FALSE; + /* get control descriptor */ + control_descriptor = mrcp_session_control_media_get(descriptor,i); + if(!control_descriptor) continue; + + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Modify Control Channel [%d]",i); + if(channel->control_channel) { + /* send offer */ + if(mrcp_server_control_channel_modify(channel->control_channel,control_descriptor) == TRUE) { + channel->waiting_for_channel = TRUE; + session->answer_flag_count++; + } + } + + if(channel->waiting_for_channel == FALSE) { + mrcp_control_descriptor_t *answer = mrcp_control_answer_create(control_descriptor,channel->pool); + answer->port = 0; + answer->session_id = session->base.id; + mrcp_session_control_media_set(session->answer,channel->id,answer); + } + } + + /* add new control channels */ + for(; icontrol_media_arr->nelts; i++) { + mrcp_channel_t **slot; + /* get control descriptor */ + control_descriptor = mrcp_session_control_media_get(descriptor,i); + if(!control_descriptor) continue; + + /* create new MRCP channel instance */ + channel = mrcp_server_channel_create(session,&control_descriptor->resource_name,i); + if(!channel) continue; + /* add to channel array */ + + control_descriptor->session_id = session->base.id; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Add Control Channel [%d]",i); + slot = apr_array_push(session->channels); + *slot = channel; + + if(channel->control_channel) { + /* send modify connection request */ + if(mrcp_server_control_channel_add(channel->control_channel,control_descriptor) == TRUE) { + channel->waiting_for_channel = TRUE; + session->answer_flag_count++; + } + } + + if(channel->waiting_for_channel == FALSE) { + mrcp_control_descriptor_t *answer = mrcp_control_answer_create(control_descriptor,channel->pool); + answer->port = 0; + answer->session_id = session->base.id; + mrcp_session_control_media_set(session->answer,channel->id,answer); + } + + if(channel->engine_channel) { + /* open resource engine channel */ + if(mrcp_engine_channel_open(channel->engine_channel) == TRUE) { + mpf_termination_t *termination = channel->engine_channel->termination; + session->answer_flag_count++; + + if(termination) { + /* send add termination request (add to media context) */ + if(mrcp_server_mpf_request_send(session,MPF_COMMAND_ADD,session->context,termination,NULL) == TRUE) { + channel->waiting_for_termination = TRUE; + session->answer_flag_count++; + } + } + } + } + } + + return TRUE; +} + +static apt_bool_t mrcp_server_av_media_offer_process(mrcp_server_session_t *session, mrcp_session_descriptor_t *descriptor) +{ + mrcp_termination_slot_t *slot; + int i; + int count = session->terminations->nelts; + if(!descriptor->audio_media_arr->nelts) { + /* no media to process */ + return TRUE; + } + if(count > descriptor->audio_media_arr->nelts) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Number of Terminations [%d] > Number of Audio Media in Offer [%d]", + count,descriptor->audio_media_arr->nelts); + count = descriptor->audio_media_arr->nelts; + } + + /* update existing terminations */ + for(i=0; iterminations->elts)[i]; + if(!slot || !slot->termination) continue; + + /* construct termination descriptor */ + rtp_descriptor = apr_palloc(session->base.pool,sizeof(mpf_rtp_termination_descriptor_t)); + mpf_rtp_termination_descriptor_init(rtp_descriptor); + rtp_descriptor->audio.local = NULL; + rtp_descriptor->audio.remote = mrcp_session_audio_media_get(descriptor,i); + + /* send modify termination request */ + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Modify RTP Termination [%d]",i); + if(mrcp_server_mpf_request_send(session,MPF_COMMAND_MODIFY,session->context,slot->termination,rtp_descriptor) == TRUE) { + slot->waiting = TRUE; + session->answer_flag_count++; + } + } + + /* add new terminations */ + for(; iaudio_media_arr->nelts; i++) { + mpf_rtp_termination_descriptor_t *rtp_descriptor; + mpf_termination_t *termination; + /* create new RTP termination instance */ + termination = mpf_termination_create(session->profile->rtp_termination_factory,session,session->base.pool); + /* add to termination array */ + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Add RTP Termination [%d]",i); + slot = apr_array_push(session->terminations); + slot->id = i; + slot->waiting = FALSE; + slot->termination = termination; + + /* construct termination descriptor */ + rtp_descriptor = apr_palloc(session->base.pool,sizeof(mpf_rtp_termination_descriptor_t)); + mpf_rtp_termination_descriptor_init(rtp_descriptor); + rtp_descriptor->audio.local = NULL; + rtp_descriptor->audio.remote = mrcp_session_audio_media_get(descriptor,i); + + /* send add termination request (add to media context) */ + if(mrcp_server_mpf_request_send(session,MPF_COMMAND_ADD,session->context,termination,rtp_descriptor) == TRUE) { + slot->waiting = TRUE; + session->answer_flag_count++; + } + } + return TRUE; +} + +static apt_bool_t mrcp_server_session_answer_send(mrcp_server_session_t *session) +{ + apt_bool_t status; + mrcp_session_descriptor_t *descriptor = session->answer; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Send Answer "APT_SID_FMT" [c:%d a:%d v:%d] Status %s", + session->base.id.buf, + descriptor->control_media_arr->nelts, + descriptor->audio_media_arr->nelts, + descriptor->video_media_arr->nelts, + mrcp_session_status_phrase_get(descriptor->status)); + status = mrcp_session_answer(&session->base,descriptor); + session->offer = NULL; + session->answer = NULL; + + session->active_request = apt_list_pop_front(session->request_queue); + if(session->active_request) { + mrcp_server_signaling_message_dispatch(session,session->active_request); + } + return status; +} + +static apt_bool_t mrcp_server_session_terminate_send(mrcp_server_session_t *session) +{ + int i; + mrcp_channel_t *channel; + for(i=0; ichannels->nelts; i++) { + channel = ((mrcp_channel_t**)session->channels->elts)[i]; + if(!channel) continue; + + if(channel->control_channel) { + mrcp_server_control_channel_destroy(channel->control_channel); + channel->control_channel = NULL; + } + if(channel->engine_channel) { + mrcp_engine_channel_destroy(channel->engine_channel); + channel->engine_channel = NULL; + } + } + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Send Terminate Response "APT_SID_FMT,session->base.id.buf); + mrcp_session_terminate_response(&session->base); + return TRUE; +} + + +static mrcp_termination_slot_t* mrcp_server_rtp_termination_find(mrcp_server_session_t *session, mpf_termination_t *termination) +{ + int i; + mrcp_termination_slot_t *slot; + for(i=0; iterminations->nelts; i++) { + slot = &((mrcp_termination_slot_t*)session->terminations->elts)[i]; + if(slot && slot->termination == termination) { + return slot; + } + } + return NULL; +} + +static mrcp_channel_t* mrcp_server_channel_termination_find(mrcp_server_session_t *session, mpf_termination_t *termination) +{ + int i; + mrcp_channel_t *channel; + for(i=0; ichannels->nelts; i++) { + channel = ((mrcp_channel_t**)session->channels->elts)[i]; + if(!channel) continue; + + if(channel->engine_channel && channel->engine_channel->termination == termination) { + return channel; + } + } + return NULL; +} + +static mrcp_channel_t* mrcp_server_channel_find(mrcp_server_session_t *session, const apt_str_t *resource_name) +{ + int i; + mrcp_channel_t *channel; + for(i=0; ichannels->nelts; i++) { + channel = ((mrcp_channel_t**)session->channels->elts)[i]; + if(!channel) continue; + + if(apt_string_compare(&channel->resource_name,resource_name) == TRUE) { + return channel; + } + } + return NULL; +} + +static apt_bool_t mrcp_server_on_termination_modify(mrcp_server_session_t *session, const mpf_message_t *mpf_message) +{ + mrcp_termination_slot_t *termination_slot; + if(!session) { + return FALSE; + } + termination_slot = mrcp_server_rtp_termination_find(session,mpf_message->termination); + if(termination_slot) { + /* rtp termination */ + mpf_rtp_termination_descriptor_t *rtp_descriptor; + if(termination_slot->waiting == FALSE) { + return FALSE; + } + termination_slot->waiting = FALSE; + rtp_descriptor = mpf_message->descriptor; + if(rtp_descriptor->audio.local) { + session->answer->ip = rtp_descriptor->audio.local->base.ip; + session->answer->ext_ip = rtp_descriptor->audio.local->base.ext_ip; + mrcp_session_audio_media_set(session->answer,termination_slot->id,rtp_descriptor->audio.local); + } + if(session->answer_flag_count) { + session->answer_flag_count--; + if(!session->answer_flag_count) { + /* send answer to client */ + mrcp_server_session_answer_send(session); + } + } + } + else { + /* engine channel termination */ + mrcp_channel_t *channel = mrcp_server_channel_termination_find(session,mpf_message->termination); + if(channel && channel->waiting_for_termination == TRUE) { + channel->waiting_for_termination = FALSE; + if(session->answer_flag_count) { + session->answer_flag_count--; + if(!session->answer_flag_count) { + /* send answer to client */ + mrcp_server_session_answer_send(session); + } + } + } + } + return TRUE; +} + +static apt_bool_t mrcp_server_on_termination_subtract(mrcp_server_session_t *session, const mpf_message_t *mpf_message) +{ + mrcp_termination_slot_t *termination_slot; + if(!session) { + return FALSE; + } + termination_slot = mrcp_server_rtp_termination_find(session,mpf_message->termination); + if(termination_slot) { + /* rtp termination */ + if(termination_slot->waiting == FALSE) { + return FALSE; + } + termination_slot->waiting = FALSE; + if(session->terminate_flag_count) { + session->terminate_flag_count--; + if(!session->terminate_flag_count) { + mrcp_server_session_terminate_send(session); + } + } + } + else { + /* engine channel termination */ + mrcp_channel_t *channel = mrcp_server_channel_termination_find(session,mpf_message->termination); + if(channel && channel->waiting_for_termination == TRUE) { + channel->waiting_for_termination = FALSE; + if(session->terminate_flag_count) { + session->terminate_flag_count--; + if(!session->terminate_flag_count) { + mrcp_server_session_terminate_send(session); + } + } + } + } + return TRUE; +} + +static apt_bool_t mrcp_server_mpf_request_send( + mrcp_server_session_t *session, + mpf_command_type_e command_id, + mpf_context_t *context, + mpf_termination_t *termination, + void *descriptor) +{ + apt_task_t *media_task = mpf_task_get(session->profile->media_engine); + apt_task_msg_t *msg; + mpf_message_t *mpf_message; + msg = apt_task_msg_get(media_task); + msg->type = TASK_MSG_USER; + mpf_message = (mpf_message_t*) msg->data; + + mpf_message->message_type = MPF_MESSAGE_TYPE_REQUEST; + mpf_message->command_id = command_id; + mpf_message->context = context; + mpf_message->termination = termination; + mpf_message->descriptor = descriptor; + return apt_task_msg_signal(media_task,msg); +} diff --git a/libs/unimrcp/libs/mrcp-signaling/Makefile.am b/libs/unimrcp/libs/mrcp-signaling/Makefile.am new file mode 100644 index 0000000000..4a7458dbd6 --- /dev/null +++ b/libs/unimrcp/libs/mrcp-signaling/Makefile.am @@ -0,0 +1,17 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_srcdir)/libs/mrcp-signaling/include \ + -I$(top_srcdir)/libs/mrcp/include \ + -I$(top_srcdir)/libs/mpf/include \ + -I$(top_srcdir)/libs/apr-toolkit/include \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) + +noinst_LTLIBRARIES = libmrcpsignaling.la + +include_HEADERS = include/mrcp_sig_types.h \ + include/mrcp_sig_agent.h \ + include/mrcp_session.h \ + include/mrcp_session_descriptor.h + +libmrcpsignaling_la_SOURCES = src/mrcp_sig_agent.c \ + src/mrcp_session_descriptor.c \ No newline at end of file diff --git a/libs/unimrcp/libs/mrcp-signaling/include/mrcp_session.h b/libs/unimrcp/libs/mrcp-signaling/include/mrcp_session.h new file mode 100644 index 0000000000..192da1c812 --- /dev/null +++ b/libs/unimrcp/libs/mrcp-signaling/include/mrcp_session.h @@ -0,0 +1,189 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_SESSION_H__ +#define __MRCP_SESSION_H__ + +/** + * @file mrcp_session.h + * @brief Abstract MRCP Session + */ + +#include "mrcp_sig_types.h" +#include "apt_string.h" + +APT_BEGIN_EXTERN_C + +#define MRCP_SESSION_PTR(session) (session) +#define MRCP_SESSION_SID(session) \ + (session)->id.buf ? (session)->id.buf : "new" + +#define MRCP_SESSION_PTRSID(session) \ + MRCP_SESSION_PTR(session), MRCP_SESSION_SID(session) + +/** MRCP session request vtable declaration */ +typedef struct mrcp_session_request_vtable_t mrcp_session_request_vtable_t; +/** MRCP session response vtable declaration */ +typedef struct mrcp_session_response_vtable_t mrcp_session_response_vtable_t; +/** MRCP session event vtable declaration */ +typedef struct mrcp_session_event_vtable_t mrcp_session_event_vtable_t; + + +/** MRCP session */ +struct mrcp_session_t { + /** Memory pool to allocate memory from */ + apr_pool_t *pool; + /** External object associated with session */ + void *obj; + /** Back pointer to signaling agent */ + mrcp_sig_agent_t *signaling_agent; + + /** Session identifier */ + apt_str_t id; + /** Last request identifier sent for client, received for server */ + mrcp_request_id last_request_id; + + /** Virtual request methods */ + const mrcp_session_request_vtable_t *request_vtable; + /** Virtual response methods */ + const mrcp_session_response_vtable_t *response_vtable; + /** Virtual event methods */ + const mrcp_session_event_vtable_t *event_vtable; +}; + + +/** MRCP session request vtable */ +struct mrcp_session_request_vtable_t { + /** Offer session descriptor */ + apt_bool_t (*offer)(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor); + /** Terminate session */ + apt_bool_t (*terminate)(mrcp_session_t *session); + /** Control session (MRCPv1 only) */ + apt_bool_t (*control)(mrcp_session_t *session, mrcp_message_t *message); + /** Discover resources */ + apt_bool_t (*discover)(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor); +}; + +/** MRCP session response vtable */ +struct mrcp_session_response_vtable_t { + /** Answer with remote session descriptor */ + apt_bool_t (*on_answer)(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor); + /** Session terminated */ + apt_bool_t (*on_terminate)(mrcp_session_t *session); + /** Control session (MRCPv1 only) */ + apt_bool_t (*on_control)(mrcp_session_t *session, mrcp_message_t *message); + /** Response to resource discovery request */ + apt_bool_t (*on_discover)(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor); +}; + +/** MRCP session event vtable */ +struct mrcp_session_event_vtable_t { + /** Received session termination event without appropriate request */ + apt_bool_t (*on_terminate)(mrcp_session_t *session); +}; + + +/** Create new memory pool and allocate session object from the pool. */ +MRCP_DECLARE(mrcp_session_t*) mrcp_session_create(apr_size_t padding); + +/** Destroy session and assosiated memory pool. */ +MRCP_DECLARE(void) mrcp_session_destroy(mrcp_session_t *session); + + +/** Offer */ +static APR_INLINE apt_bool_t mrcp_session_offer(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor) +{ + if(session->request_vtable->offer) { + return session->request_vtable->offer(session,descriptor); + } + return FALSE; +} + +/** Terminate */ +static APR_INLINE apt_bool_t mrcp_session_terminate_request(mrcp_session_t *session) +{ + if(session->request_vtable->terminate) { + return session->request_vtable->terminate(session); + } + return FALSE; +} + +/** Answer */ +static APR_INLINE apt_bool_t mrcp_session_answer(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor) +{ + if(session->response_vtable->on_answer) { + return session->response_vtable->on_answer(session,descriptor); + } + return FALSE; +} + +/** On terminate response */ +static APR_INLINE apt_bool_t mrcp_session_terminate_response(mrcp_session_t *session) +{ + if(session->response_vtable->on_terminate) { + return session->response_vtable->on_terminate(session); + } + return FALSE; +} + +/** On terminate event */ +static APR_INLINE apt_bool_t mrcp_session_terminate_event(mrcp_session_t *session) +{ + if(session->event_vtable->on_terminate) { + return session->event_vtable->on_terminate(session); + } + return FALSE; +} + +/** Control request */ +static APR_INLINE apt_bool_t mrcp_session_control_request(mrcp_session_t *session, mrcp_message_t *message) +{ + if(session->request_vtable->control) { + return session->request_vtable->control(session,message); + } + return FALSE; +} + +/** On control response/event */ +static APR_INLINE apt_bool_t mrcp_session_control_response(mrcp_session_t *session, mrcp_message_t *message) +{ + if(session->response_vtable->on_control) { + return session->response_vtable->on_control(session,message); + } + return FALSE; +} + +/** Resource discovery request */ +static APR_INLINE apt_bool_t mrcp_session_discover_request(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor) +{ + if(session->request_vtable->discover) { + return session->request_vtable->discover(session,descriptor); + } + return FALSE; +} + +/** On resource discovery response */ +static APR_INLINE apt_bool_t mrcp_session_discover_response(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor) +{ + if(session->response_vtable->on_discover) { + return session->response_vtable->on_discover(session,descriptor); + } + return FALSE; +} + +APT_END_EXTERN_C + +#endif /*__MRCP_SESSION_H__*/ diff --git a/libs/unimrcp/libs/mrcp-signaling/include/mrcp_session_descriptor.h b/libs/unimrcp/libs/mrcp-signaling/include/mrcp_session_descriptor.h new file mode 100644 index 0000000000..4a2fa19b53 --- /dev/null +++ b/libs/unimrcp/libs/mrcp-signaling/include/mrcp_session_descriptor.h @@ -0,0 +1,149 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_SESSION_DESCRIPTOR_H__ +#define __MRCP_SESSION_DESCRIPTOR_H__ + +/** + * @file mrcp_session_descriptor.h + * @brief MRCP Session Descriptor + */ + +#include "mpf_rtp_descriptor.h" +#include "mrcp_sig_types.h" + +APT_BEGIN_EXTERN_C + +/** MRCP session status */ +typedef enum { + MRCP_SESSION_STATUS_OK, /**< OK */ + MRCP_SESSION_STATUS_NO_SUCH_RESOURCE, /**< no such resource found */ + MRCP_SESSION_STATUS_UNACCEPTABLE_RESOURCE,/**< resource exists, but no implementation (plugin) found */ + MRCP_SESSION_STATUS_UNAVAILABLE_RESOURCE, /**< resource exists, but is temporary unavailable */ + MRCP_SESSION_STATUS_ERROR /**< internal error occuried */ +} mrcp_session_status_e; + +/** MRCP session descriptor */ +struct mrcp_session_descriptor_t { + /** SDP origin */ + apt_str_t origin; + /** Session level IP address */ + apt_str_t ip; + /** Session level external (NAT) IP address */ + apt_str_t ext_ip; + /** Session level resource name (MRCPv1 only) */ + apt_str_t resource_name; + /** Resource state (MRCPv1 only) */ + apt_bool_t resource_state; + /** Session status */ + mrcp_session_status_e status; + + /** MRCP control media array (mrcp_control_descriptor_t) */ + apr_array_header_t *control_media_arr; + /** Audio media array (mpf_rtp_media_descriptor_t) */ + apr_array_header_t *audio_media_arr; + /** Video media array (mpf_rtp_media_descriptor_t) */ + apr_array_header_t *video_media_arr; +}; + +/** Create session descriptor */ +MRCP_DECLARE(mrcp_session_descriptor_t*) mrcp_session_descriptor_create(apr_pool_t *pool); + +static APR_INLINE apr_size_t mrcp_session_media_count_get(const mrcp_session_descriptor_t *descriptor) +{ + return descriptor->control_media_arr->nelts + descriptor->audio_media_arr->nelts + descriptor->video_media_arr->nelts; +} + +static APR_INLINE apr_size_t mrcp_session_control_media_add(mrcp_session_descriptor_t *descriptor, void *media) +{ + void **slot = apr_array_push(descriptor->control_media_arr); + *slot = media; + return mrcp_session_media_count_get(descriptor) - 1; +} + +static APR_INLINE void* mrcp_session_control_media_get(const mrcp_session_descriptor_t *descriptor, apr_size_t id) +{ + if((int)id >= descriptor->control_media_arr->nelts) { + return NULL; + } + return ((void**)descriptor->control_media_arr->elts)[id]; +} + +static APR_INLINE apt_bool_t mrcp_session_control_media_set(mrcp_session_descriptor_t *descriptor, apr_size_t id, void *media) +{ + if((int)id >= descriptor->control_media_arr->nelts) { + return FALSE; + } + ((void**)descriptor->control_media_arr->elts)[id] = media; + return TRUE; +} + + +static APR_INLINE apr_size_t mrcp_session_audio_media_add(mrcp_session_descriptor_t *descriptor, mpf_rtp_media_descriptor_t *media) +{ + mpf_rtp_media_descriptor_t **slot = apr_array_push(descriptor->audio_media_arr); + *slot = media; + return mrcp_session_media_count_get(descriptor) - 1; +} + +static APR_INLINE mpf_rtp_media_descriptor_t* mrcp_session_audio_media_get(const mrcp_session_descriptor_t *descriptor, apr_size_t id) +{ + if((int)id >= descriptor->audio_media_arr->nelts) { + return NULL; + } + return ((mpf_rtp_media_descriptor_t**)descriptor->audio_media_arr->elts)[id]; +} + +static APR_INLINE apt_bool_t mrcp_session_audio_media_set(const mrcp_session_descriptor_t *descriptor, apr_size_t id, mpf_rtp_media_descriptor_t* media) +{ + if((int)id >= descriptor->audio_media_arr->nelts) { + return FALSE; + } + ((mpf_rtp_media_descriptor_t**)descriptor->audio_media_arr->elts)[id] = media; + return TRUE; +} + + +static APR_INLINE apr_size_t mrcp_session_video_media_add(mrcp_session_descriptor_t *descriptor, mpf_rtp_media_descriptor_t *media) +{ + mpf_rtp_media_descriptor_t **slot = apr_array_push(descriptor->video_media_arr); + *slot = media; + return mrcp_session_media_count_get(descriptor) - 1; +} + +static APR_INLINE mpf_rtp_media_descriptor_t* mrcp_session_video_media_get(const mrcp_session_descriptor_t *descriptor, apr_size_t id) +{ + if((int)id >= descriptor->video_media_arr->nelts) { + return NULL; + } + return ((mpf_rtp_media_descriptor_t**)descriptor->video_media_arr->elts)[id]; +} + +static APR_INLINE apt_bool_t mrcp_session_video_media_set(mrcp_session_descriptor_t *descriptor, apr_size_t id, mpf_rtp_media_descriptor_t* media) +{ + if((int)id >= descriptor->video_media_arr->nelts) { + return FALSE; + } + ((mpf_rtp_media_descriptor_t**)descriptor->video_media_arr->elts)[id] = media; + return TRUE; +} + +/** Get session status phrase */ +MRCP_DECLARE(const char*) mrcp_session_status_phrase_get(mrcp_session_status_e status); + +APT_END_EXTERN_C + +#endif /*__MRCP_SESSION_DESCRIPTOR_H__*/ diff --git a/libs/unimrcp/libs/mrcp-signaling/include/mrcp_sig_agent.h b/libs/unimrcp/libs/mrcp-signaling/include/mrcp_sig_agent.h new file mode 100644 index 0000000000..d17986c445 --- /dev/null +++ b/libs/unimrcp/libs/mrcp-signaling/include/mrcp_sig_agent.h @@ -0,0 +1,59 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_SIG_AGENT_H__ +#define __MRCP_SIG_AGENT_H__ + +/** + * @file mrcp_sig_agent.h + * @brief Abstract MRCP Signaling Agent + */ + +#include "mrcp_sig_types.h" +#include "apt_task.h" + +APT_BEGIN_EXTERN_C + + +/** MRCP signaling agent */ +struct mrcp_sig_agent_t { + /** Memory pool to allocate memory from */ + apr_pool_t *pool; + /** External object associated with agent */ + void *obj; + /** Parent object (client/server) */ + void *parent; + /** MRCP version */ + mrcp_version_e mrcp_version; + /** MRCP resource factory */ + mrcp_resource_factory_t *resource_factory; + /** Task interface */ + apt_task_t *task; + /** Task message pool used to allocate signaling agent messages */ + apt_task_msg_pool_t *msg_pool; + + /** Virtual create_server_session */ + mrcp_session_t* (*create_server_session)(mrcp_sig_agent_t *signaling_agent); + /** Virtual create_client_session */ + apt_bool_t (*create_client_session)(mrcp_session_t *session); +}; + +/** Create signaling agent. */ +MRCP_DECLARE(mrcp_sig_agent_t*) mrcp_signaling_agent_create(void *obj, mrcp_version_e mrcp_version, apr_pool_t *pool); + +APT_END_EXTERN_C + +#endif /*__MRCP_SIG_AGENT_H__*/ diff --git a/libs/unimrcp/libs/mrcp-signaling/include/mrcp_sig_types.h b/libs/unimrcp/libs/mrcp-signaling/include/mrcp_sig_types.h new file mode 100644 index 0000000000..41de32b861 --- /dev/null +++ b/libs/unimrcp/libs/mrcp-signaling/include/mrcp_sig_types.h @@ -0,0 +1,40 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_SIG_TYPES_H__ +#define __MRCP_SIG_TYPES_H__ + +/** + * @file mrcp_sig_types.h + * @brief MRCP Signaling Types Declaration + */ + +#include "mrcp_types.h" + +APT_BEGIN_EXTERN_C + +/** Opaque MRCP signaling agent declaration */ +typedef struct mrcp_sig_agent_t mrcp_sig_agent_t; + +/** Opaque MRCP session declaration */ +typedef struct mrcp_session_t mrcp_session_t; + +/** Opaque MRCP session descriptor declaration */ +typedef struct mrcp_session_descriptor_t mrcp_session_descriptor_t; + +APT_END_EXTERN_C + +#endif /*__MRCP_SIG_TYPES_H__*/ diff --git a/libs/unimrcp/libs/mrcp-signaling/mrcpsignaling.vcproj b/libs/unimrcp/libs/mrcp-signaling/mrcpsignaling.vcproj new file mode 100644 index 0000000000..19353d77bf --- /dev/null +++ b/libs/unimrcp/libs/mrcp-signaling/mrcpsignaling.vcproj @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/libs/mrcp-signaling/src/mrcp_session_descriptor.c b/libs/unimrcp/libs/mrcp-signaling/src/mrcp_session_descriptor.c new file mode 100644 index 0000000000..5b0162725f --- /dev/null +++ b/libs/unimrcp/libs/mrcp-signaling/src/mrcp_session_descriptor.c @@ -0,0 +1,49 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mrcp_session_descriptor.h" + +MRCP_DECLARE(mrcp_session_descriptor_t*) mrcp_session_descriptor_create(apr_pool_t *pool) +{ + mrcp_session_descriptor_t *descriptor = apr_palloc(pool,sizeof(mrcp_session_descriptor_t)); + apt_string_reset(&descriptor->origin); + apt_string_reset(&descriptor->ip); + apt_string_reset(&descriptor->ext_ip); + apt_string_reset(&descriptor->resource_name); + descriptor->resource_state = FALSE; + descriptor->status = MRCP_SESSION_STATUS_OK; + descriptor->control_media_arr = apr_array_make(pool,1,sizeof(void*)); + descriptor->audio_media_arr = apr_array_make(pool,1,sizeof(mpf_rtp_media_descriptor_t*)); + descriptor->video_media_arr = apr_array_make(pool,0,sizeof(mpf_rtp_media_descriptor_t*)); + return descriptor; +} + +MRCP_DECLARE(const char*) mrcp_session_status_phrase_get(mrcp_session_status_e status) +{ + switch(status) { + case MRCP_SESSION_STATUS_OK: + return "OK"; + case MRCP_SESSION_STATUS_NO_SUCH_RESOURCE: + return "Not Found"; + case MRCP_SESSION_STATUS_UNACCEPTABLE_RESOURCE: + return "Not Acceptable"; + case MRCP_SESSION_STATUS_UNAVAILABLE_RESOURCE: + return "Unavailable"; + case MRCP_SESSION_STATUS_ERROR: + return "Error"; + } + return "Unknown"; +} diff --git a/libs/unimrcp/libs/mrcp-signaling/src/mrcp_sig_agent.c b/libs/unimrcp/libs/mrcp-signaling/src/mrcp_sig_agent.c new file mode 100644 index 0000000000..90773c345b --- /dev/null +++ b/libs/unimrcp/libs/mrcp-signaling/src/mrcp_sig_agent.c @@ -0,0 +1,60 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mrcp_sig_agent.h" +#include "mrcp_session.h" +#include "apt_pool.h" + +MRCP_DECLARE(mrcp_sig_agent_t*) mrcp_signaling_agent_create(void *obj, mrcp_version_e mrcp_version, apr_pool_t *pool) +{ + mrcp_sig_agent_t *sig_agent = apr_palloc(pool,sizeof(mrcp_sig_agent_t)); + sig_agent->pool = pool; + sig_agent->obj = obj; + sig_agent->mrcp_version = mrcp_version; + sig_agent->resource_factory = NULL; + sig_agent->parent = NULL; + sig_agent->task = NULL; + sig_agent->msg_pool = NULL; + sig_agent->create_server_session = NULL; + sig_agent->create_client_session = NULL; + return sig_agent; +} + +MRCP_DECLARE(mrcp_session_t*) mrcp_session_create(apr_size_t padding) +{ + mrcp_session_t *session; + apr_pool_t *pool = apt_pool_create(); + if(!pool) { + return NULL; + } + session = apr_palloc(pool,sizeof(mrcp_session_t)+padding); + session->pool = pool; + session->obj = NULL; + session->signaling_agent = NULL; + session->request_vtable = NULL; + session->response_vtable = NULL; + session->event_vtable = NULL; + apt_string_reset(&session->id); + session->last_request_id = 0; + return session; +} + +MRCP_DECLARE(void) mrcp_session_destroy(mrcp_session_t *session) +{ + if(session->pool) { + apr_pool_destroy(session->pool); + } +} diff --git a/libs/unimrcp/libs/mrcp/Makefile.am b/libs/unimrcp/libs/mrcp/Makefile.am new file mode 100644 index 0000000000..2d823331b7 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/Makefile.am @@ -0,0 +1,42 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_srcdir)/libs/mrcp/include \ + -I$(top_srcdir)/libs/mrcp/message/include \ + -I$(top_srcdir)/libs/mrcp/control/include \ + -I$(top_srcdir)/libs/mrcp/resources/include \ + -I$(top_srcdir)/libs/apr-toolkit/include \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) + +noinst_LTLIBRARIES = libmrcp.la + +include_HEADERS = include/mrcp.h \ + include/mrcp_types.h \ + message/include/mrcp_header_accessor.h \ + message/include/mrcp_generic_header.h \ + message/include/mrcp_message.h \ + control/include/mrcp_state_machine.h \ + control/include/mrcp_resource.h \ + control/include/mrcp_resource_factory.h \ + control/include/mrcp_stream.h \ + resources/include/mrcp_synth_header.h \ + resources/include/mrcp_synth_resource.h \ + resources/include/mrcp_synth_state_machine.h \ + resources/include/mrcp_recog_header.h \ + resources/include/mrcp_recog_resource.h \ + resources/include/mrcp_recog_state_machine.h \ + resources/include/mrcp_default_factory.h + +libmrcp_la_SOURCES = message/src/mrcp_header_accessor.c \ + message/src/mrcp_generic_header.c \ + message/src/mrcp_message.c \ + control/src/mrcp_resource_factory.c \ + control/src/mrcp_stream.c \ + resources/src/mrcp_synth_header.c \ + resources/src/mrcp_synth_resource.c \ + resources/src/mrcp_synth_server_state_machine.c \ + resources/src/mrcp_synth_client_state_machine.c \ + resources/src/mrcp_recog_header.c \ + resources/src/mrcp_recog_resource.c \ + resources/src/mrcp_recog_server_state_machine.c \ + resources/src/mrcp_recog_client_state_machine.c \ + resources/src/mrcp_default_factory.c diff --git a/libs/unimrcp/libs/mrcp/control/include/mrcp_resource.h b/libs/unimrcp/libs/mrcp/control/include/mrcp_resource.h new file mode 100644 index 0000000000..c85af89991 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/control/include/mrcp_resource.h @@ -0,0 +1,66 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_RESOURCE_H__ +#define __MRCP_RESOURCE_H__ + +/** + * @file mrcp_resource.h + * @brief Abstract MRCP Resource + */ + +#include "mrcp_types.h" +#include "mrcp_header_accessor.h" +#include "mrcp_state_machine.h" + +APT_BEGIN_EXTERN_C + + +/** MRCP resource definition */ +struct mrcp_resource_t { + /** MRCP resource identifier */ + mrcp_resource_id id; + + /** Set resource specific data in a message by resource id */ + apt_bool_t (*resourcify_message_by_id)(mrcp_resource_t *resource, mrcp_message_t *message); + /** Set resource specific data in a message by resource name */ + apt_bool_t (*resourcify_message_by_name)(mrcp_resource_t *resource, mrcp_message_t *message); + + /** Create client side state machine */ + mrcp_state_machine_t* (*create_client_state_machine)(void *obj, mrcp_message_dispatcher_f dispatcher, mrcp_version_e version, apr_pool_t *pool); + /** Create server side state machine */ + mrcp_state_machine_t* (*create_server_state_machine)(void *obj, mrcp_message_dispatcher_f dispatcher, mrcp_version_e version, apr_pool_t *pool); +}; + +/** Initialize MRCP resource */ +static APR_INLINE void mrcp_resource_init(mrcp_resource_t *resource) +{ + resource->resourcify_message_by_id = NULL; + resource->resourcify_message_by_name = NULL; + resource->create_client_state_machine = NULL; + resource->create_server_state_machine = NULL; +} + +/** Validate MRCP resource */ +static APR_INLINE apt_bool_t mrcp_resource_validate(mrcp_resource_t *resource) +{ + return (resource->resourcify_message_by_id && + resource->resourcify_message_by_name) ? TRUE : FALSE; +} + +APT_END_EXTERN_C + +#endif /*__MRCP_RESOURCE_H__*/ diff --git a/libs/unimrcp/libs/mrcp/control/include/mrcp_resource_factory.h b/libs/unimrcp/libs/mrcp/control/include/mrcp_resource_factory.h new file mode 100644 index 0000000000..59af8e8ae0 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/control/include/mrcp_resource_factory.h @@ -0,0 +1,62 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_RESOURCE_FACTORY_H__ +#define __MRCP_RESOURCE_FACTORY_H__ + +/** + * @file mrcp_resource_factory.h + * @brief Aggregation of MRCP Resources + */ + +#include "apt_string_table.h" +#include "apt_text_stream.h" +#include "mrcp_types.h" + +APT_BEGIN_EXTERN_C + +/** Create MRCP resource factory */ +MRCP_DECLARE(mrcp_resource_factory_t*) mrcp_resource_factory_create(apr_size_t resource_count, apr_pool_t *pool); + +/** Destroy MRCP resource factory */ +MRCP_DECLARE(apt_bool_t) mrcp_resource_factory_destroy(mrcp_resource_factory_t *resource_factory); + +/** Set MRCP resource string table */ +MRCP_DECLARE(apt_bool_t) mrcp_resource_string_table_set(mrcp_resource_factory_t *resource_factory, const apt_str_table_item_t *string_table); + +/** Register MRCP resource */ +MRCP_DECLARE(apt_bool_t) mrcp_resource_register(mrcp_resource_factory_t *resource_factory, mrcp_resource_t *resource, mrcp_resource_id resource_id); + +/** Get MRCP resource by resource id */ +MRCP_DECLARE(mrcp_resource_t*) mrcp_resource_get(mrcp_resource_factory_t *resource_factory, mrcp_resource_id resource_id); + +/** Get resource name associated with specified resource id */ +MRCP_DECLARE(const apt_str_t*) mrcp_resource_name_get(mrcp_resource_factory_t *resource_factory, mrcp_resource_id resource_id); + +/** Find resource id associated with specified resource name */ +MRCP_DECLARE(mrcp_resource_id) mrcp_resource_id_find(mrcp_resource_factory_t *resource_factory, const apt_str_t *resource_name); + + +/** Associate MRCP resource specific data by resource identifier */ +MRCP_DECLARE(apt_bool_t) mrcp_message_resourcify_by_id(mrcp_resource_factory_t *resource_factory, mrcp_message_t *message); + +/** Associate MRCP resource specific data by resource name */ +MRCP_DECLARE(apt_bool_t) mrcp_message_resourcify_by_name(mrcp_resource_factory_t *resource_factory, mrcp_message_t *message); + + +APT_END_EXTERN_C + +#endif /*__MRCP_RESOURCE_FACTORY_H__*/ diff --git a/libs/unimrcp/libs/mrcp/control/include/mrcp_state_machine.h b/libs/unimrcp/libs/mrcp/control/include/mrcp_state_machine.h new file mode 100644 index 0000000000..52ec2ddf32 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/control/include/mrcp_state_machine.h @@ -0,0 +1,62 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_STATE_MACHINE_H__ +#define __MRCP_STATE_MACHINE_H__ + +/** + * @file mrcp_state_machine.h + * @brief MRCP State Machine + */ + +#include "mrcp_types.h" + +APT_BEGIN_EXTERN_C + +/** MRCP state machine declaration */ +typedef struct mrcp_state_machine_t mrcp_state_machine_t; + +/** MRCP message dispatcher */ +typedef apt_bool_t (*mrcp_message_dispatcher_f)(mrcp_state_machine_t *state_machine, mrcp_message_t *message); + + +/** MRCP state machine */ +struct mrcp_state_machine_t { + /** External object associated with state machine */ + void *obj; + /** Message dispatcher */ + mrcp_message_dispatcher_f dispatcher; + + /** Virtual update */ + apt_bool_t (*update)(mrcp_state_machine_t *state_machine, mrcp_message_t *message); +}; + +/** Initialize MRCP state machine */ +static APR_INLINE void mrcp_state_machine_init(mrcp_state_machine_t *state_machine, void *obj, mrcp_message_dispatcher_f dispatcher) +{ + state_machine->obj = obj; + state_machine->dispatcher = dispatcher; +} + +/** Update MRCP state machine */ +static APR_INLINE apt_bool_t mrcp_state_machine_update(mrcp_state_machine_t *state_machine, mrcp_message_t *message) +{ + return state_machine->update(state_machine,message); +} + +APT_END_EXTERN_C + +#endif /*__MRCP_STATE_MACHINE_H__*/ diff --git a/libs/unimrcp/libs/mrcp/control/include/mrcp_stream.h b/libs/unimrcp/libs/mrcp/control/include/mrcp_stream.h new file mode 100644 index 0000000000..69643f4874 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/control/include/mrcp_stream.h @@ -0,0 +1,79 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_STREAM_H__ +#define __MRCP_STREAM_H__ + +/** + * @file mrcp_stream.h + * @brief MRCP Stream Parser and Generator + */ + +#include "apt_text_stream.h" +#include "mrcp_types.h" + +APT_BEGIN_EXTERN_C + +/** Result of MRCP stream processing (parse/generate) */ +typedef enum { + MRCP_STREAM_MESSAGE_COMPLETE, + MRCP_STREAM_MESSAGE_TRUNCATED, + MRCP_STREAM_MESSAGE_INVALID +} mrcp_stream_result_e; + +/** Opaque MRCP parser declaration */ +typedef struct mrcp_parser_t mrcp_parser_t; +/** Opaque MRCP generator declaration */ +typedef struct mrcp_generator_t mrcp_generator_t; + +/** MRCP message handler */ +typedef apt_bool_t (*mrcp_message_handler_f)(void *obj, mrcp_message_t *message, mrcp_stream_result_e result); + +/** Parse MRCP message (excluding message body) */ +MRCP_DECLARE(apt_bool_t) mrcp_message_parse(mrcp_resource_factory_t *resource_factory, mrcp_message_t *message, apt_text_stream_t *stream); + +/** Generate MRCP message (excluding message body) */ +MRCP_DECLARE(apt_bool_t) mrcp_message_generate(mrcp_resource_factory_t *resource_factory, mrcp_message_t *message, apt_text_stream_t *stream); + + +/** Create MRCP stream parser */ +MRCP_DECLARE(mrcp_parser_t*) mrcp_parser_create(mrcp_resource_factory_t *resource_factory, apr_pool_t *pool); + +/** Set resource name to be used while parsing (MRCPv1 only) */ +MRCP_DECLARE(void) mrcp_parser_resource_name_set(mrcp_parser_t *parser, const apt_str_t *resource_name); + +/** Parse MRCP stream */ +MRCP_DECLARE(mrcp_stream_result_e) mrcp_parser_run(mrcp_parser_t *parser, apt_text_stream_t *stream); + +/** Get parsed MRCP message */ +MRCP_DECLARE(mrcp_message_t*) mrcp_parser_message_get(const mrcp_parser_t *parser); + + +/** Create MRCP stream generator */ +MRCP_DECLARE(mrcp_generator_t*) mrcp_generator_create(mrcp_resource_factory_t *resource_factory, apr_pool_t *pool); + +/** Set MRCP message to generate */ +MRCP_DECLARE(apt_bool_t) mrcp_generator_message_set(mrcp_generator_t *generator, mrcp_message_t *message); + +/** Generate MRCP stream */ +MRCP_DECLARE(mrcp_stream_result_e) mrcp_generator_run(mrcp_generator_t *generator, apt_text_stream_t *stream); + +/** Walk through MRCP stream and call message handler for each parsed message */ +MRCP_DECLARE(apt_bool_t) mrcp_stream_walk(mrcp_parser_t *parser, apt_text_stream_t *stream, mrcp_message_handler_f handler, void *obj); + +APT_END_EXTERN_C + +#endif /*__MRCP_STREAM_H__*/ diff --git a/libs/unimrcp/libs/mrcp/control/src/mrcp_resource_factory.c b/libs/unimrcp/libs/mrcp/control/src/mrcp_resource_factory.c new file mode 100644 index 0000000000..7280d3df41 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/control/src/mrcp_resource_factory.c @@ -0,0 +1,151 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mrcp_resource_factory.h" +#include "mrcp_message.h" +#include "mrcp_resource.h" +#include "mrcp_generic_header.h" + +/** Resource factory definition (aggregation of resources) */ +struct mrcp_resource_factory_t { + /** Array of MRCP resources */ + mrcp_resource_t **resource_array; + /** Number of MRCP resources */ + apr_size_t resource_count; + /** String table of MRCP resource names */ + const apt_str_table_item_t *string_table; +}; + +/** Create MRCP resource factory */ +MRCP_DECLARE(mrcp_resource_factory_t*) mrcp_resource_factory_create(apr_size_t resource_count, apr_pool_t *pool) +{ + apr_size_t i; + mrcp_resource_factory_t *resource_factory; + if(resource_count == 0) { + return NULL; + } + + resource_factory = apr_palloc(pool,sizeof(mrcp_resource_factory_t)); + resource_factory->resource_count = resource_count; + resource_factory->resource_array = apr_palloc(pool,sizeof(mrcp_resource_t*)*resource_count); + for(i=0; iresource_array[i] = NULL; + } + resource_factory->string_table = NULL; + + return resource_factory; +} + +/** Destroy MRCP resource container */ +MRCP_DECLARE(apt_bool_t) mrcp_resource_factory_destroy(mrcp_resource_factory_t *resource_factory) +{ + if(resource_factory->resource_array) { + resource_factory->resource_array = NULL; + } + resource_factory->resource_count = 0; + return TRUE; +} + +/** Set MRCP resource string table */ +MRCP_DECLARE(apt_bool_t) mrcp_resource_string_table_set(mrcp_resource_factory_t *resource_factory, const apt_str_table_item_t *string_table) +{ + resource_factory->string_table = string_table; + return TRUE; +} + +/** Register MRCP resource */ +MRCP_DECLARE(apt_bool_t) mrcp_resource_register(mrcp_resource_factory_t *resource_factory, mrcp_resource_t *resource, mrcp_resource_id resource_id) +{ + if(!resource || resource_id >= resource_factory->resource_count) { + /* invalid params */ + return FALSE; + } + if(resource_factory->resource_array[resource_id]) { + /* resource with specified id already exists */ + return FALSE; + } + resource->id = resource_id; + if(mrcp_resource_validate(resource) != TRUE) { + /* invalid resource */ + return FALSE; + } + resource_factory->resource_array[resource->id] = resource; + return TRUE; +} + +/** Get MRCP resource by resource id */ +MRCP_DECLARE(mrcp_resource_t*) mrcp_resource_get(mrcp_resource_factory_t *resource_factory, mrcp_resource_id resource_id) +{ + if(resource_id >= resource_factory->resource_count) { + return NULL; + } + return resource_factory->resource_array[resource_id]; +} + + +/** Set header accessor interface */ +static APR_INLINE void mrcp_generic_header_accessor_set(mrcp_message_t *message) +{ + message->header.generic_header_accessor.vtable = mrcp_generic_header_vtable_get(message->start_line.version); +} + +/** Associate MRCP resource specific data by resource identifier */ +MRCP_DECLARE(apt_bool_t) mrcp_message_resourcify_by_id(mrcp_resource_factory_t *resource_factory, mrcp_message_t *message) +{ + mrcp_resource_t *resource; + const apt_str_t *name; + resource = mrcp_resource_get(resource_factory,message->channel_id.resource_id); + if(!resource) { + return FALSE; + } + name = mrcp_resource_name_get(resource_factory,resource->id); + if(!name) { + return FALSE; + } + /* associate resource_name and resource_id */ + message->channel_id.resource_name = *name; + + mrcp_generic_header_accessor_set(message); + return resource->resourcify_message_by_id(resource,message); +} + +/** Associate MRCP resource specific data by resource name */ +MRCP_DECLARE(apt_bool_t) mrcp_message_resourcify_by_name(mrcp_resource_factory_t *resource_factory, mrcp_message_t *message) +{ + mrcp_resource_t *resource; + /* associate resource_name and resource_id */ + const apt_str_t *name = &message->channel_id.resource_name; + message->channel_id.resource_id = mrcp_resource_id_find(resource_factory,name); + resource = mrcp_resource_get(resource_factory,message->channel_id.resource_id); + if(!resource) { + return FALSE; + } + + mrcp_generic_header_accessor_set(message); + return resource->resourcify_message_by_name(resource,message); +} + +/** Get resource name associated with specified resource id */ +MRCP_DECLARE(const apt_str_t*) mrcp_resource_name_get(mrcp_resource_factory_t *resource_factory, mrcp_resource_id resource_id) +{ + return apt_string_table_str_get(resource_factory->string_table,resource_factory->resource_count,resource_id); +} + +/** Find resource id associated with specified resource name */ +MRCP_DECLARE(mrcp_resource_id) mrcp_resource_id_find(mrcp_resource_factory_t *resource_factory, const apt_str_t *resource_name) +{ + return apt_string_table_id_find(resource_factory->string_table,resource_factory->resource_count,resource_name); +} diff --git a/libs/unimrcp/libs/mrcp/control/src/mrcp_stream.c b/libs/unimrcp/libs/mrcp/control/src/mrcp_stream.c new file mode 100644 index 0000000000..f90d068642 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/control/src/mrcp_stream.c @@ -0,0 +1,360 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mrcp_stream.h" +#include "mrcp_message.h" +#include "mrcp_generic_header.h" +#include "mrcp_resource_factory.h" +#include "apt_log.h" + +/** MRCP parser */ +struct mrcp_parser_t { + mrcp_resource_factory_t *resource_factory; + apt_str_t resource_name; + mrcp_stream_result_e result; + char *pos; + apt_bool_t skip_lf; + mrcp_message_t *message; + apr_pool_t *pool; +}; + +/** MRCP generator */ +struct mrcp_generator_t { + mrcp_resource_factory_t *resource_factory; + mrcp_stream_result_e result; + char *pos; + mrcp_message_t *message; + apr_pool_t *pool; +}; + + +/** Read MRCP message-body */ +static mrcp_stream_result_e mrcp_message_body_read(mrcp_message_t *message, apt_text_stream_t *stream) +{ + mrcp_stream_result_e result = MRCP_STREAM_MESSAGE_COMPLETE; + if(message->body.buf) { + mrcp_generic_header_t *generic_header = mrcp_generic_header_get(message); + /* stream length available to read */ + apr_size_t stream_length = stream->text.length - (stream->pos - stream->text.buf); + /* required/remaining length to read */ + apr_size_t required_length = generic_header->content_length - message->body.length; + if(required_length > stream_length) { + required_length = stream_length; + /* not complete */ + result = MRCP_STREAM_MESSAGE_TRUNCATED; + } + memcpy(message->body.buf+message->body.length,stream->pos,required_length); + message->body.length += required_length; + stream->pos += required_length; + message->body.buf[message->body.length] = '\0'; + } + + return result; +} + +/** Parse MRCP message-body */ +static mrcp_stream_result_e mrcp_message_body_parse(mrcp_message_t *message, apt_text_stream_t *stream, apr_pool_t *pool) +{ + if(mrcp_generic_header_property_check(message,GENERIC_HEADER_CONTENT_LENGTH) == TRUE) { + mrcp_generic_header_t *generic_header = mrcp_generic_header_get(message); + if(generic_header && generic_header->content_length) { + apt_str_t *body = &message->body; + body->buf = apr_palloc(pool,generic_header->content_length+1); + body->length = 0; + return mrcp_message_body_read(message,stream); + } + } + return MRCP_STREAM_MESSAGE_COMPLETE; +} + +/** Write MRCP message-body */ +static mrcp_stream_result_e mrcp_message_body_write(mrcp_message_t *message, apt_text_stream_t *stream) +{ + mrcp_stream_result_e result = MRCP_STREAM_MESSAGE_COMPLETE; + mrcp_generic_header_t *generic_header = mrcp_generic_header_get(message); + if(generic_header && message->body.length < generic_header->content_length) { + /* stream length available to write */ + apr_size_t stream_length = stream->text.length - (stream->pos - stream->text.buf); + /* required/remaining length to write */ + apr_size_t required_length = generic_header->content_length - message->body.length; + if(required_length > stream_length) { + required_length = stream_length; + /* not complete */ + result = MRCP_STREAM_MESSAGE_TRUNCATED; + } + + memcpy(stream->pos,message->body.buf+message->body.length,required_length); + message->body.length += required_length; + stream->pos += required_length; + } + + return result; +} + +/** Generate MRCP message-body */ +static mrcp_stream_result_e mrcp_message_body_generate(mrcp_message_t *message, apt_text_stream_t *stream) +{ + if(mrcp_generic_header_property_check(message,GENERIC_HEADER_CONTENT_LENGTH) == TRUE) { + mrcp_generic_header_t *generic_header = mrcp_generic_header_get(message); + if(generic_header && generic_header->content_length) { + apt_str_t *body = &message->body; + body->length = 0; + return mrcp_message_body_write(message,stream); + } + } + return MRCP_STREAM_MESSAGE_COMPLETE; +} + +/** Parse MRCP message (excluding message body) */ +MRCP_DECLARE(apt_bool_t) mrcp_message_parse(mrcp_resource_factory_t *resource_factory, mrcp_message_t *message, apt_text_stream_t *stream) +{ + /* parse start-line */ + if(mrcp_start_line_parse(&message->start_line,stream,message->pool) == FALSE) { + return FALSE; + } + + if(message->start_line.version == MRCP_VERSION_2) { + mrcp_channel_id_parse(&message->channel_id,stream,message->pool); + } + + if(mrcp_message_resourcify_by_name(resource_factory,message) == FALSE) { + return FALSE; + } + + /* parse header */ + if(mrcp_message_header_parse(&message->header,stream,message->pool) == FALSE) { + return FALSE; + } + + return TRUE; +} + +/** Generate MRCP message (excluding message body) */ +MRCP_DECLARE(apt_bool_t) mrcp_message_generate(mrcp_resource_factory_t *resource_factory, mrcp_message_t *message, apt_text_stream_t *stream) +{ + /* initialize resource specific data */ + if(mrcp_message_resourcify_by_id(resource_factory,message) == FALSE) { + return FALSE; + } + + /* validate message */ + if(mrcp_message_validate(message) == FALSE) { + return FALSE; + } + + /* generate start-line */ + if(mrcp_start_line_generate(&message->start_line,stream) == FALSE) { + return FALSE; + } + + if(message->start_line.version == MRCP_VERSION_2) { + mrcp_channel_id_generate(&message->channel_id,stream); + } + + /* generate header */ + if(mrcp_message_header_generate(&message->header,stream) == FALSE) { + return FALSE; + } + + /* finalize start-line generation */ + mrcp_start_line_finalize(&message->start_line,message->body.length,stream); + return TRUE; +} + + +/** Create MRCP stream parser */ +MRCP_DECLARE(mrcp_parser_t*) mrcp_parser_create(mrcp_resource_factory_t *resource_factory, apr_pool_t *pool) +{ + mrcp_parser_t *parser = apr_palloc(pool,sizeof(mrcp_parser_t)); + parser->resource_factory = resource_factory; + apt_string_reset(&parser->resource_name); + parser->result = MRCP_STREAM_MESSAGE_INVALID; + parser->pos = NULL; + parser->skip_lf = FALSE; + parser->message = NULL; + parser->pool = pool; + return parser; +} + +/** Set resource name to be used while parsing (MRCPv1 only) */ +MRCP_DECLARE(void) mrcp_parser_resource_name_set(mrcp_parser_t *parser, const apt_str_t *resource_name) +{ + if(resource_name) { + apt_string_copy(&parser->resource_name,resource_name,parser->pool); + } +} + +static mrcp_stream_result_e mrcp_parser_break(mrcp_parser_t *parser, apt_text_stream_t *stream) +{ + /* failed to parse either start-line or header */ + if(apt_text_is_eos(stream) == TRUE) { + /* end of stream reached, rewind/restore stream */ + stream->pos = parser->pos; + parser->result = MRCP_STREAM_MESSAGE_TRUNCATED; + parser->message = NULL; + } + else { + /* error case */ + parser->result = MRCP_STREAM_MESSAGE_INVALID; + } + return parser->result; +} + +/** Parse MRCP stream */ +MRCP_DECLARE(mrcp_stream_result_e) mrcp_parser_run(mrcp_parser_t *parser, apt_text_stream_t *stream) +{ + mrcp_message_t *message = parser->message; + if(message && parser->result == MRCP_STREAM_MESSAGE_TRUNCATED) { + /* process continuation data */ + parser->result = mrcp_message_body_read(message,stream); + return parser->result; + } + + /* create new MRCP message */ + message = mrcp_message_create(parser->pool); + message->channel_id.resource_name = parser->resource_name; + parser->message = message; + /* store current position to be able to rewind/restore stream if needed */ + parser->pos = stream->pos; + + /* parse start-line and header */ + if(mrcp_message_parse(parser->resource_factory,message,stream) == FALSE) { + return mrcp_parser_break(parser,stream); + } + + /* parse body */ + parser->result = mrcp_message_body_parse(message,stream,message->pool); + + /* in the worst case message segmentation may occur between and + of the final empty header */ + if(!message->body.length && *(stream->pos-1)== APT_TOKEN_CR) { + /* if this is the case be prepared to skip */ + parser->skip_lf = TRUE; + } + return parser->result; +} + +/** Get parsed MRCP message */ +MRCP_DECLARE(mrcp_message_t*) mrcp_parser_message_get(const mrcp_parser_t *parser) +{ + return parser->message; +} + + +/** Create MRCP stream generator */ +MRCP_DECLARE(mrcp_generator_t*) mrcp_generator_create(mrcp_resource_factory_t *resource_factory, apr_pool_t *pool) +{ + mrcp_generator_t *generator = apr_palloc(pool,sizeof(mrcp_generator_t)); + generator->resource_factory = resource_factory; + generator->result = MRCP_STREAM_MESSAGE_INVALID; + generator->pos = NULL; + generator->message = NULL; + generator->pool = pool; + return generator; +} + +/** Set MRCP message to generate */ +MRCP_DECLARE(apt_bool_t) mrcp_generator_message_set(mrcp_generator_t *generator, mrcp_message_t *message) +{ + if(!message) { + return FALSE; + } + generator->message = message; + return TRUE; +} + +static mrcp_stream_result_e mrcp_generator_break(mrcp_generator_t *generator, apt_text_stream_t *stream) +{ + /* failed to generate either start-line or header */ + if(apt_text_is_eos(stream) == TRUE) { + /* end of stream reached, rewind/restore stream */ + stream->pos = generator->pos; + generator->result = MRCP_STREAM_MESSAGE_TRUNCATED; + } + else { + /* error case */ + generator->result = MRCP_STREAM_MESSAGE_INVALID; + } + return generator->result; +} + +/** Generate MRCP stream */ +MRCP_DECLARE(mrcp_stream_result_e) mrcp_generator_run(mrcp_generator_t *generator, apt_text_stream_t *stream) +{ + mrcp_message_t *message = generator->message; + if(!message) { + return MRCP_STREAM_MESSAGE_INVALID; + } + + if(message && generator->result == MRCP_STREAM_MESSAGE_TRUNCATED) { + /* process continuation data */ + generator->result = mrcp_message_body_write(message,stream); + return generator->result; + } + + /* generate start-line and header */ + if(mrcp_message_generate(generator->resource_factory,message,stream) == FALSE) { + return mrcp_generator_break(generator,stream); + } + + /* generate body */ + generator->result = mrcp_message_body_generate(message,stream); + return generator->result; +} + + +/** Walk through MRCP stream and invoke message handler for each parsed message */ +MRCP_DECLARE(apt_bool_t) mrcp_stream_walk(mrcp_parser_t *parser, apt_text_stream_t *stream, mrcp_message_handler_f handler, void *obj) +{ + mrcp_stream_result_e result; + if(parser->skip_lf == TRUE) { + /* skip occurred as a result of message segmentation between and */ + apt_text_char_skip(stream,APT_TOKEN_LF); + parser->skip_lf = FALSE; + } + do { + result = mrcp_parser_run(parser,stream); + if(result == MRCP_STREAM_MESSAGE_COMPLETE) { + /* message is completely parsed */ + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Parsed MRCP Message [%lu]", stream->pos - stream->text.buf); + /* invoke message handler */ + handler(obj,parser->message,result); + } + else if(result == MRCP_STREAM_MESSAGE_TRUNCATED) { + /* message is partially parsed, to be continued */ + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Truncated MRCP Message [%lu]", stream->pos - stream->text.buf); + /* prepare stream for further processing */ + if(apt_text_stream_scroll(stream) == TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Scroll MRCP Stream", stream->text.buf); + } + return TRUE; + } + else if(result == MRCP_STREAM_MESSAGE_INVALID){ + /* error case */ + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Parse MRCP Message"); + /* invoke message handler */ + handler(obj,parser->message,result); + /* reset stream pos */ + stream->pos = stream->text.buf; + return FALSE; + } + } + while(apt_text_is_eos(stream) == FALSE); + + /* reset stream pos */ + stream->pos = stream->text.buf; + return TRUE; +} diff --git a/libs/unimrcp/libs/mrcp/include/mrcp.h b/libs/unimrcp/libs/mrcp/include/mrcp.h new file mode 100644 index 0000000000..0140a1bfe8 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/include/mrcp.h @@ -0,0 +1,43 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_H__ +#define __MRCP_H__ + +/** + * @file mrcp.h + * @brief MRCP Core Definitions + */ + +#include +#include + +/** Library export/import defines */ +#ifdef WIN32 +#ifdef MRCP_STATIC_LIB +#define MRCP_DECLARE(type) type __stdcall +#else +#ifdef MRCP_LIB_EXPORT +#define MRCP_DECLARE(type) __declspec(dllexport) type __stdcall +#else +#define MRCP_DECLARE(type) __declspec(dllimport) type __stdcall +#endif +#endif +#else +#define MRCP_DECLARE(type) type +#endif + +#endif /*__MRCP_H__*/ diff --git a/libs/unimrcp/libs/mrcp/include/mrcp_types.h b/libs/unimrcp/libs/mrcp/include/mrcp_types.h new file mode 100644 index 0000000000..9b381ab2c3 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/include/mrcp_types.h @@ -0,0 +1,63 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_TYPES_H__ +#define __MRCP_TYPES_H__ + +/** + * @file mrcp_types.h + * @brief Basic MRCP Types + */ + +#include "mrcp.h" + +APT_BEGIN_EXTERN_C + +/** Protocol version */ +typedef enum { + + MRCP_VERSION_UNKNOWN = 0, /**< Unknown version */ + MRCP_VERSION_1 = 1, /**< MRCPv1 (RFC4463) */ + MRCP_VERSION_2 = 2 /**< MRCPv2 (draft-ietf-speechsc-mrcpv2-15) */ +} mrcp_version_e; + +/** Enumeration of MRCP resource types */ +typedef enum { + MRCP_SYNTHESIZER_RESOURCE, /**< Synthesizer resource */ + MRCP_RECOGNIZER_RESOURCE, /**< Recognizer resource */ + + MRCP_RESOURCE_TYPE_COUNT /**< Number of resources */ +} mrcp_resource_type_e; + +/** Message identifier used in request/response/event messages. */ +typedef apr_size_t mrcp_request_id; +/** Method identifier associated with method name. */ +typedef apr_size_t mrcp_method_id; +/** Resource identifier associated with resource name. */ +typedef apr_size_t mrcp_resource_id; + + +/** Opaque MRCP message declaration */ +typedef struct mrcp_message_t mrcp_message_t; +/** Opaque MRCP resource declaration */ +typedef struct mrcp_resource_t mrcp_resource_t; +/** Opaque MRCP resource factory declaration */ +typedef struct mrcp_resource_factory_t mrcp_resource_factory_t; + + +APT_END_EXTERN_C + +#endif /*__MRCP_TYPES_H__*/ diff --git a/libs/unimrcp/libs/mrcp/message/include/mrcp_generic_header.h b/libs/unimrcp/libs/mrcp/message/include/mrcp_generic_header.h new file mode 100644 index 0000000000..fb8ebf852f --- /dev/null +++ b/libs/unimrcp/libs/mrcp/message/include/mrcp_generic_header.h @@ -0,0 +1,123 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_GENERIC_HEADER_H__ +#define __MRCP_GENERIC_HEADER_H__ + +/** + * @file mrcp_generic_header.h + * @brief MRCP Generic Header + */ + +#include "mrcp_types.h" +#include "mrcp_header_accessor.h" + +APT_BEGIN_EXTERN_C + +/** Enumeration of MRCP generic headers */ +typedef enum { + GENERIC_HEADER_ACTIVE_REQUEST_ID_LIST, + GENERIC_HEADER_PROXY_SYNC_ID, + GENERIC_HEADER_ACCEPT_CHARSET, + GENERIC_HEADER_CONTENT_TYPE, + GENERIC_HEADER_CONTENT_ID, + GENERIC_HEADER_CONTENT_BASE, + GENERIC_HEADER_CONTENT_ENCODING, + GENERIC_HEADER_CONTENT_LOCATION, + GENERIC_HEADER_CONTENT_LENGTH, + GENERIC_HEADER_CACHE_CONTROL, + GENERIC_HEADER_LOGGING_TAG, + GENERIC_HEADER_VENDOR_SPECIFIC_PARAMS, + + /** Additional headers for MRCP v2 */ + GENERIC_HEADER_ACCEPT, + GENERIC_HEADER_FETCH_TIMEOUT, + GENERIC_HEADER_SET_COOKIE, + GENERIC_HEADER_SET_COOKIE2, + + GENERIC_HEADER_COUNT +} mrcp_generic_header_id; + +/** MRCP request identifiers list declaration */ +typedef struct mrcp_request_id_list_t mrcp_request_id_list_t; +/** MRCP vendor specific parameter list of pairs */ +typedef struct mrcp_vendor_specific_params_list_t mrcp_vendor_specific_params_list_t; +/** MRCP generic header declaration */ +typedef struct mrcp_generic_header_t mrcp_generic_header_t; + +/** Max number of request ids in active request id list */ +#define MAX_ACTIVE_REQUEST_ID_COUNT 5 +/** List (array) of MRCP request identifiers */ +struct mrcp_request_id_list_t { + /** Array of request identifiers */ + mrcp_request_id ids[MAX_ACTIVE_REQUEST_ID_COUNT]; + /** Number of request identifiers */ + size_t count; +}; + + +/** MRCP generic header */ +struct mrcp_generic_header_t { + /** Indicates the list of request-ids to which it should apply */ + mrcp_request_id_list_t active_request_id_list; + /** Helps the resource receiving the event, proxied by the client, + to decide if this event has been processed through a direct interaction of the resources */ + apt_str_t proxy_sync_id; + /** Specifies the acceptable character set for entities returned in the response or events associated with this request */ + apt_str_t accept_charset; + /** Restricted to speech markup, grammar, recognition results, etc. */ + apt_str_t content_type; + /** Contains an ID or name for the content, by which it can be referred to */ + apt_str_t content_id; + /** May be used to specify the base URI for resolving relative URLs within the entity */ + apt_str_t content_base; + /** Indicates what additional content coding has been applied to the entity-body */ + apt_str_t content_encoding; + /** Statement of the location of the resource corresponding to this particular entity at the time of the request */ + apt_str_t content_location; + /** Contains the length of the content of the message body */ + size_t content_length; + /** Defines the default caching algorithms on the media server for the session or request */ + apt_str_t cache_control; + /** Sets the logging tag for logs generated by the media server */ + apt_str_t logging_tag; + /** Specifies the vendor specific parameters used by the media server */ + apt_pair_arr_t *vendor_specific_params; + + /** Additional headers for MRCP v2 */ + /** Specifies the acceptable media types set for entities returned in the response or events associated with this request */ + apt_str_t accept; + /** Defines the timeout for content that the server may need to fetch over the network */ + apr_size_t fetch_timeout; + /** Enables to synchronize the cookie store of MRCP v2 client and server */ + apt_str_t set_cookie; + /** Enables to synchronize the cookie store of MRCP v2 client and server */ + apt_str_t set_cookie2; +}; + +/** Get generic header vtable */ +MRCP_DECLARE(const mrcp_header_vtable_t*) mrcp_generic_header_vtable_get(mrcp_version_e version); + + +/** Append active request id list */ +MRCP_DECLARE(apt_bool_t) active_request_id_list_append(mrcp_generic_header_t *generic_header, mrcp_request_id request_id); +/** Find request id in active request id list */ +MRCP_DECLARE(apt_bool_t) active_request_id_list_find(mrcp_generic_header_t *generic_header, mrcp_request_id request_id); + + +APT_END_EXTERN_C + +#endif /*__MRCP_GENERIC_HEADER_H__*/ diff --git a/libs/unimrcp/libs/mrcp/message/include/mrcp_header_accessor.h b/libs/unimrcp/libs/mrcp/message/include/mrcp_header_accessor.h new file mode 100644 index 0000000000..209183310f --- /dev/null +++ b/libs/unimrcp/libs/mrcp/message/include/mrcp_header_accessor.h @@ -0,0 +1,154 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_HEADER_ACCESSOR_H__ +#define __MRCP_HEADER_ACCESSOR_H__ + +/** + * @file mrcp_header_accessor.h + * @brief Abstract MRCP Header Accessor + */ + +#include "apt_string_table.h" +#include "apt_text_stream.h" +#include "mrcp.h" + +APT_BEGIN_EXTERN_C + +/** MRCP header accessor declaration */ +typedef struct mrcp_header_accessor_t mrcp_header_accessor_t; +/** MRCP header vtable declaration */ +typedef struct mrcp_header_vtable_t mrcp_header_vtable_t; + +/** MRCP header accessor interface */ +struct mrcp_header_vtable_t { + /** Allocate actual header data */ + void* (*allocate)(mrcp_header_accessor_t *accessor, apr_pool_t *pool); + /** Destroy header data */ + void (*destroy)(mrcp_header_accessor_t *accessor); + + /** Parse header field */ + apt_bool_t (*parse_field)(mrcp_header_accessor_t *accessor, apr_size_t id, const apt_str_t *value, apr_pool_t *pool); + /** Generate header field */ + apt_bool_t (*generate_field)(mrcp_header_accessor_t *accessor, apr_size_t id, apt_text_stream_t *value); + /** Duplicate header field */ + apt_bool_t (*duplicate_field)(mrcp_header_accessor_t *accessor, const mrcp_header_accessor_t *src, apr_size_t id, apr_pool_t *pool); + + /** Table of fields */ + const apt_str_table_item_t *field_table; + /** Number of fields */ + apr_size_t field_count; +}; + + +/** Initialize header vtable */ +static APR_INLINE void mrcp_header_vtable_init(mrcp_header_vtable_t *vtable) +{ + vtable->allocate = NULL; + vtable->destroy = NULL; + vtable->parse_field = NULL; + vtable->generate_field = NULL; + vtable->duplicate_field = NULL; + vtable->field_table = NULL; + vtable->field_count = 0; +} + +/** Validate header vtable */ +static APR_INLINE apt_bool_t mrcp_header_vtable_validate(const mrcp_header_vtable_t *vtable) +{ + return (vtable->allocate && vtable->destroy && + vtable->parse_field && vtable->generate_field && + vtable->duplicate_field && vtable->field_table && + vtable->field_count) ? TRUE : FALSE; +} + + +/** MRCP header accessor */ +struct mrcp_header_accessor_t { + /** Actual header data allocated by accessor */ + void *data; + + /** Array properties (mrcp_header_property_e) */ + char *properties; + /** Number of filled properties (header fields) */ + apr_size_t counter; + + /** Header accessor interface */ + const mrcp_header_vtable_t *vtable; +}; + +/** Initialize header accessor */ +static APR_INLINE void mrcp_header_accessor_init(mrcp_header_accessor_t *accessor) +{ + accessor->data = NULL; + accessor->properties = NULL; + accessor->counter = 0; + accessor->vtable = NULL; +} + + +/** Allocate header data */ +static APR_INLINE void* mrcp_header_allocate(mrcp_header_accessor_t *accessor, apr_pool_t *pool) +{ + if(accessor->data) { + return accessor->data; + } + if(!accessor->vtable || !accessor->vtable->allocate) { + return NULL; + } + accessor->properties = (char*)apr_pcalloc(pool,sizeof(char)*accessor->vtable->field_count); + accessor->counter = 0; + return accessor->vtable->allocate(accessor,pool); +} + +/** Destroy header data */ +static APR_INLINE void mrcp_header_destroy(mrcp_header_accessor_t *accessor) +{ + if(!accessor->vtable || !accessor->vtable->destroy) { + return; + } + accessor->vtable->destroy(accessor); +} + + +/** Parse header */ +MRCP_DECLARE(apt_bool_t) mrcp_header_parse(mrcp_header_accessor_t *accessor, const apt_pair_t *pair, apr_pool_t *pool); + +/** Generate header */ +MRCP_DECLARE(apt_bool_t) mrcp_header_generate(mrcp_header_accessor_t *accessor, apt_text_stream_t *text_stream); + +/** Set header */ +MRCP_DECLARE(apt_bool_t) mrcp_header_set(mrcp_header_accessor_t *accessor, const mrcp_header_accessor_t *src, const mrcp_header_accessor_t *mask, apr_pool_t *pool); + +/** Inherit header */ +MRCP_DECLARE(apt_bool_t) mrcp_header_inherit(mrcp_header_accessor_t *accessor, const mrcp_header_accessor_t *parent, apr_pool_t *pool); + + +/** Add name:value property */ +MRCP_DECLARE(void) mrcp_header_property_add(mrcp_header_accessor_t *accessor, apr_size_t id); + +/** Remove property */ +MRCP_DECLARE(void) mrcp_header_property_remove(mrcp_header_accessor_t *accessor, apr_size_t id); + +/** Check the property */ +MRCP_DECLARE(apt_bool_t) mrcp_header_property_check(mrcp_header_accessor_t *accessor, apr_size_t id); + +/** Add name only property */ +MRCP_DECLARE(void) mrcp_header_name_property_add(mrcp_header_accessor_t *accessor, apr_size_t id); + +APT_END_EXTERN_C + +#endif /*__MRCP_HEADER_ACCESSOR_H__*/ diff --git a/libs/unimrcp/libs/mrcp/message/include/mrcp_message.h b/libs/unimrcp/libs/mrcp/message/include/mrcp_message.h new file mode 100644 index 0000000000..769ab08989 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/message/include/mrcp_message.h @@ -0,0 +1,278 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_MESSAGE_H__ +#define __MRCP_MESSAGE_H__ + +/** + * @file mrcp_message.h + * @brief MRCP Message Definition + */ + +#include "mrcp_types.h" +#include "mrcp_header_accessor.h" + +APT_BEGIN_EXTERN_C + +/** Request-states used in MRCP response message */ +typedef enum { + /** The request was processed to completion and there will be no + more events from that resource to the client with that request-id */ + MRCP_REQUEST_STATE_COMPLETE, + /** The job has been placed on a queue and will be processed in first-in-first-out order */ + MRCP_REQUEST_STATE_INPROGRESS, + /** Indicate that further event messages will be delivered with that request-id */ + MRCP_REQUEST_STATE_PENDING, + + /** Number of request states */ + MRCP_REQUEST_STATE_COUNT, + /** Unknown request state */ + MRCP_REQUEST_STATE_UNKNOWN = MRCP_REQUEST_STATE_COUNT +} mrcp_request_state_e; + +/** Status codes */ +typedef enum { + MRCP_STATUS_CODE_UNKNOWN = 0, + /* success codes (2xx) */ + MRCP_STATUS_CODE_SUCCESS = 200, + MRCP_STATUS_CODE_SUCCESS_WITH_IGNORE = 201, + /* failure codes (4xx) */ + MRCP_STATUS_CODE_METHOD_NOT_ALLOWED = 401, + MRCP_STATUS_CODE_METHOD_NOT_VALID = 402, + MRCP_STATUS_CODE_UNSUPPORTED_PARAM = 403, + MRCP_STATUS_CODE_ILLEGAL_PARAM_VALUE = 404, + MRCP_STATUS_CODE_NOT_FOUND = 405, + MRCP_STATUS_CODE_MISSING_PARAM = 406, + MRCP_STATUS_CODE_METHOD_FAILED = 407, + MRCP_STATUS_CODE_UNRECOGNIZED_MESSAGE = 408, + MRCP_STATUS_CODE_UNSUPPORTED_PARAM_VALUE = 409, + MRCP_STATUS_CODE_RESOURCE_SPECIFIC_FAILURE = 421 +} mrcp_status_code_e; + +/** MRCP message types */ +typedef enum { + MRCP_MESSAGE_TYPE_UNKNOWN, + MRCP_MESSAGE_TYPE_REQUEST, + MRCP_MESSAGE_TYPE_RESPONSE, + MRCP_MESSAGE_TYPE_EVENT +} mrcp_message_type_e; + + +/** MRCP start-line declaration */ +typedef struct mrcp_start_line_t mrcp_start_line_t; +/** MRCP channel-id declaration */ +typedef struct mrcp_channel_id mrcp_channel_id; +/** MRCP message header declaration */ +typedef struct mrcp_message_header_t mrcp_message_header_t; + + +/** Start-line of MRCP message */ +struct mrcp_start_line_t { + /** MRCP message type */ + mrcp_message_type_e message_type; + /** Version of protocol in use */ + mrcp_version_e version; + /** Specify the length of the message, including the start-line (v2) */ + size_t length; + /** Unique identifier among client and server */ + mrcp_request_id request_id; + /** MRCP method name */ + apt_str_t method_name; + /** MRCP method id (associated with method name) */ + mrcp_method_id method_id; + /** Success or failure or other status of the request */ + mrcp_status_code_e status_code; + /** The state of the job initiated by the request */ + mrcp_request_state_e request_state; +}; + +/** Initialize MRCP start-line */ +MRCP_DECLARE(void) mrcp_start_line_init(mrcp_start_line_t *start_line); +/** Parse MRCP start-line */ +MRCP_DECLARE(apt_bool_t) mrcp_start_line_parse(mrcp_start_line_t *start_line, apt_text_stream_t *text_stream, apr_pool_t *pool); +/** Generate MRCP start-line */ +MRCP_DECLARE(apt_bool_t) mrcp_start_line_generate(mrcp_start_line_t *start_line, apt_text_stream_t *text_stream); +/** Finalize MRCP start-line generation */ +MRCP_DECLARE(apt_bool_t) mrcp_start_line_finalize(mrcp_start_line_t *start_line, apr_size_t content_length, apt_text_stream_t *text_stream); + + +/** MRCP channel-identifier */ +struct mrcp_channel_id { + /** Unambiguous string identifying the MRCP session */ + apt_str_t session_id; + /** MRCP resource name */ + apt_str_t resource_name; + /** MRCP resource id (associated with resource name) */ + mrcp_resource_id resource_id; +}; + +/** Initialize MRCP channel-identifier */ +MRCP_DECLARE(void) mrcp_channel_id_init(mrcp_channel_id *channel_id); + +/** Parse MRCP channel-identifier */ +MRCP_DECLARE(apt_bool_t) mrcp_channel_id_parse(mrcp_channel_id *channel_id, apt_text_stream_t *text_stream, apr_pool_t *pool); + +/** Generate MRCP channel-identifier */ +MRCP_DECLARE(apt_bool_t) mrcp_channel_id_generate(mrcp_channel_id *channel_id, apt_text_stream_t *text_stream); + + +/** MRCP message-header */ +struct mrcp_message_header_t { + /** MRCP generic-header */ + mrcp_header_accessor_t generic_header_accessor; + /** MRCP resource specific header */ + mrcp_header_accessor_t resource_header_accessor; +}; + +/** Initialize MRCP message-header */ +static APR_INLINE void mrcp_message_header_init(mrcp_message_header_t *message_header) +{ + mrcp_header_accessor_init(&message_header->generic_header_accessor); + mrcp_header_accessor_init(&message_header->resource_header_accessor); +} + +/** Destroy MRCP message-header */ +static APR_INLINE void mrcp_message_header_destroy(mrcp_message_header_t *message_header) +{ + mrcp_header_destroy(&message_header->generic_header_accessor); + mrcp_header_destroy(&message_header->resource_header_accessor); +} + + +/** Parse MRCP message-header */ +MRCP_DECLARE(apt_bool_t) mrcp_message_header_parse(mrcp_message_header_t *message_header, apt_text_stream_t *text_stream, apr_pool_t *pool); + +/** Generate MRCP message-header */ +MRCP_DECLARE(apt_bool_t) mrcp_message_header_generate(mrcp_message_header_t *message_header, apt_text_stream_t *text_stream); + +/** Set MRCP message-header */ +MRCP_DECLARE(apt_bool_t) mrcp_message_header_set(mrcp_message_header_t *message_header, const mrcp_message_header_t *src, apr_pool_t *pool); + +/** Get MRCP message-header */ +MRCP_DECLARE(apt_bool_t) mrcp_message_header_get(mrcp_message_header_t *message_header, const mrcp_message_header_t *src, apr_pool_t *pool); + +/** Inherit MRCP message-header */ +MRCP_DECLARE(apt_bool_t) mrcp_message_header_inherit(mrcp_message_header_t *message_header, const mrcp_message_header_t *parent, apr_pool_t *pool); + + + +/** MRCP message */ +struct mrcp_message_t { + /** Start-line of MRCP message */ + mrcp_start_line_t start_line; + /** Channel-identifier of MRCP message */ + mrcp_channel_id channel_id; + /** Header of MRCP message */ + mrcp_message_header_t header; + /** Body of MRCP message */ + apt_str_t body; + + /** Memory pool MRCP message is allocated from */ + apr_pool_t *pool; +}; + +/** Create MRCP message */ +MRCP_DECLARE(mrcp_message_t*) mrcp_message_create(apr_pool_t *pool); + +/** Initialize MRCP message */ +MRCP_DECLARE(void) mrcp_message_init(mrcp_message_t *message, apr_pool_t *pool); + +/** Initialize MRCP response/event message by request message */ +MRCP_DECLARE(void) mrcp_message_init_by_request(mrcp_message_t *message, const mrcp_message_t *request_message); + +/** Create MRCP request message */ +MRCP_DECLARE(mrcp_message_t*) mrcp_request_create(mrcp_resource_id resource_id, mrcp_method_id method_id, apr_pool_t *pool); +/** Create MRCP response message */ +MRCP_DECLARE(mrcp_message_t*) mrcp_response_create(const mrcp_message_t *request_message, apr_pool_t *pool); +/** Create MRCP event message */ +MRCP_DECLARE(mrcp_message_t*) mrcp_event_create(const mrcp_message_t *request_message, mrcp_method_id event_id, apr_pool_t *pool); + +/** Validate MRCP message */ +MRCP_DECLARE(apt_bool_t) mrcp_message_validate(mrcp_message_t *message); + +/** Destroy MRCP message */ +MRCP_DECLARE(void) mrcp_message_destroy(mrcp_message_t *message); + + +/** Parse MRCP message-body */ +MRCP_DECLARE(apt_bool_t) mrcp_body_parse(mrcp_message_t *message, apt_text_stream_t *text_stream, apr_pool_t *pool); +/** Generate MRCP message-body */ +MRCP_DECLARE(apt_bool_t) mrcp_body_generate(mrcp_message_t *message, apt_text_stream_t *text_stream); + +/** Get MRCP generic-header */ +static APR_INLINE void* mrcp_generic_header_get(mrcp_message_t *mrcp_message) +{ + return mrcp_message->header.generic_header_accessor.data; +} + +/** Prepare MRCP generic-header */ +static APR_INLINE void* mrcp_generic_header_prepare(mrcp_message_t *mrcp_message) +{ + return mrcp_header_allocate(&mrcp_message->header.generic_header_accessor,mrcp_message->pool); +} + +/** Add MRCP generic-header proprerty */ +static APR_INLINE void mrcp_generic_header_property_add(mrcp_message_t *mrcp_message, size_t id) +{ + mrcp_header_property_add(&mrcp_message->header.generic_header_accessor,id); +} + +/** Add MRCP generic-header name only proprerty (should be used to construct empty headers in case of GET-PARAMS request) */ +static APR_INLINE void mrcp_generic_header_name_property_add(mrcp_message_t *mrcp_message, size_t id) +{ + mrcp_header_name_property_add(&mrcp_message->header.generic_header_accessor,id); +} + +/** Check MRCP generic-header proprerty */ +static APR_INLINE apt_bool_t mrcp_generic_header_property_check(mrcp_message_t *mrcp_message, size_t id) +{ + return mrcp_header_property_check(&mrcp_message->header.generic_header_accessor,id); +} + + +/** Get MRCP resource-header */ +static APR_INLINE void* mrcp_resource_header_get(const mrcp_message_t *mrcp_message) +{ + return mrcp_message->header.resource_header_accessor.data; +} + +/** Prepare MRCP resource-header */ +static APR_INLINE void* mrcp_resource_header_prepare(mrcp_message_t *mrcp_message) +{ + return mrcp_header_allocate(&mrcp_message->header.resource_header_accessor,mrcp_message->pool); +} + +/** Add MRCP resource-header proprerty */ +static APR_INLINE void mrcp_resource_header_property_add(mrcp_message_t *mrcp_message, size_t id) +{ + mrcp_header_property_add(&mrcp_message->header.resource_header_accessor,id); +} + +/** Add MRCP resource-header name only proprerty (should be used to construct empty headers in case of GET-PARAMS request) */ +static APR_INLINE void mrcp_resource_header_name_property_add(mrcp_message_t *mrcp_message, size_t id) +{ + mrcp_header_name_property_add(&mrcp_message->header.resource_header_accessor,id); +} + +/** Check MRCP resource-header proprerty */ +static APR_INLINE apt_bool_t mrcp_resource_header_property_check(mrcp_message_t *mrcp_message, size_t id) +{ + return mrcp_header_property_check(&mrcp_message->header.resource_header_accessor,id); +} + +APT_END_EXTERN_C + +#endif /*__MRCP_MESSAGE_H__*/ diff --git a/libs/unimrcp/libs/mrcp/message/src/mrcp_generic_header.c b/libs/unimrcp/libs/mrcp/message/src/mrcp_generic_header.c new file mode 100644 index 0000000000..fe1b796dcd --- /dev/null +++ b/libs/unimrcp/libs/mrcp/message/src/mrcp_generic_header.c @@ -0,0 +1,330 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mrcp_generic_header.h" + +/** String table of mrcp generic-header fields (mrcp_generic_header_id) */ +static const apt_str_table_item_t generic_header_string_table[] = { + {{"Active-Request-Id-List", 22},3}, + {{"Proxy-Sync-Id", 13},0}, + {{"Accept-Charset", 14},7}, + {{"Content-Type", 12},9}, + {{"Content-Id", 10},9}, + {{"Content-Base", 12},8}, + {{"Content-Encoding", 16},9}, + {{"Content-Location", 16},9}, + {{"Content-Length", 14},10}, + {{"Cache-Control", 13},1}, + {{"Logging-Tag", 11},0}, + {{"Vendor-Specific-Parameters",26},0}, + {{"Accept", 6},6}, + {{"Fetch-Timeout", 13},0}, + {{"Set-Cookie", 10},10}, + {{"Set-Cookie2", 11},10} +}; + + +/** Parse mrcp request-id list */ +static apt_bool_t mrcp_request_id_list_parse(mrcp_request_id_list_t *request_id_list, const apt_str_t *value) +{ + apt_str_t field; + apt_text_stream_t stream; + stream.text = *value; + stream.pos = stream.text.buf; + request_id_list->count = 0; + while(request_id_list->count < MAX_ACTIVE_REQUEST_ID_COUNT) { + if(apt_text_field_read(&stream,',',TRUE,&field) == FALSE) { + break; + } + request_id_list->ids[request_id_list->count] = apt_size_value_parse(&field); + request_id_list->count++; + } + return TRUE; +} + +/** Generate mrcp request-id list */ +static apt_bool_t mrcp_request_id_list_generate(mrcp_request_id_list_t *request_id_list, apt_text_stream_t *stream) +{ + size_t i; + for(i=0; icount; i++) { + apt_size_value_generate(request_id_list->ids[i],stream); + if(i < request_id_list->count-1) { + *stream->pos++ = ','; + } + } + return TRUE; +} + + +/** Initialize generic-header */ +static void mrcp_generic_header_init(mrcp_generic_header_t *generic_header) +{ + generic_header->active_request_id_list.count = 0; + apt_string_reset(&generic_header->proxy_sync_id); + apt_string_reset(&generic_header->accept_charset); + apt_string_reset(&generic_header->content_type); + apt_string_reset(&generic_header->content_id); + apt_string_reset(&generic_header->content_base); + apt_string_reset(&generic_header->content_encoding); + apt_string_reset(&generic_header->content_location); + generic_header->content_length = 0; + apt_string_reset(&generic_header->cache_control); + apt_string_reset(&generic_header->logging_tag); + generic_header->vendor_specific_params = NULL; + /* initializes additionnal MRCP V2 generic headers */ + apt_string_reset(&generic_header->accept); + generic_header->fetch_timeout = 0; + apt_string_reset(&generic_header->set_cookie); + apt_string_reset(&generic_header->set_cookie2); +} + + +/** Allocate generic-header */ +static void* mrcp_generic_header_allocate(mrcp_header_accessor_t *accessor, apr_pool_t *pool) +{ + mrcp_generic_header_t *generic_header = apr_palloc(pool,sizeof(mrcp_generic_header_t)); + mrcp_generic_header_init(generic_header); + accessor->data = generic_header; + return accessor->data; +} + +/** Parse generic-header */ +static apt_bool_t mrcp_generic_header_parse(mrcp_header_accessor_t *accessor, size_t id, const apt_str_t *value, apr_pool_t *pool) +{ + apt_bool_t status = TRUE; + mrcp_generic_header_t *generic_header = accessor->data; + switch(id) { + case GENERIC_HEADER_ACTIVE_REQUEST_ID_LIST: + mrcp_request_id_list_parse(&generic_header->active_request_id_list,value); + break; + case GENERIC_HEADER_PROXY_SYNC_ID: + apt_string_copy(&generic_header->proxy_sync_id,value,pool); + break; + case GENERIC_HEADER_ACCEPT_CHARSET: + apt_string_copy(&generic_header->accept_charset,value,pool); + break; + case GENERIC_HEADER_CONTENT_TYPE: + apt_string_copy(&generic_header->content_type,value,pool); + break; + case GENERIC_HEADER_CONTENT_ID: + apt_string_copy(&generic_header->content_id,value,pool); + break; + case GENERIC_HEADER_CONTENT_BASE: + apt_string_copy(&generic_header->content_base,value,pool); + break; + case GENERIC_HEADER_CONTENT_ENCODING: + apt_string_copy(&generic_header->content_encoding,value,pool); + break; + case GENERIC_HEADER_CONTENT_LOCATION: + apt_string_copy(&generic_header->content_location,value,pool); + break; + case GENERIC_HEADER_CONTENT_LENGTH: + generic_header->content_length = apt_size_value_parse(value); + break; + case GENERIC_HEADER_CACHE_CONTROL: + apt_string_copy(&generic_header->cache_control,value,pool); + break; + case GENERIC_HEADER_LOGGING_TAG: + apt_string_copy(&generic_header->logging_tag,value,pool); + break; + case GENERIC_HEADER_VENDOR_SPECIFIC_PARAMS: + if(!generic_header->vendor_specific_params) { + generic_header->vendor_specific_params = apt_pair_array_create(1,pool); + } + apt_pair_array_parse(generic_header->vendor_specific_params,value,pool); + break; + case GENERIC_HEADER_ACCEPT: + apt_string_copy(&generic_header->accept,value,pool); + break; + case GENERIC_HEADER_FETCH_TIMEOUT: + generic_header->fetch_timeout = apt_size_value_parse(value); + break; + case GENERIC_HEADER_SET_COOKIE: + apt_string_copy(&generic_header->set_cookie,value,pool); + break; + case GENERIC_HEADER_SET_COOKIE2: + apt_string_copy(&generic_header->set_cookie2,value,pool); + break; + default: + status = FALSE; + } + return status; +} + +/** Generate generic-header */ +static apt_bool_t mrcp_generic_header_generate(mrcp_header_accessor_t *accessor, size_t id, apt_text_stream_t *value) +{ + mrcp_generic_header_t *generic_header = accessor->data; + switch(id) { + case GENERIC_HEADER_ACTIVE_REQUEST_ID_LIST: + mrcp_request_id_list_generate(&generic_header->active_request_id_list,value); + break; + case GENERIC_HEADER_PROXY_SYNC_ID: + apt_string_value_generate(&generic_header->proxy_sync_id,value); + break; + case GENERIC_HEADER_ACCEPT_CHARSET: + apt_string_value_generate(&generic_header->accept_charset,value); + break; + case GENERIC_HEADER_CONTENT_TYPE: + apt_string_value_generate(&generic_header->content_type,value); + break; + case GENERIC_HEADER_CONTENT_ID: + apt_string_value_generate(&generic_header->content_id,value); + break; + case GENERIC_HEADER_CONTENT_BASE: + apt_string_value_generate(&generic_header->content_base,value); + break; + case GENERIC_HEADER_CONTENT_ENCODING: + apt_string_value_generate(&generic_header->content_encoding,value); + break; + case GENERIC_HEADER_CONTENT_LOCATION: + apt_string_value_generate(&generic_header->content_location,value); + break; + case GENERIC_HEADER_CONTENT_LENGTH: + apt_size_value_generate(generic_header->content_length,value); + break; + case GENERIC_HEADER_CACHE_CONTROL: + apt_string_value_generate(&generic_header->cache_control,value); + break; + case GENERIC_HEADER_LOGGING_TAG: + apt_string_value_generate(&generic_header->logging_tag,value); + break; + case GENERIC_HEADER_VENDOR_SPECIFIC_PARAMS: + apt_pair_array_generate(generic_header->vendor_specific_params,value); + break; + case GENERIC_HEADER_ACCEPT: + apt_string_value_generate(&generic_header->accept,value); + break; + case GENERIC_HEADER_FETCH_TIMEOUT: + apt_size_value_generate(generic_header->fetch_timeout,value); + break; + case GENERIC_HEADER_SET_COOKIE: + apt_string_value_generate(&generic_header->set_cookie,value); + break; + case GENERIC_HEADER_SET_COOKIE2: + apt_string_value_generate(&generic_header->set_cookie2,value); + break; + default: + break; + } + return TRUE; +} + +/** Duplicate generic-header */ +static apt_bool_t mrcp_generic_header_duplicate(mrcp_header_accessor_t *accessor, const mrcp_header_accessor_t *src, size_t id, apr_pool_t *pool) +{ + mrcp_generic_header_t *generic_header = accessor->data; + const mrcp_generic_header_t *src_generic_header = src->data; + apt_bool_t status = TRUE; + + if(!generic_header || !src_generic_header) { + return FALSE; + } + + switch(id) { + case GENERIC_HEADER_ACTIVE_REQUEST_ID_LIST: + break; + case GENERIC_HEADER_PROXY_SYNC_ID: + apt_string_copy(&generic_header->proxy_sync_id,&src_generic_header->proxy_sync_id,pool); + break; + case GENERIC_HEADER_ACCEPT_CHARSET: + apt_string_copy(&generic_header->accept_charset,&src_generic_header->accept_charset,pool); + break; + case GENERIC_HEADER_CONTENT_TYPE: + apt_string_copy(&generic_header->content_type,&src_generic_header->content_type,pool); + break; + case GENERIC_HEADER_CONTENT_ID: + apt_string_copy(&generic_header->content_id,&src_generic_header->content_id,pool); + break; + case GENERIC_HEADER_CONTENT_BASE: + apt_string_copy(&generic_header->content_base,&src_generic_header->content_base,pool); + break; + case GENERIC_HEADER_CONTENT_ENCODING: + apt_string_copy(&generic_header->content_encoding,&src_generic_header->content_encoding,pool); + break; + case GENERIC_HEADER_CONTENT_LOCATION: + apt_string_copy(&generic_header->content_location,&src_generic_header->content_location,pool); + break; + case GENERIC_HEADER_CONTENT_LENGTH: + generic_header->content_length = src_generic_header->content_length; + break; + case GENERIC_HEADER_CACHE_CONTROL: + apt_string_copy(&generic_header->cache_control,&src_generic_header->cache_control,pool); + break; + case GENERIC_HEADER_LOGGING_TAG: + apt_string_copy(&generic_header->logging_tag,&src_generic_header->logging_tag,pool); + break; + case GENERIC_HEADER_VENDOR_SPECIFIC_PARAMS: + generic_header->vendor_specific_params = apt_pair_array_copy(src_generic_header->vendor_specific_params,pool); + break; + case GENERIC_HEADER_ACCEPT: + apt_string_copy(&generic_header->accept,&src_generic_header->accept,pool); + break; + case GENERIC_HEADER_FETCH_TIMEOUT: + generic_header->fetch_timeout = src_generic_header->fetch_timeout; + break; + case GENERIC_HEADER_SET_COOKIE: + apt_string_copy(&generic_header->set_cookie,&src_generic_header->set_cookie,pool); + break; + case GENERIC_HEADER_SET_COOKIE2: + apt_string_copy(&generic_header->set_cookie2,&src_generic_header->set_cookie2,pool); + break; + default: + status = FALSE; + } + return status; +} + +static const mrcp_header_vtable_t vtable = { + mrcp_generic_header_allocate, + NULL, /* nothing to destroy */ + mrcp_generic_header_parse, + mrcp_generic_header_generate, + mrcp_generic_header_duplicate, + generic_header_string_table, + GENERIC_HEADER_COUNT +}; + + +MRCP_DECLARE(const mrcp_header_vtable_t*) mrcp_generic_header_vtable_get(mrcp_version_e version) +{ + return &vtable; +} + + +/** Append active request id list */ +MRCP_DECLARE(apt_bool_t) active_request_id_list_append(mrcp_generic_header_t *generic_header, mrcp_request_id request_id) +{ + mrcp_request_id_list_t *request_id_list = &generic_header->active_request_id_list; + if(request_id_list->count >= MAX_ACTIVE_REQUEST_ID_COUNT) { + return FALSE; + } + request_id_list->ids[request_id_list->count++] = request_id; + return TRUE; +} + +/** Find request id in active request id list */ +MRCP_DECLARE(apt_bool_t) active_request_id_list_find(mrcp_generic_header_t *generic_header, mrcp_request_id request_id) +{ + size_t i; + mrcp_request_id_list_t *request_id_list = &generic_header->active_request_id_list; + for(i=0; icount; i++) { + if(request_id_list->ids[i] == request_id) { + return TRUE; + } + } + return FALSE; +} diff --git a/libs/unimrcp/libs/mrcp/message/src/mrcp_header_accessor.c b/libs/unimrcp/libs/mrcp/message/src/mrcp_header_accessor.c new file mode 100644 index 0000000000..e7219006dc --- /dev/null +++ b/libs/unimrcp/libs/mrcp/message/src/mrcp_header_accessor.c @@ -0,0 +1,175 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mrcp_header_accessor.h" + +typedef enum { + MRCP_HEADER_FIELD_NONE = 0x0, + MRCP_HEADER_FIELD_NAME = 0x1, + MRCP_HEADER_FIELD_VALUE = 0x2, + MRCP_HEADER_FIELD_NAME_VALUE = MRCP_HEADER_FIELD_NAME | MRCP_HEADER_FIELD_VALUE +} mrcp_header_property_e; + + +MRCP_DECLARE(apt_bool_t) mrcp_header_parse(mrcp_header_accessor_t *accessor, const apt_pair_t *pair, apr_pool_t *pool) +{ + size_t id; + if(!accessor->vtable) { + return FALSE; + } + + id = apt_string_table_id_find(accessor->vtable->field_table,accessor->vtable->field_count,&pair->name); + if(id >= accessor->vtable->field_count) { + return FALSE; + } + + if(!pair->value.length) { + mrcp_header_name_property_add(accessor,id); + return TRUE; + } + + if(accessor->vtable->parse_field(accessor,id,&pair->value,pool) == FALSE) { + return FALSE; + } + + mrcp_header_property_add(accessor,id); + return TRUE; +} + +MRCP_DECLARE(apt_bool_t) mrcp_header_generate(mrcp_header_accessor_t *accessor, apt_text_stream_t *text_stream) +{ + const apt_str_t *name; + apr_size_t i,j; + char prop; + + if(!accessor->vtable) { + return FALSE; + } + + for(i=0, j=0; ivtable->field_count && jcounter; i++) { + prop = accessor->properties[i]; + if((prop & MRCP_HEADER_FIELD_NAME) == MRCP_HEADER_FIELD_NAME) { + j++; + name = apt_string_table_str_get(accessor->vtable->field_table,accessor->vtable->field_count,i); + if(!name) continue; + + apt_text_header_name_generate(name,text_stream); + if((prop & MRCP_HEADER_FIELD_VALUE) == MRCP_HEADER_FIELD_VALUE) { + accessor->vtable->generate_field(accessor,i,text_stream); + } + apt_text_eol_insert(text_stream); + } + } + + return TRUE; +} + +MRCP_DECLARE(void) mrcp_header_property_add(mrcp_header_accessor_t *accessor, apr_size_t id) +{ + if(id < accessor->vtable->field_count) { + char *prop = &accessor->properties[id]; + if((*prop & MRCP_HEADER_FIELD_NAME) != MRCP_HEADER_FIELD_NAME) { + accessor->counter++; + } + *prop = MRCP_HEADER_FIELD_NAME_VALUE; + } +} + +MRCP_DECLARE(void) mrcp_header_name_property_add(mrcp_header_accessor_t *accessor, apr_size_t id) +{ + if(id < accessor->vtable->field_count) { + char *prop = &accessor->properties[id]; + if((*prop & MRCP_HEADER_FIELD_NAME) != MRCP_HEADER_FIELD_NAME) { + *prop = MRCP_HEADER_FIELD_NAME; + accessor->counter++; + } + } +} + + +MRCP_DECLARE(void) mrcp_header_property_remove(mrcp_header_accessor_t *accessor, apr_size_t id) +{ + if(id < accessor->vtable->field_count) { + char *prop = &accessor->properties[id]; + if((*prop & MRCP_HEADER_FIELD_NAME) == MRCP_HEADER_FIELD_NAME) { + accessor->counter--; + } + *prop = MRCP_HEADER_FIELD_NONE; + } +} + +MRCP_DECLARE(apt_bool_t) mrcp_header_property_check(mrcp_header_accessor_t *accessor, apr_size_t id) +{ + if((id < accessor->vtable->field_count) && accessor->properties) { + if((accessor->properties[id] & MRCP_HEADER_FIELD_NAME) == MRCP_HEADER_FIELD_NAME) { + return TRUE; + } + } + return FALSE; +} + + +MRCP_DECLARE(apt_bool_t) mrcp_header_set(mrcp_header_accessor_t *accessor, const mrcp_header_accessor_t *src, const mrcp_header_accessor_t *mask, apr_pool_t *pool) +{ + apr_size_t i,j; + + if(!accessor->vtable || !src->vtable) { + return FALSE; + } + + mrcp_header_allocate(accessor,pool); + + for(i=0, j=0; i < src->vtable->field_count && j < src->counter; i++) { + if((mask->properties[i] & src->properties[i] & MRCP_HEADER_FIELD_NAME) == MRCP_HEADER_FIELD_NAME) { + j++; + if((src->properties[i] & MRCP_HEADER_FIELD_VALUE) == MRCP_HEADER_FIELD_VALUE) { + accessor->vtable->duplicate_field(accessor,src,i,pool); + mrcp_header_property_add(accessor,i); + } + else { + mrcp_header_name_property_add(accessor,i); + } + } + } + + return TRUE; +} + +MRCP_DECLARE(apt_bool_t) mrcp_header_inherit(mrcp_header_accessor_t *accessor, const mrcp_header_accessor_t *parent, apr_pool_t *pool) +{ + apr_size_t i,j; + + if(!accessor->vtable || !parent->vtable) { + return FALSE; + } + + mrcp_header_allocate(accessor,pool); + + for(i=0, j=0; ivtable->field_count && j < parent->counter; i++) { + if((parent->properties[i] & MRCP_HEADER_FIELD_NAME) == MRCP_HEADER_FIELD_NAME) { + j++; + if((parent->properties[i] & MRCP_HEADER_FIELD_VALUE) == MRCP_HEADER_FIELD_VALUE) { + accessor->vtable->duplicate_field(accessor,parent,i,pool); + mrcp_header_property_add(accessor,i); + } + else { + mrcp_header_name_property_add(accessor,i); + } + } + } + + return TRUE; +} diff --git a/libs/unimrcp/libs/mrcp/message/src/mrcp_message.c b/libs/unimrcp/libs/mrcp/message/src/mrcp_message.c new file mode 100644 index 0000000000..dbe8627f5d --- /dev/null +++ b/libs/unimrcp/libs/mrcp/message/src/mrcp_message.c @@ -0,0 +1,691 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mrcp_message.h" +#include "mrcp_generic_header.h" +#include "apt_log.h" + +/** Protocol name used in version string */ +#define MRCP_NAME "MRCP" +#define MRCP_NAME_LENGTH (sizeof(MRCP_NAME)-1) + +#define MRCP_CHANNEL_ID "Channel-Identifier" +#define MRCP_CHANNEL_ID_LENGTH (sizeof(MRCP_CHANNEL_ID)-1) + +/** Separators used in MRCP version string parse/generate */ +#define MRCP_NAME_VERSION_SEPARATOR '/' +#define MRCP_VERSION_MAJOR_MINOR_SEPARATOR '.' + +#define MRCP_MESSAGE_LENGTH_MAX_DIGITS_COUNT 4 + + +/** String table of MRCP request-states (mrcp_request_state_t) */ +static const apt_str_table_item_t mrcp_request_state_string_table[] = { + {{"COMPLETE", 8},0}, + {{"IN-PROGRESS",11},0}, + {{"PENDING", 7},0} +}; + + +/** Parse MRCP version */ +static mrcp_version_e mrcp_version_parse(const apt_str_t *field) +{ + mrcp_version_e version = MRCP_VERSION_UNKNOWN; + const char *pos; + if(field->length <= MRCP_NAME_LENGTH || strncasecmp(field->buf,MRCP_NAME,MRCP_NAME_LENGTH) != 0) { + /* unexpected protocol name */ + return version; + } + + pos = field->buf + MRCP_NAME_LENGTH; + if(*pos == MRCP_NAME_VERSION_SEPARATOR) { + pos++; + switch(*pos) { + case '1': version = MRCP_VERSION_1; break; + case '2': version = MRCP_VERSION_2; break; + default: ; + } + } + return version; +} + +/** Generate MRCP version */ +static apt_bool_t mrcp_version_generate(mrcp_version_e version, apt_text_stream_t *stream) +{ + memcpy(stream->pos,MRCP_NAME,MRCP_NAME_LENGTH); + stream->pos += MRCP_NAME_LENGTH; + *stream->pos++ = MRCP_NAME_VERSION_SEPARATOR; + apt_size_value_generate(version,stream); + *stream->pos++ = MRCP_VERSION_MAJOR_MINOR_SEPARATOR; + *stream->pos++ = '0'; + return TRUE; +} + +/** Parse MRCP request-state used in MRCP response and event */ +static APR_INLINE mrcp_request_state_e mrcp_request_state_parse(const apt_str_t *request_state_str) +{ + return apt_string_table_id_find(mrcp_request_state_string_table,MRCP_REQUEST_STATE_COUNT,request_state_str); +} + +/** Generate MRCP request-state used in MRCP response and event */ +static apt_bool_t mrcp_request_state_generate(mrcp_request_state_e request_state, apt_text_stream_t *stream) +{ + const apt_str_t *name; + name = apt_string_table_str_get(mrcp_request_state_string_table,MRCP_REQUEST_STATE_COUNT,request_state); + if(request_state < MRCP_REQUEST_STATE_COUNT) { + memcpy(stream->pos,name->buf,name->length); + stream->pos += name->length; + } + return TRUE; +} + + +/** Parse MRCP request-id */ +static APR_INLINE mrcp_request_id mrcp_request_id_parse(const apt_str_t *field) +{ + return apt_size_value_parse(field); +} + +/** Generate MRCP request-id */ +static APR_INLINE apt_bool_t mrcp_request_id_generate(mrcp_request_id request_id, apt_text_stream_t *stream) +{ + return apt_size_value_generate(request_id,stream); +} + +/** Parse MRCP status-code */ +static APR_INLINE mrcp_status_code_e mrcp_status_code_parse(const apt_str_t *field) +{ + return apt_size_value_parse(field); +} + +/** Generate MRCP status-code */ +static APR_INLINE size_t mrcp_status_code_generate(mrcp_status_code_e status_code, apt_text_stream_t *stream) +{ + return apt_size_value_generate(status_code,stream); +} + + +/** Parse MRCP request-line */ +static apt_bool_t mrcp_request_line_parse(mrcp_start_line_t *start_line, apt_text_stream_t *stream) +{ + apt_str_t field; + if(apt_text_field_read(stream,APT_TOKEN_SP,TRUE,&field) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot parse request-id in request-line"); + return FALSE; + } + start_line->request_id = mrcp_request_id_parse(&field); + + if(apt_text_field_read(stream,APT_TOKEN_SP,TRUE,&field) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot parse mrcp-version in request-line"); + return FALSE; + } + + start_line->request_state = mrcp_request_state_parse(&field); + if(start_line->request_state == MRCP_REQUEST_STATE_UNKNOWN) { + /* request-line */ + start_line->message_type = MRCP_MESSAGE_TYPE_REQUEST; + } + else { + /* event line */ + start_line->message_type = MRCP_MESSAGE_TYPE_EVENT; + + if(apt_text_field_read(stream,APT_TOKEN_SP,TRUE,&field) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot parse mrcp-version in request-line"); + return FALSE; + } + } + + start_line->version = mrcp_version_parse(&field); + if(start_line->version == MRCP_VERSION_UNKNOWN) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown mrcp-version"); + return FALSE; + } + return TRUE; +} + +/** Generate MRCP request-line */ +static apt_bool_t mrcp_request_line_generate(mrcp_start_line_t *start_line, apt_text_stream_t *stream) +{ + memcpy(stream->pos,start_line->method_name.buf,start_line->method_name.length); + stream->pos += start_line->method_name.length; + *stream->pos++ = APT_TOKEN_SP; + + mrcp_request_id_generate(start_line->request_id,stream); + *stream->pos++ = APT_TOKEN_SP; + + if(start_line->message_type == MRCP_MESSAGE_TYPE_REQUEST) { + if(start_line->status_code != MRCP_STATUS_CODE_UNKNOWN) { + mrcp_status_code_generate(start_line->status_code,stream); + *stream->pos++ = APT_TOKEN_SP; + } + } + else if(start_line->message_type == MRCP_MESSAGE_TYPE_EVENT) { + mrcp_request_state_generate(start_line->request_state,stream); + *stream->pos++ = APT_TOKEN_SP; + } + + mrcp_version_generate(start_line->version,stream); + return TRUE; +} + +/** Parse MRCP response-line */ +static apt_bool_t mrcp_response_line_parse(mrcp_start_line_t *start_line, apt_text_stream_t *stream) +{ + apt_str_t field; + start_line->length = 0; + if(start_line->version == MRCP_VERSION_2) { + if(apt_text_field_read(stream,APT_TOKEN_SP,TRUE,&field) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot parse message-length in response-line"); + return FALSE; + } + start_line->length = apt_size_value_parse(&field); + } + + if(apt_text_field_read(stream,APT_TOKEN_SP,TRUE,&field) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot parse request-id in response-line"); + return FALSE; + } + start_line->request_id = mrcp_request_id_parse(&field); + + if(apt_text_field_read(stream,APT_TOKEN_SP,TRUE,&field) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot parse status-code in response-line"); + return FALSE; + } + start_line->status_code = mrcp_status_code_parse(&field); + + if(apt_text_field_read(stream,APT_TOKEN_SP,TRUE,&field) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot parse request-state in response-line"); + return FALSE; + } + start_line->request_state = mrcp_request_state_parse(&field); + return TRUE; +} + +/** Generate MRCP response-line */ +static apt_bool_t mrcp_response_line_generate(mrcp_start_line_t *start_line, apt_text_stream_t *stream) +{ + mrcp_version_generate(start_line->version,stream); + *stream->pos++ = APT_TOKEN_SP; + + mrcp_request_id_generate(start_line->request_id,stream); + *stream->pos++ = APT_TOKEN_SP; + + mrcp_status_code_generate(start_line->status_code,stream); + *stream->pos++ = APT_TOKEN_SP; + + mrcp_request_state_generate(start_line->request_state,stream); + return TRUE; +} + +/** Parse MRCP v2 start-line */ +static apt_bool_t mrcp_v2_start_line_parse(mrcp_start_line_t *start_line, apt_text_stream_t *stream, apr_pool_t *pool) +{ + apt_str_t field; + if(apt_text_field_read(stream,APT_TOKEN_SP,TRUE,&field) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot parse message-length in v2 start-line"); + return FALSE; + } + start_line->length = apt_size_value_parse(&field); + + if(apt_text_field_read(stream,APT_TOKEN_SP,TRUE,&field) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot parse request-id in v2 start-line"); + return FALSE; + } + start_line->request_id = mrcp_request_id_parse(&field); + if(start_line->request_id == 0 && *field.buf != '0') { + /* parsing MRCP v2 request or event */ + start_line->message_type = MRCP_MESSAGE_TYPE_REQUEST; + apt_string_copy(&start_line->method_name,&field,pool); + + if(apt_text_field_read(stream,APT_TOKEN_SP,TRUE,&field) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot parse request-id in v2 start-line"); + return FALSE; + } + start_line->request_id = mrcp_request_id_parse(&field); + + if(apt_text_field_read(stream,APT_TOKEN_SP,TRUE,&field) == TRUE) { + /* parsing MRCP v2 event */ + start_line->request_state = mrcp_request_state_parse(&field); + start_line->message_type = MRCP_MESSAGE_TYPE_EVENT; + } + } + else { + /* parsing MRCP v2 response */ + start_line->message_type = MRCP_MESSAGE_TYPE_RESPONSE; + + if(apt_text_field_read(stream,APT_TOKEN_SP,TRUE,&field) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot parse status-code in v2 start-line"); + return FALSE; + } + start_line->status_code = mrcp_status_code_parse(&field); + + if(apt_text_field_read(stream,APT_TOKEN_SP,TRUE,&field) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot parse request-state in v2 start-line"); + return FALSE; + } + start_line->request_state = mrcp_request_state_parse(&field); + } + + return TRUE; +} + +/** Generate MRCP v2 start-line */ +static apt_bool_t mrcp_v2_start_line_generate(mrcp_start_line_t *start_line, apt_text_stream_t *stream) +{ + char *pos = stream->pos; + mrcp_version_generate(start_line->version,stream); + *stream->pos++ = APT_TOKEN_SP; + + start_line->length = stream->pos - pos; /* length is temrorary used to store offset */ + /* reserving MRCP_MESSAGE_LENGTH_MAX_DIGITS_COUNT space for start_line->length */ + memset(stream->pos,APT_TOKEN_SP,MRCP_MESSAGE_LENGTH_MAX_DIGITS_COUNT+1); + stream->pos += MRCP_MESSAGE_LENGTH_MAX_DIGITS_COUNT+1; + + if(start_line->message_type == MRCP_MESSAGE_TYPE_RESPONSE) { + mrcp_request_id_generate(start_line->request_id,stream); + *stream->pos++ = APT_TOKEN_SP; + + mrcp_status_code_generate(start_line->status_code,stream); + *stream->pos++ = APT_TOKEN_SP; + + mrcp_request_state_generate(start_line->request_state,stream); + *stream->pos++ = APT_TOKEN_SP; + } + else { + memcpy(stream->pos,start_line->method_name.buf,start_line->method_name.length); + stream->pos += start_line->method_name.length; + *stream->pos++ = APT_TOKEN_SP; + + mrcp_request_id_generate(start_line->request_id,stream); + if(start_line->message_type == MRCP_MESSAGE_TYPE_EVENT) { + *stream->pos++ = APT_TOKEN_SP; + mrcp_request_state_generate(start_line->request_state,stream); + } + } + return TRUE; +} + +/** Initialize MRCP start-line */ +MRCP_DECLARE(void) mrcp_start_line_init(mrcp_start_line_t *start_line) +{ + start_line->message_type = MRCP_MESSAGE_TYPE_UNKNOWN; + start_line->version = MRCP_VERSION_UNKNOWN; + start_line->length = 0; + start_line->request_id = 0; + apt_string_reset(&start_line->method_name); + start_line->status_code = MRCP_STATUS_CODE_UNKNOWN; + start_line->request_state = MRCP_REQUEST_STATE_UNKNOWN; +} + +/** Parse MRCP start-line */ +MRCP_DECLARE(apt_bool_t) mrcp_start_line_parse(mrcp_start_line_t *start_line, apt_text_stream_t *text_stream, apr_pool_t *pool) +{ + apt_text_stream_t line; + apt_str_t field; + apt_bool_t status = TRUE; + start_line->message_type = MRCP_MESSAGE_TYPE_UNKNOWN; + if(apt_text_line_read(text_stream,&line.text) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot parse MRCP start-line"); + return FALSE; + } + line.pos = line.text.buf; + + if(apt_text_field_read(&line,APT_TOKEN_SP,TRUE,&field) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot read the first field in start-line"); + return FALSE; + } + + if(field.buf == strstr(field.buf,MRCP_NAME)) { + start_line->version = mrcp_version_parse(&field); + + if(start_line->version == MRCP_VERSION_1) { + /* parsing MRCP v1 response */ + start_line->message_type = MRCP_MESSAGE_TYPE_RESPONSE; + status = mrcp_response_line_parse(start_line,&line); + } + else if(start_line->version == MRCP_VERSION_2) { + /* parsing MRCP v2 start-line (request/response/event) */ + status = mrcp_v2_start_line_parse(start_line,&line,pool); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown MRCP version"); + return FALSE; + } + } + else { + /* parsing MRCP v1 request or event */ + apt_string_copy(&start_line->method_name,&field,pool); + status = mrcp_request_line_parse(start_line,&line); + } + return status; +} + +/** Generate MRCP start-line */ +MRCP_DECLARE(apt_bool_t) mrcp_start_line_generate(mrcp_start_line_t *start_line, apt_text_stream_t *text_stream) +{ + apt_bool_t status = FALSE; + if(start_line->version == MRCP_VERSION_1) { + switch(start_line->message_type) { + case MRCP_MESSAGE_TYPE_REQUEST: + status = mrcp_request_line_generate(start_line,text_stream); + break; + case MRCP_MESSAGE_TYPE_RESPONSE: + status = mrcp_response_line_generate(start_line,text_stream); + break; + case MRCP_MESSAGE_TYPE_EVENT: + status = mrcp_request_line_generate(start_line,text_stream); + break; + default: + break; + } + } + else if(start_line->version == MRCP_VERSION_2) { + status = mrcp_v2_start_line_generate(start_line,text_stream); + } + + if(status == TRUE) { + apt_text_eol_insert(text_stream); + } + + return status; +} + +/** Finalize MRCP start-line generation */ +MRCP_DECLARE(apt_bool_t) mrcp_start_line_finalize(mrcp_start_line_t *start_line, apr_size_t content_length, apt_text_stream_t *text_stream) +{ + apr_size_t length = text_stream->pos - text_stream->text.buf + content_length; + if(start_line->version == MRCP_VERSION_2) { + /* message-length includes the number of bytes that specify the message-length in the header */ + /* too comlex to generate!!! see the discussion */ + /* http://www1.ietf.org/mail-archive/web/speechsc/current/msg01734.html */ + apt_str_t field; + field.buf = text_stream->text.buf + start_line->length; /* length is temrorary used to store offset */ + length -= MRCP_MESSAGE_LENGTH_MAX_DIGITS_COUNT; + apt_var_length_value_generate(&length,MRCP_MESSAGE_LENGTH_MAX_DIGITS_COUNT,&field); + field.buf[field.length] = APT_TOKEN_SP; + start_line->length += field.length; + + field.length = MRCP_MESSAGE_LENGTH_MAX_DIGITS_COUNT - field.length; + if(field.length) { + memmove(text_stream->text.buf+field.length,text_stream->text.buf,start_line->length); + text_stream->text.buf += field.length; + text_stream->text.length -= field.length; + } + } + + start_line->length = length; + return TRUE; +} + +/** Initialize MRCP channel-identifier */ +MRCP_DECLARE(void) mrcp_channel_id_init(mrcp_channel_id *channel_id) +{ + apt_string_reset(&channel_id->session_id); + apt_string_reset(&channel_id->resource_name); + channel_id->resource_id = 0; +} + +/** Parse MRCP channel-identifier */ +MRCP_DECLARE(apt_bool_t) mrcp_channel_id_parse(mrcp_channel_id *channel_id, apt_text_stream_t *text_stream, apr_pool_t *pool) +{ + apt_bool_t match = FALSE; + apt_pair_t pair; + do { + if(apt_text_header_read(text_stream,&pair) == TRUE) { + if(pair.name.length) { + if(pair.value.length && strncasecmp(pair.name.buf,MRCP_CHANNEL_ID,MRCP_CHANNEL_ID_LENGTH) == 0) { + match = TRUE; + apt_id_resource_parse(&pair.value,'@',&channel_id->session_id,&channel_id->resource_name,pool); + break; + } + /* skip this header, expecting channel identifier first */ + } + else { + /* empty header */ + break; + } + } + } + while(apt_text_is_eos(text_stream) == FALSE); + return match; +} + +/** Generate MRCP channel-identifier */ +MRCP_DECLARE(apt_bool_t) mrcp_channel_id_generate(mrcp_channel_id *channel_id, apt_text_stream_t *text_stream) +{ + apt_str_t *str; + char *pos = text_stream->pos; + + memcpy(pos,MRCP_CHANNEL_ID,MRCP_CHANNEL_ID_LENGTH); + pos += MRCP_CHANNEL_ID_LENGTH; + *pos++ = ':'; + *pos++ = ' '; + + str = &channel_id->session_id; + memcpy(pos,str->buf,str->length); + pos += str->length; + *pos++ = '@'; + + str = &channel_id->resource_name; + memcpy(pos,str->buf,str->length); + pos += str->length; + + text_stream->pos = pos; + apt_text_eol_insert(text_stream); + return TRUE; +} + +/** Parse MRCP message-header */ +MRCP_DECLARE(apt_bool_t) mrcp_message_header_parse(mrcp_message_header_t *message_header, apt_text_stream_t *text_stream, apr_pool_t *pool) +{ + apt_pair_t pair; + apt_bool_t result = FALSE; + + mrcp_header_allocate(&message_header->generic_header_accessor,pool); + mrcp_header_allocate(&message_header->resource_header_accessor,pool); + + do { + if(apt_text_header_read(text_stream,&pair) == TRUE) { + if(pair.name.length) { + /* normal header */ + if(mrcp_header_parse(&message_header->resource_header_accessor,&pair,pool) != TRUE) { + if(mrcp_header_parse(&message_header->generic_header_accessor,&pair,pool) != TRUE) { + /* unknown MRCP header */ + } + } + } + else { + /* empty header -> exit */ + result = TRUE; + break; + } + } + else { + /* malformed header, skip to the next one */ + } + } + while(apt_text_is_eos(text_stream) == FALSE); + + return result; +} + +/** Generate MRCP message-header */ +MRCP_DECLARE(apt_bool_t) mrcp_message_header_generate(mrcp_message_header_t *message_header, apt_text_stream_t *text_stream) +{ + mrcp_header_generate(&message_header->resource_header_accessor,text_stream); + mrcp_header_generate(&message_header->generic_header_accessor,text_stream); + apt_text_eol_insert(text_stream); + return TRUE; +} + +/** Set MRCP message-header */ +MRCP_DECLARE(apt_bool_t) mrcp_message_header_set(mrcp_message_header_t *message_header, const mrcp_message_header_t *src, apr_pool_t *pool) +{ + mrcp_header_set( + &message_header->resource_header_accessor, + &src->resource_header_accessor, + &src->resource_header_accessor,pool); + mrcp_header_set( + &message_header->generic_header_accessor, + &src->generic_header_accessor, + &src->generic_header_accessor,pool); + return TRUE; +} + +/** Get MRCP message-header */ +MRCP_DECLARE(apt_bool_t) mrcp_message_header_get(mrcp_message_header_t *message_header, const mrcp_message_header_t *src, apr_pool_t *pool) +{ + mrcp_header_set( + &message_header->resource_header_accessor, + &src->resource_header_accessor, + &message_header->resource_header_accessor, + pool); + mrcp_header_set( + &message_header->generic_header_accessor, + &src->generic_header_accessor, + &message_header->generic_header_accessor, + pool); + return TRUE; +} + +/** Inherit MRCP message-header */ +MRCP_DECLARE(apt_bool_t) mrcp_message_header_inherit(mrcp_message_header_t *message_header, const mrcp_message_header_t *parent, apr_pool_t *pool) +{ + mrcp_header_inherit(&message_header->resource_header_accessor,&parent->resource_header_accessor,pool); + mrcp_header_inherit(&message_header->generic_header_accessor,&parent->generic_header_accessor,pool); + return TRUE; +} + + +/** Parse MRCP message-body */ +MRCP_DECLARE(apt_bool_t) mrcp_body_parse(mrcp_message_t *message, apt_text_stream_t *text_stream, apr_pool_t *pool) +{ + if(mrcp_generic_header_property_check(message,GENERIC_HEADER_CONTENT_LENGTH) == TRUE) { + mrcp_generic_header_t *generic_header = mrcp_generic_header_get(message); + if(generic_header && generic_header->content_length) { + apt_str_t *body = &message->body; + body->length = generic_header->content_length; + if(body->length > (text_stream->text.length - (text_stream->pos - text_stream->text.buf))) { + body->length = text_stream->text.length - (text_stream->pos - text_stream->text.buf); + } + body->buf = apr_pstrmemdup(pool,text_stream->pos,body->length); + text_stream->pos += body->length; + } + } + return TRUE; +} + +/** Generate MRCP message-body */ +MRCP_DECLARE(apt_bool_t) mrcp_body_generate(mrcp_message_t *message, apt_text_stream_t *text_stream) +{ + apt_str_t *body = &message->body; + if(body->length) { + memcpy(text_stream->pos,body->buf,body->length); + text_stream->pos += body->length; + } + return TRUE; +} + + +/** Create MRCP message */ +MRCP_DECLARE(mrcp_message_t*) mrcp_message_create(apr_pool_t *pool) +{ + mrcp_message_t *message = apr_palloc(pool,sizeof(mrcp_message_t)); + mrcp_message_init(message,pool); + return message; +} + +/** Initialize MRCP message */ +MRCP_DECLARE(void) mrcp_message_init(mrcp_message_t *message, apr_pool_t *pool) +{ + mrcp_start_line_init(&message->start_line); + mrcp_channel_id_init(&message->channel_id); + mrcp_message_header_init(&message->header); + apt_string_reset(&message->body); + message->pool = pool; +} + +/** Initialize response/event message by request message */ +MRCP_DECLARE(void) mrcp_message_init_by_request(mrcp_message_t *message, const mrcp_message_t *request_message) +{ + message->channel_id = request_message->channel_id; + message->start_line.request_id = request_message->start_line.request_id; + message->start_line.version = request_message->start_line.version; + message->start_line.method_id = request_message->start_line.method_id; + message->header.generic_header_accessor.vtable = request_message->header.generic_header_accessor.vtable; + message->header.resource_header_accessor.vtable = request_message->header.resource_header_accessor.vtable; +} + +/** Create MRCP request message */ +MRCP_DECLARE(mrcp_message_t*) mrcp_request_create(mrcp_resource_id resource_id, mrcp_method_id method_id, apr_pool_t *pool) +{ + mrcp_message_t *request_message = mrcp_message_create(pool); + request_message->start_line.message_type = MRCP_MESSAGE_TYPE_REQUEST; + request_message->start_line.method_id = method_id; + request_message->channel_id.resource_id = resource_id; + return request_message; +} + +/** Create MRCP response message */ +MRCP_DECLARE(mrcp_message_t*) mrcp_response_create(const mrcp_message_t *request_message, apr_pool_t *pool) +{ + mrcp_message_t *response_message = mrcp_message_create(pool); + if(request_message) { + mrcp_message_init_by_request(response_message,request_message); + } + response_message->start_line.message_type = MRCP_MESSAGE_TYPE_RESPONSE; + response_message->start_line.request_state = MRCP_REQUEST_STATE_COMPLETE; + response_message->start_line.status_code = MRCP_STATUS_CODE_SUCCESS; + return response_message; +} + +/** Create MRCP event message */ +MRCP_DECLARE(mrcp_message_t*) mrcp_event_create(const mrcp_message_t *request_message, mrcp_method_id event_id, apr_pool_t *pool) +{ + mrcp_message_t *event_message = mrcp_message_create(pool); + if(request_message) { + mrcp_message_init_by_request(event_message,request_message); + } + event_message->start_line.message_type = MRCP_MESSAGE_TYPE_EVENT; + event_message->start_line.method_id = event_id; + return event_message; +} + +/** Destroy MRCP message */ +MRCP_DECLARE(void) mrcp_message_destroy(mrcp_message_t *message) +{ + apt_string_reset(&message->body); + mrcp_message_header_destroy(&message->header); +} + +/** Validate MRCP message */ +MRCP_DECLARE(apt_bool_t) mrcp_message_validate(mrcp_message_t *message) +{ + if(message->body.length) { + /* content length must be specified */ + mrcp_generic_header_t *generic_header = mrcp_generic_header_prepare(message); + if(!generic_header) { + return FALSE; + } + if(mrcp_generic_header_property_check(message,GENERIC_HEADER_CONTENT_LENGTH) != TRUE || + !generic_header->content_length) { + generic_header->content_length = message->body.length; + mrcp_generic_header_property_add(message,GENERIC_HEADER_CONTENT_LENGTH); + } + } + + return TRUE; +} diff --git a/libs/unimrcp/libs/mrcp/mrcp.vcproj b/libs/unimrcp/libs/mrcp/mrcp.vcproj new file mode 100644 index 0000000000..1ea0069435 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/mrcp.vcproj @@ -0,0 +1,300 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/libs/mrcp/resources/include/mrcp_default_factory.h b/libs/unimrcp/libs/mrcp/resources/include/mrcp_default_factory.h new file mode 100644 index 0000000000..7820aae47b --- /dev/null +++ b/libs/unimrcp/libs/mrcp/resources/include/mrcp_default_factory.h @@ -0,0 +1,35 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_DEFAULT_FACTORY_H__ +#define __MRCP_DEFAULT_FACTORY_H__ + +/** + * @file mrcp_default_factory.h + * @brief Default MRCP Resource Factory + */ + +#include "mrcp_resource_factory.h" + +APT_BEGIN_EXTERN_C + +/** Create default MRCP resource factory */ +MRCP_DECLARE(mrcp_resource_factory_t*) mrcp_default_factory_create(apr_pool_t *pool); + + +APT_END_EXTERN_C + +#endif /*__MRCP_DEFAULT_FACTORY_H__*/ diff --git a/libs/unimrcp/libs/mrcp/resources/include/mrcp_recog_header.h b/libs/unimrcp/libs/mrcp/resources/include/mrcp_recog_header.h new file mode 100644 index 0000000000..1669147e50 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/resources/include/mrcp_recog_header.h @@ -0,0 +1,216 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_RECOG_HEADER_H__ +#define __MRCP_RECOG_HEADER_H__ + +/** + * @file mrcp_recog_header.h + * @brief MRCP Recognizer Header + */ + +#include "mrcp_types.h" +#include "mrcp_header_accessor.h" + +APT_BEGIN_EXTERN_C + +/** MRCP recognizer headers */ +typedef enum { + RECOGNIZER_HEADER_CONFIDENCE_THRESHOLD, + RECOGNIZER_HEADER_SENSITIVITY_LEVEL, + RECOGNIZER_HEADER_SPEED_VS_ACCURACY, + RECOGNIZER_HEADER_N_BEST_LIST_LENGTH, + RECOGNIZER_HEADER_NO_INPUT_TIMEOUT, + RECOGNIZER_HEADER_RECOGNITION_TIMEOUT, + RECOGNIZER_HEADER_WAVEFORM_URI, + RECOGNIZER_HEADER_COMPLETION_CAUSE, + RECOGNIZER_HEADER_RECOGNIZER_CONTEXT_BLOCK, + RECOGNIZER_HEADER_START_INPUT_TIMERS, + RECOGNIZER_HEADER_SPEECH_COMPLETE_TIMEOUT, + RECOGNIZER_HEADER_SPEECH_INCOMPLETE_TIMEOUT, + RECOGNIZER_HEADER_DTMF_INTERDIGIT_TIMEOUT, + RECOGNIZER_HEADER_DTMF_TERM_TIMEOUT, + RECOGNIZER_HEADER_DTMF_TERM_CHAR, + RECOGNIZER_HEADER_FAILED_URI, + RECOGNIZER_HEADER_FAILED_URI_CAUSE, + RECOGNIZER_HEADER_SAVE_WAVEFORM, + RECOGNIZER_HEADER_NEW_AUDIO_CHANNEL, + RECOGNIZER_HEADER_SPEECH_LANGUAGE, + + /** Additional headers for MRCP v2 */ + RECOGNIZER_HEADER_INPUT_TYPE, + RECOGNIZER_HEADER_INPUT_WAVEFORM_URI, + RECOGNIZER_HEADER_COMPLETION_REASON, + RECOGNIZER_HEADER_MEDIA_TYPE, + RECOGNIZER_HEADER_VER_BUFFER_UTTERANCE, + RECOGNIZER_HEADER_RECOGNITION_MODE, + RECOGNIZER_HEADER_CANCEL_IF_QUEUE, + RECOGNIZER_HEADER_HOTWORD_MAX_DURATION, + RECOGNIZER_HEADER_HOTWORD_MIN_DURATION, + RECOGNIZER_HEADER_INTERPRET_TEXT, + RECOGNIZER_HEADER_DTMF_BUFFER_TIME, + RECOGNIZER_HEADER_CLEAR_DTMF_BUFFER, + RECOGNIZER_HEADER_EARLY_NO_MATCH, + + RECOGNIZER_HEADER_COUNT +} mrcp_recognizer_header_id; + + +/** MRCP recognizer completion-cause */ +typedef enum { + RECOGNIZER_COMPLETION_CAUSE_SUCCESS = 0, + RECOGNIZER_COMPLETION_CAUSE_NO_MATCH = 1, + RECOGNIZER_COMPLETION_CAUSE_NO_INPUT_TIMEOUT = 2, + RECOGNIZER_COMPLETION_CAUSE_RECOGNITION_TIMEOUT = 3, + RECOGNIZER_COMPLETION_CAUSE_GRAM_LOAD_FAILURE = 4, + RECOGNIZER_COMPLETION_CAUSE_GRAM_COMP_FAILURE = 5, + RECOGNIZER_COMPLETION_CAUSE_ERROR = 6, + RECOGNIZER_COMPLETION_CAUSE_SPEECH_TOO_EARLY = 7, + RECOGNIZER_COMPLETION_CAUSE_TOO_MUCH_SPEECH_TIMEOUT = 8, + RECOGNIZER_COMPLETION_CAUSE_URI_FAILURE = 9, + RECOGNIZER_COMPLETION_CAUSE_LANGUAGE_UNSUPPORTED = 10, + + /** Additional completion-cause for MRCP v2 */ + RECOGNIZER_COMPLETION_CAUSE_CANCELLED = 11, + RECOGNIZER_COMPLETION_CAUSE_SEMANTICS_FAILURE = 12, + RECOGNIZER_COMPLETION_CAUSE_PARTIAL_MATCH = 13, + RECOGNIZER_COMPLETION_CAUSE_PARTIAL_MATCH_MAXTIME = 14, + RECOGNIZER_COMPLETION_CAUSE_NO_MATCH_MAXTIME = 15, + RECOGNIZER_COMPLETION_CAUSE_GRAM_DEFINITION_FAILURE = 16, + + RECOGNIZER_COMPLETION_CAUSE_COUNT = 17, + RECOGNIZER_COMPLETION_CAUSE_UNKNOWN = RECOGNIZER_COMPLETION_CAUSE_COUNT +} mrcp_recog_completion_cause_e; + + + +/** MRCP recognizer-header declaration */ +typedef struct mrcp_recog_header_t mrcp_recog_header_t; + +/** MRCP recognizer-header */ +struct mrcp_recog_header_t { + /** Tells the recognizer resource what confidence level the client considers a + successful match */ + float confidence_threshold; + /** To filter out background noise and not mistake it for speech */ + float sensitivity_level; + /** Tunable towards Performance or Accuracy */ + float speed_vs_accuracy; + /** The client, by setting this header, can ask the recognition resource + to send it more than 1 alternative */ + apr_size_t n_best_list_length; + /** The client can use the no-input-timeout header to set this timeout */ + apr_size_t no_input_timeout; + /** The client can use the recognition-timeout header to set this timeout */ + apr_size_t recognition_timeout; + /** MUST be present in the RECOGNITION-COMPLETE event if the Save-Waveform + header was set to true */ + apt_str_t waveform_uri; + /** MUST be part of a RECOGNITION-COMPLETE, event coming from + the recognizer resource to the client */ + mrcp_recog_completion_cause_e completion_cause; + /** MAY be sent as part of the SET-PARAMS or GET-PARAMS request */ + apt_str_t recognizer_context_block; + /** MAY be sent as part of the RECOGNIZE request. A value of false tells + the recognizer to start recognition, but not to start the no-input timer yet */ + apt_bool_t start_input_timers; + /** Specifies the length of silence required following user + speech before the speech recognizer finalizes a result */ + apr_size_t speech_complete_timeout; + /** Specifies the required length of silence following user + speech after which a recognizer finalizes a result */ + apr_size_t speech_incomplete_timeout; + /** Specifies the inter-digit timeout value to use when + recognizing DTMF input */ + apr_size_t dtmf_interdigit_timeout; + /** Specifies the terminating timeout to use when + recognizing DTMF input*/ + apr_size_t dtmf_term_timeout; + /** Specifies the terminating DTMF character for DTMF input + recognition */ + char dtmf_term_char; + /** When a recognizer needs to fetch or access a URI and the access fails + the server SHOULD provide the failed URI in this header in the method response*/ + apt_str_t failed_uri; + /** When a recognizer method needs a recognizer to fetch or access a URI + and the access fails the server MUST provide the URI specific or + protocol specific response code for the URI in the Failed-URI header */ + apt_str_t failed_uri_cause; + /** Allows the client to request the recognizer resource to + save the audio input to the recognizer */ + apt_bool_t save_waveform; + /** MAY be specified in a RECOGNIZE request and allows the + client to tell the server that, from this point on, further input + audio comes from a different audio source */ + apt_bool_t new_audio_channel; + /** Specifies the language of recognition grammar data within + a session or request, if it is not specified within the data */ + apt_str_t speech_language; + + /** Additional headers for MRCP v2 */ + /** Specifies if the input that caused a barge-in was DTMF or speech */ + apt_str_t input_type; + /** Optional header specifies a URI pointing to audio content to be + processed by the RECOGNIZE operation */ + apt_str_t input_waveform_uri; + /** MAY be specified in a RECOGNITION-COMPLETE event coming from + the recognizer resource to the client */ + apt_str_t completion_reason; + /** tells the server resource the Media Type in which to store captured + audio such as the one captured and returned by the Waveform-URI header */ + apt_str_t media_type; + /** lets the client request the server to buffer the + utterance associated with this recognition request into a buffer + available to a co-resident verification resource */ + apt_bool_t ver_buffer_utterance; + /** Specifies what mode the RECOGNIZE method will operate in */ + apt_str_t recognition_mode; + /** Specifies what will happen if the client attempts to + invoke another RECOGNIZE method when this RECOGNIZE request is + already in progress for the resource*/ + apt_bool_t cancel_if_queue; + /** Specifies the maximum length of an utterance (in seconds) that will + be considered for Hotword recognition */ + apr_size_t hotword_max_duration; + /** Specifies the minimum length of an utterance (in seconds) that will + be considered for Hotword recognition */ + apr_size_t hotword_min_duration; + /** Provides a pointer to the text for which a natural language interpretation is desired */ + apt_str_t interpret_text; + /** MAY be specified in a GET-PARAMS or SET-PARAMS method and + is used to specify the size in time, in milliseconds, of the + typeahead buffer for the recognizer */ + apr_size_t dtmf_buffer_time; + /** MAY be specified in a RECOGNIZE method and is used to + tell the recognizer to clear the DTMF type-ahead buffer before + starting the recognize */ + apt_bool_t clear_dtmf_buffer; + /** MAY be specified in a RECOGNIZE method and is used to + tell the recognizer that it MUST not wait for the end of speech + before processing the collected speech to match active grammars */ + apt_bool_t early_no_match; +}; + + +/** Get recognizer header vtable */ +MRCP_DECLARE(const mrcp_header_vtable_t*) mrcp_recog_header_vtable_get(mrcp_version_e version); + +/** Get recognizer completion cause string */ +MRCP_DECLARE(const apt_str_t*) mrcp_recog_completion_cause_get(mrcp_recog_completion_cause_e completion_cause, mrcp_version_e version); + +APT_END_EXTERN_C + +#endif /*__MRCP_RECOG_HEADER_H__*/ diff --git a/libs/unimrcp/libs/mrcp/resources/include/mrcp_recog_resource.h b/libs/unimrcp/libs/mrcp/resources/include/mrcp_recog_resource.h new file mode 100644 index 0000000000..0131e9caed --- /dev/null +++ b/libs/unimrcp/libs/mrcp/resources/include/mrcp_recog_resource.h @@ -0,0 +1,55 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_RECOG_RESOURCE_H__ +#define __MRCP_RECOG_RESOURCE_H__ + +/** + * @file mrcp_recog_resource.h + * @brief MRCP Recognizer Resource + */ + +#include "mrcp_types.h" + +APT_BEGIN_EXTERN_C + +/** MRCP recognizer methods */ +typedef enum { + RECOGNIZER_SET_PARAMS, + RECOGNIZER_GET_PARAMS, + RECOGNIZER_DEFINE_GRAMMAR, + RECOGNIZER_RECOGNIZE, + RECOGNIZER_GET_RESULT, + RECOGNIZER_START_INPUT_TIMERS, + RECOGNIZER_STOP, + + RECOGNIZER_METHOD_COUNT +} mrcp_recognizer_method_id; + +/** MRCP recognizer events */ +typedef enum { + RECOGNIZER_START_OF_INPUT, + RECOGNIZER_RECOGNITION_COMPLETE, + + RECOGNIZER_EVENT_COUNT +} mrcp_recognizer_event_id; + +/** Create MRCP recognizer resource */ +MRCP_DECLARE(mrcp_resource_t*) mrcp_recog_resource_create(apr_pool_t *pool); + +APT_END_EXTERN_C + +#endif /*__MRCP_RECOG_RESOURCE_H__*/ diff --git a/libs/unimrcp/libs/mrcp/resources/include/mrcp_recog_state_machine.h b/libs/unimrcp/libs/mrcp/resources/include/mrcp_recog_state_machine.h new file mode 100644 index 0000000000..0fcbd5e5f1 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/resources/include/mrcp_recog_state_machine.h @@ -0,0 +1,37 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_RECOG_STATE_MACHINE_H__ +#define __MRCP_RECOG_STATE_MACHINE_H__ + +/** + * @file mrcp_recog_state_machine.h + * @brief MRCP Recognizer State Machine + */ + +#include "mrcp_state_machine.h" + +APT_BEGIN_EXTERN_C + +/** Create MRCP recognizer server state machine */ +mrcp_state_machine_t* mrcp_recog_server_state_machine_create(void *obj, mrcp_message_dispatcher_f dispatcher, mrcp_version_e version, apr_pool_t *pool); + +/** Create MRCP recognizer client state machine */ +mrcp_state_machine_t* mrcp_recog_client_state_machine_create(void *obj, mrcp_message_dispatcher_f dispatcher, mrcp_version_e version, apr_pool_t *pool); + +APT_END_EXTERN_C + +#endif /*__MRCP_RECOG_STATE_MACHINE_H__*/ diff --git a/libs/unimrcp/libs/mrcp/resources/include/mrcp_synth_header.h b/libs/unimrcp/libs/mrcp/resources/include/mrcp_synth_header.h new file mode 100644 index 0000000000..f0bf60c9f6 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/resources/include/mrcp_synth_header.h @@ -0,0 +1,302 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_SYNTH_HEADER_H__ +#define __MRCP_SYNTH_HEADER_H__ + +/** + * @file mrcp_synth_header.h + * @brief MRCP Synthesizer Header + */ + +#include "mrcp_types.h" +#include "mrcp_header_accessor.h" + +APT_BEGIN_EXTERN_C + +/** MRCP synthesizer headers */ +typedef enum { + SYNTHESIZER_HEADER_JUMP_SIZE, + SYNTHESIZER_HEADER_KILL_ON_BARGE_IN, + SYNTHESIZER_HEADER_SPEAKER_PROFILE, + SYNTHESIZER_HEADER_COMPLETION_CAUSE, + SYNTHESIZER_HEADER_COMPLETION_REASON, + SYNTHESIZER_HEADER_VOICE_GENDER, + SYNTHESIZER_HEADER_VOICE_AGE, + SYNTHESIZER_HEADER_VOICE_VARIANT, + SYNTHESIZER_HEADER_VOICE_NAME, + SYNTHESIZER_HEADER_PROSODY_VOLUME, + SYNTHESIZER_HEADER_PROSODY_RATE, + SYNTHESIZER_HEADER_SPEECH_MARKER, + SYNTHESIZER_HEADER_SPEECH_LANGUAGE, + SYNTHESIZER_HEADER_FETCH_HINT, + SYNTHESIZER_HEADER_AUDIO_FETCH_HINT, + SYNTHESIZER_HEADER_FAILED_URI, + SYNTHESIZER_HEADER_FAILED_URI_CAUSE, + SYNTHESIZER_HEADER_SPEAK_RESTART, + SYNTHESIZER_HEADER_SPEAK_LENGTH, + SYNTHESIZER_HEADER_LOAD_LEXICON, + SYNTHESIZER_HEADER_LEXICON_SEARCH_ORDER, + + SYNTHESIZER_HEADER_COUNT +} mrcp_synthesizer_header_id; + + +/** Speech-units */ +typedef enum { + SPEECH_UNIT_SECOND, + SPEECH_UNIT_WORD, + SPEECH_UNIT_SENTENCE, + SPEECH_UNIT_PARAGRAPH, + + SPEECH_UNIT_COUNT +} mrcp_speech_unit_e; + +/** Speech-length types */ +typedef enum { + SPEECH_LENGTH_TYPE_TEXT, + SPEECH_LENGTH_TYPE_NUMERIC_POSITIVE, + SPEECH_LENGTH_TYPE_NUMERIC_NEGATIVE, + + SPEECH_LENGTH_TYPE_UNKNOWN +} mrcp_speech_length_type_e; + +/** MRCP voice-gender */ +typedef enum { + VOICE_GENDER_MALE, + VOICE_GENDER_FEMALE, + VOICE_GENDER_NEUTRAL, + + VOICE_GENDER_COUNT, + VOICE_GENDER_UNKNOWN = VOICE_GENDER_COUNT +} mrcp_voice_gender_e; + +/** Prosody-volume type */ +typedef enum { + PROSODY_VOLUME_TYPE_LABEL, + PROSODY_VOLUME_TYPE_NUMERIC, + PROSODY_VOLUME_TYPE_RELATIVE_CHANGE, + + PROSODY_VOLUME_TYPE_UNKNOWN +} mrcp_prosody_volume_type_e; + +/** Prosody-rate type */ +typedef enum { + PROSODY_RATE_TYPE_LABEL, + PROSODY_RATE_TYPE_RELATIVE_CHANGE, + + PROSODY_RATE_TYPE_UNKNOWN +} mrcp_prosody_rate_type_e; + +/** Prosody-volume */ +typedef enum { + PROSODY_VOLUME_SILENT, + PROSODY_VOLUME_XSOFT, + PROSODY_VOLUME_SOFT, + PROSODY_VOLUME_MEDIUM, + PROSODY_VOLUME_LOUD, + PROSODY_VOLUME_XLOUD, + PROSODY_VOLUME_DEFAULT, + + PROSODY_VOLUME_COUNT, + PROSODY_VOLUME_UNKNOWN = PROSODY_VOLUME_COUNT +} mrcp_prosody_volume_label_e; + +/** Prosody-rate */ +typedef enum { + PROSODY_RATE_XSLOW, + PROSODY_RATE_SLOW, + PROSODY_RATE_MEDIUM, + PROSODY_RATE_FAST, + PROSODY_RATE_XFAST, + PROSODY_RATE_DEFAULT, + + PROSODY_RATE_COUNT, + PROSODY_RATE_UNKNOWN = PROSODY_RATE_COUNT +} mrcp_prosody_rate_label_e; + +/** Synthesizer completion-cause specified in SPEAK-COMPLETE event */ +typedef enum { + SYNTHESIZER_COMPLETION_CAUSE_NORMAL = 0, + SYNTHESIZER_COMPLETION_CAUSE_BARGE_IN = 1, + SYNTHESIZER_COMPLETION_CAUSE_PARSE_FAILURE = 2, + SYNTHESIZER_COMPLETION_CAUSE_URI_FAILURE = 3, + SYNTHESIZER_COMPLETION_CAUSE_ERROR = 4, + SYNTHESIZER_COMPLETION_CAUSE_LANGUAGE_UNSUPPORTED = 5, + SYNTHESIZER_COMPLETION_CAUSE_LEXICON_LOAD_FAILURE = 6, + SYNTHESIZER_COMPLETION_CAUSE_CANCELLED = 7, + + SYNTHESIZER_COMPLETION_CAUSE_COUNT = 8, + SYNTHESIZER_COMPLETION_CAUSE_UNKNOWN = SYNTHESIZER_COMPLETION_CAUSE_COUNT +} mrcp_synth_completion_cause_e; + + +/** Speech-length value declaration */ +typedef struct mrcp_speech_length_value_t mrcp_speech_length_value_t; +/** Numeric speech-length declaration */ +typedef struct mrcp_numeric_speech_length_t mrcp_numeric_speech_length_t; +/** Prosody-param declaration */ +typedef struct mrcp_prosody_param_t mrcp_prosody_param_t; +/** Voice-param declaration */ +typedef struct mrcp_voice_param_t mrcp_voice_param_t; +/**Prosody-rate declaration*/ +typedef struct mrcp_prosody_rate_t mrcp_prosody_rate_t; +/**Prosody-volume declaration*/ +typedef struct mrcp_prosody_volume_t mrcp_prosody_volume_t; +/** MRCP synthesizer-header declaration */ +typedef struct mrcp_synth_header_t mrcp_synth_header_t; + +/** Numeric speech-length */ +struct mrcp_numeric_speech_length_t { + /** The length */ + apr_size_t length; + /** The unit (second/word/sentence/paragraph) */ + mrcp_speech_unit_e unit; +}; + +/** Definition of speech-length value */ +struct mrcp_speech_length_value_t { + /** Speech-length type (numeric/text)*/ + mrcp_speech_length_type_e type; + /** Speech-length value (either numeric or text) */ + union { + /** Text speech-length */ + apt_str_t tag; + /** Numeric speech-length */ + mrcp_numeric_speech_length_t numeric; + } value; +}; + +/** MRCP voice-param */ +struct mrcp_voice_param_t { + /** Voice gender (male/femaile/neutral)*/ + mrcp_voice_gender_e gender; + /** Voice age */ + apr_size_t age; + /** Voice variant */ + apr_size_t variant; + /** Voice name */ + apt_str_t name; +}; + +/** MRCP prosody-volume */ +struct mrcp_prosody_volume_t { + /** prosody-volume type (one of label,numeric,relative change) */ + mrcp_prosody_volume_type_e type; + + /** prosody-volume value */ + union { + /** one of "silent", "x-soft,..." */ + mrcp_prosody_volume_label_e label; + /** numeric value */ + float numeric; + /** relative change */ + float relative; + } value; +}; + +/** MRCP prosody-rate */ +struct mrcp_prosody_rate_t { + /** prosody-rate type (one of label, relative change) */ + mrcp_prosody_rate_type_e type; + + /** prosody-rate value */ + union { + /** one of "x-slow", "slow,..." */ + mrcp_prosody_rate_label_e label; + /** relative change */ + float relative; + } value; +}; + +/** MRCP prosody-param */ +struct mrcp_prosody_param_t { + /** Prosofy volume */ + mrcp_prosody_volume_t volume; + /** Prosofy rate */ + mrcp_prosody_rate_t rate; +}; + +/** MRCP synthesizer-header */ +struct mrcp_synth_header_t { + /** MAY be specified in a CONTROL method and controls the + amount to jump forward or backward in an active "SPEAK" request */ + mrcp_speech_length_value_t jump_size; + /** MAY be sent as part of the "SPEAK" method to enable kill- + on-barge-in support */ + apt_bool_t kill_on_barge_in; + /** MAY be part of the "SET-PARAMS"/"GET-PARAMS" or "SPEAK" + request from the client to the server and specifies a URI which + references the profile of the speaker */ + apt_str_t speaker_profile; + /** MUST be specified in a "SPEAK-COMPLETE" event coming from + the synthesizer resource to the client */ + mrcp_synth_completion_cause_e completion_cause; + /** MAY be specified in a "SPEAK-COMPLETE" event coming from + the synthesizer resource to the client */ + apt_str_t completion_reason; + /** This set of headers defines the voice of the speaker */ + mrcp_voice_param_t voice_param; + /** This set of headers defines the prosody of the speech */ + mrcp_prosody_param_t prosody_param; + /** Contains timestamp information in a "timestamp" field */ + apt_str_t speech_marker; + /** specifies the default language of the speech data if the + language is not specified in the markup */ + apt_str_t speech_language; + /** When the synthesizer needs to fetch documents or other resources like + speech markup or audio files, this header controls the corresponding + URI access properties */ + apt_str_t fetch_hint; + /** When the synthesizer needs to fetch documents or other resources like + speech audio files, this header controls the corresponding URI access + properties */ + apt_str_t audio_fetch_hint; + /** When a synthesizer method needs a synthesizer to fetch or access a + URI and the access fails, the server SHOULD provide the failed URI in + this header in the method response */ + apt_str_t failed_uri; + /** When a synthesizer method needs a synthesizer to fetch or access a + URI and the access fails the server MUST provide the URI specific or + protocol specific response code for the URI in the Failed-URI header + in the method response through this header */ + apt_str_t failed_uri_cause; + /** When a CONTROL request to jump backward is issued to a currently + speaking synthesizer resource, and the target jump point is before + the start of the current "SPEAK" request, the current "SPEAK" request + MUST restart */ + apt_bool_t speak_restart; + /** MAY be specified in a CONTROL method to control the + length of speech to speak, relative to the current speaking point in + the currently active "SPEAK" request */ + mrcp_speech_length_value_t speak_length; + /** Used to indicate whether a lexicon has to be loaded or unloaded */ + apt_bool_t load_lexicon; + /** used to specify a list of active Lexicon URIs and the + search order among the active lexicons */ + apt_str_t lexicon_search_order; +}; + +/** Get synthesizer header vtable */ +MRCP_DECLARE(const mrcp_header_vtable_t*) mrcp_synth_header_vtable_get(mrcp_version_e version); + +/** Get synthesizer completion cause string */ +MRCP_DECLARE(const apt_str_t*) mrcp_synth_completion_cause_get(mrcp_synth_completion_cause_e completion_cause, mrcp_version_e version); + + +APT_END_EXTERN_C + +#endif /*__MRCP_SYNTH_HEADER_H__*/ diff --git a/libs/unimrcp/libs/mrcp/resources/include/mrcp_synth_resource.h b/libs/unimrcp/libs/mrcp/resources/include/mrcp_synth_resource.h new file mode 100644 index 0000000000..d63fbdbfe3 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/resources/include/mrcp_synth_resource.h @@ -0,0 +1,58 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_SYNTH_RESOURCE_H__ +#define __MRCP_SYNTH_RESOURCE_H__ + +/** + * @file mrcp_synth_resource.h + * @brief MRCP Synthesizer Resource + */ + +#include "mrcp_types.h" + +APT_BEGIN_EXTERN_C + +/** MRCP synthesizer methods */ +typedef enum { + SYNTHESIZER_SET_PARAMS, + SYNTHESIZER_GET_PARAMS, + SYNTHESIZER_SPEAK, + SYNTHESIZER_STOP, + SYNTHESIZER_PAUSE, + SYNTHESIZER_RESUME, + SYNTHESIZER_BARGE_IN_OCCURRED, + SYNTHESIZER_CONTROL, + SYNTHESIZER_DEFINE_LEXICON, + + SYNTHESIZER_METHOD_COUNT +} mrcp_synthesizer_method_id; + +/** MRCP synthesizer events */ +typedef enum { + SYNTHESIZER_SPEECH_MARKER, + SYNTHESIZER_SPEAK_COMPLETE, + + SYNTHESIZER_EVENT_COUNT +} mrcp_synthesizer_event_id; + + +/** Create MRCP synthesizer resource */ +MRCP_DECLARE(mrcp_resource_t*) mrcp_synth_resource_create(apr_pool_t *pool); + +APT_END_EXTERN_C + +#endif /*__MRCP_SYNTH_RESOURCE_H__*/ diff --git a/libs/unimrcp/libs/mrcp/resources/include/mrcp_synth_state_machine.h b/libs/unimrcp/libs/mrcp/resources/include/mrcp_synth_state_machine.h new file mode 100644 index 0000000000..524fd29271 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/resources/include/mrcp_synth_state_machine.h @@ -0,0 +1,37 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_SYNTH_STATE_MACHINE_H__ +#define __MRCP_SYNTH_STATE_MACHINE_H__ + +/** + * @file mrcp_synth_state_machine.h + * @brief MRCP Synthesizer State Machine + */ + +#include "mrcp_state_machine.h" + +APT_BEGIN_EXTERN_C + +/** Create MRCP synthesizer server state machine */ +mrcp_state_machine_t* mrcp_synth_server_state_machine_create(void *obj, mrcp_message_dispatcher_f dispatcher, mrcp_version_e version, apr_pool_t *pool); + +/** Create MRCP synthesizer client state machine */ +mrcp_state_machine_t* mrcp_synth_client_state_machine_create(void *obj, mrcp_message_dispatcher_f dispatcher, mrcp_version_e version, apr_pool_t *pool); + +APT_END_EXTERN_C + +#endif /*__MRCP_SYNTH_STATE_MACHINE_H__*/ diff --git a/libs/unimrcp/libs/mrcp/resources/src/mrcp_default_factory.c b/libs/unimrcp/libs/mrcp/resources/src/mrcp_default_factory.c new file mode 100644 index 0000000000..d34ba99c13 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/resources/src/mrcp_default_factory.c @@ -0,0 +1,57 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mrcp_default_factory.h" +#include "mrcp_synth_resource.h" +#include "mrcp_recog_resource.h" +#include "apt_log.h" + +/** String table of MRCPv2 resources (mrcp_resource_types_e) */ +static const apt_str_table_item_t mrcp_resource_string_table[] = { + {{"speechsynth",11},6}, + {{"speechrecog",11},6} +}; + +/** Create default MRCP resource factory */ +MRCP_DECLARE(mrcp_resource_factory_t*) mrcp_default_factory_create(apr_pool_t *pool) +{ + mrcp_resource_t *resource; + mrcp_resource_factory_t *resource_factory; + /* create resource factory instance */ + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create MRCP Resource Factory [%d]",MRCP_RESOURCE_TYPE_COUNT); + resource_factory = mrcp_resource_factory_create(MRCP_RESOURCE_TYPE_COUNT,pool); + if(!resource_factory) { + return NULL; + } + + /* set resource string table */ + mrcp_resource_string_table_set(resource_factory,mrcp_resource_string_table); + + /* create and register resources */ + resource = mrcp_synth_resource_create(pool); + if(resource) { + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Register Synthesizer Resource"); + mrcp_resource_register(resource_factory,resource,MRCP_SYNTHESIZER_RESOURCE); + } + + resource = mrcp_recog_resource_create(pool); + if(resource) { + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Register Recognizer Resource"); + mrcp_resource_register(resource_factory,resource,MRCP_RECOGNIZER_RESOURCE); + } + + return resource_factory; +} diff --git a/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_client_state_machine.c b/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_client_state_machine.c new file mode 100644 index 0000000000..3e313d7202 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_client_state_machine.c @@ -0,0 +1,36 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mrcp_state_machine.h" +#include "mrcp_recog_state_machine.h" +#include "mrcp_message.h" + + +/** Update state according to request received from user level or response/event received from MRCP server */ +static apt_bool_t recog_state_update(mrcp_state_machine_t *state_machine, mrcp_message_t *message) +{ + /* no actual state machine processing yet, dispatch whatever received */ + return state_machine->dispatcher(state_machine,message); +} + +/** Create MRCP recognizer client state machine */ +mrcp_state_machine_t* mrcp_recog_client_state_machine_create(void *obj, mrcp_message_dispatcher_f dispatcher, mrcp_version_e version, apr_pool_t *pool) +{ + mrcp_state_machine_t *state_machine = apr_palloc(pool,sizeof(mrcp_state_machine_t)); + mrcp_state_machine_init(state_machine,obj,dispatcher); + state_machine->update = recog_state_update; + return state_machine; +} diff --git a/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_header.c b/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_header.c new file mode 100644 index 0000000000..70691a5a31 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_header.c @@ -0,0 +1,656 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mrcp_recog_header.h" + +/** String table of MRCPv1 recognizer headers (mrcp_recog_header_id) */ +static const apt_str_table_item_t v1_recog_header_string_table[] = { + {{"Confidence-Threshold", 20},7}, + {{"Sensitivity-Level", 17},3}, + {{"Speed-Vs-Accuracy", 17},4}, + {{"N-Best-List-Length", 18},1}, + {{"No-Input-Timeout", 16},2}, + {{"Recognition-Timeout", 19},16}, + {{"Waveform-Url", 12},0}, + {{"Completion-Cause", 16},16}, + {{"Recognizer-Context-Block", 24},12}, + {{"Recognizer-Start-Timers", 23},11}, + {{"Speech-Complete-Timeout", 23},7}, + {{"Speech-Incomplete-Timeout", 25},8}, + {{"DTMF-Interdigit-Timeout", 23},5}, + {{"DTMF-Term-Timeout", 17},14}, + {{"DTMF-Term-Char", 14},14}, + {{"Failed-Uri", 10},10}, + {{"Failed-Uri-Cause", 16},16}, + {{"Save-Waveform", 13},5}, + {{"New-Audio-Channel", 17},2}, + {{"Speech-Language", 15},8}, + {{"Input-Type", 10},10}, + {{"Input-Waveform-Uri", 18},6}, + {{"Completion-Reason", 17},15}, + {{"Media-Type", 10},0}, + {{"Ver-Buffer-Utterance", 20},0}, + {{"Recognition-Mode", 16},14}, + {{"Cancel-If-Queue", 15},3}, + {{"Hotword-Max-Duration", 20},10}, + {{"Hotword-Min-Duration", 20},20}, + {{"Interpret-Text", 14},7}, + {{"DTMF-Buffer-Time", 16},5}, + {{"Clear-DTMF-Buffer", 17},1}, + {{"Early-No-Match", 14},0} +}; + +/** String table of MRCPv2 recognizer headers (mrcp_recog_header_id) */ +static const apt_str_table_item_t v2_recog_header_string_table[] = { + {{"Confidence-Threshold", 20},8}, + {{"Sensitivity-Level", 17},3}, + {{"Speed-Vs-Accuracy", 17},4}, + {{"N-Best-List-Length", 18},1}, + {{"No-Input-Timeout", 16},2}, + {{"Recognition-Timeout", 19},16}, + {{"Waveform-Uri", 12},0}, + {{"Completion-Cause", 16},16}, + {{"Recognizer-Context-Block", 24},7}, + {{"Start-Input-Timers", 18},2}, + {{"Speech-Complete-Timeout", 23},7}, + {{"Speech-Incomplete-Timeout", 25},8}, + {{"DTMF-Interdigit-Timeout", 23},5}, + {{"DTMF-Term-Timeout", 17},14}, + {{"DTMF-Term-Char", 14},14}, + {{"Failed-Uri", 10},10}, + {{"Failed-Uri-Cause", 16},16}, + {{"Save-Waveform", 13},5}, + {{"New-Audio-Channel", 17},2}, + {{"Speech-Language", 15},8}, + {{"Input-Type", 10},10}, + {{"Input-Waveform-Uri", 18},6}, + {{"Completion-Reason", 17},13}, + {{"Media-Type", 10},0}, + {{"Ver-Buffer-Utterance", 20},0}, + {{"Recognition-Mode", 16},14}, + {{"Cancel-If-Queue", 15},3}, + {{"Hotword-Max-Duration", 20},10}, + {{"Hotword-Min-Duration", 20},20}, + {{"Interpret-Text", 14},7}, + {{"DTMF-Buffer-Time", 16},5}, + {{"Clear-DTMF-Buffer", 17},1}, + {{"Early-No-Match", 14},0} +}; + +/** String table of MRCPv1 recognizer completion-cause fields (mrcp_recog_completion_cause_e) */ +static const apt_str_table_item_t v1_completion_cause_string_table[] = { + {{"success", 7},1}, + {{"no-match", 8},8}, + {{"no-input-timeout", 16},3}, + {{"recognition-timeout", 19},0}, + {{"gram-load-failure", 17},7}, + {{"gram-comp-failure", 17},5}, + {{"error", 5},0}, + {{"speech-too-early", 16},1}, + {{"too-much-speech-timeout", 23},0}, + {{"uri-failure", 11},0}, + {{"language-unsupported", 20},0}, + {{"cancelled", 9},0}, + {{"semantics-failure", 17},2}, + {{"partial-match", 13},13}, + {{"partial-match-maxtime", 21},13}, + {{"no-match-maxtime", 16},9}, + {{"gram-definition-failure", 23},5} +}; + + +/** String table of MRCPv2 recognizer completion-cause fields (mrcp_recog_completion_cause_e) */ +static const apt_str_table_item_t v2_completion_cause_string_table[] = { + {{"success", 7},7}, + {{"no-match", 8},4}, + {{"no-input-timeout", 16},3}, + {{"hotword-maxtime", 15},0}, + {{"grammar-load-failure", 20},8}, + {{"grammar-compilation-failure",27},8}, + {{"recognizer-error", 16},0}, + {{"speech-too-early", 16},1}, + {{"success-maxtime", 15},15}, + {{"uri-failure", 11},0}, + {{"language-unsupported", 20},0}, + {{"cancelled", 9},0}, + {{"semantics-failure", 17},2}, + {{"partial-match", 13},13}, + {{"partial-match-maxtime", 21},13}, + {{"no-match-maxtime", 16},9}, + {{"grammar-definition-failure", 26},9} +}; + +/** Generate MRCP recognizer completion-cause */ +static apt_bool_t mrcp_completion_cause_generate(mrcp_recog_completion_cause_e completion_cause, const apt_str_t *name, apt_text_stream_t *stream) +{ + int length = sprintf(stream->pos,"%03"APR_SIZE_T_FMT" ",completion_cause); + if(length <= 0) { + return FALSE; + } + stream->pos += length; + + if(name) { + memcpy(stream->pos,name->buf,name->length); + stream->pos += name->length; + } + return TRUE; +} + + +/** Initialize recognizer header */ +static void mrcp_recog_header_init(mrcp_recog_header_t *recog_header) +{ + recog_header->confidence_threshold = 0.0; + recog_header->sensitivity_level = 0.0; + recog_header->speed_vs_accuracy = 0.0; + recog_header->n_best_list_length = 0; + recog_header->no_input_timeout = 0; + recog_header->recognition_timeout = 0; + apt_string_reset(&recog_header->waveform_uri); + recog_header->completion_cause = RECOGNIZER_COMPLETION_CAUSE_COUNT; + apt_string_reset(&recog_header->recognizer_context_block); + recog_header->start_input_timers = FALSE; + recog_header->speech_complete_timeout = 0; + recog_header->speech_incomplete_timeout = 0; + recog_header->dtmf_interdigit_timeout = 0; + recog_header->dtmf_term_timeout = 0; + recog_header->dtmf_term_char = 0; + apt_string_reset(&recog_header->failed_uri); + apt_string_reset(&recog_header->failed_uri_cause); + recog_header->save_waveform = FALSE; + recog_header->new_audio_channel = FALSE; + apt_string_reset(&recog_header->speech_language); + /* initializes additionnal MRCPV2 recog headers */ + apt_string_reset(&recog_header->input_type); + apt_string_reset(&recog_header->input_waveform_uri); + apt_string_reset(&recog_header->completion_reason); + apt_string_reset(&recog_header->media_type); + recog_header->ver_buffer_utterance = FALSE; + apt_string_reset(&recog_header->recognition_mode); + recog_header->cancel_if_queue = FALSE; + recog_header->hotword_max_duration = 0; + recog_header->hotword_min_duration = 0; + apt_string_reset(&recog_header->interpret_text); + recog_header->dtmf_buffer_time = 0; + recog_header->clear_dtmf_buffer = FALSE; + recog_header->early_no_match = FALSE; +} + +/** Allocate MRCP recognizer header */ +static void* mrcp_recog_header_allocate(mrcp_header_accessor_t *accessor, apr_pool_t *pool) +{ + mrcp_recog_header_t *recog_header = apr_palloc(pool,sizeof(mrcp_recog_header_t)); + mrcp_recog_header_init(recog_header); + accessor->data = recog_header; + return accessor->data; +} + +/** Parse MRCP recognizer header */ +static apt_bool_t mrcp_recog_header_parse(mrcp_recog_header_t *recog_header, apr_size_t id, const apt_str_t *value, apr_pool_t *pool) +{ + apt_bool_t status = TRUE; + switch(id) { + case RECOGNIZER_HEADER_N_BEST_LIST_LENGTH: + recog_header->n_best_list_length = apt_size_value_parse(value); + break; + case RECOGNIZER_HEADER_NO_INPUT_TIMEOUT: + recog_header->no_input_timeout = apt_size_value_parse(value); + break; + case RECOGNIZER_HEADER_RECOGNITION_TIMEOUT: + recog_header->recognition_timeout = apt_size_value_parse(value); + break; + case RECOGNIZER_HEADER_WAVEFORM_URI: + apt_string_copy(&recog_header->waveform_uri,value,pool); + break; + case RECOGNIZER_HEADER_COMPLETION_CAUSE: + recog_header->completion_cause = apt_size_value_parse(value); + break; + case RECOGNIZER_HEADER_RECOGNIZER_CONTEXT_BLOCK: + apt_string_copy(&recog_header->recognizer_context_block,value,pool); + break; + case RECOGNIZER_HEADER_START_INPUT_TIMERS: + apt_boolean_value_parse(value,&recog_header->start_input_timers); + break; + case RECOGNIZER_HEADER_SPEECH_COMPLETE_TIMEOUT: + recog_header->speech_complete_timeout = apt_size_value_parse(value); + break; + case RECOGNIZER_HEADER_SPEECH_INCOMPLETE_TIMEOUT: + recog_header->speech_incomplete_timeout = apt_size_value_parse(value); + break; + case RECOGNIZER_HEADER_DTMF_INTERDIGIT_TIMEOUT: + recog_header->dtmf_interdigit_timeout = apt_size_value_parse(value); + break; + case RECOGNIZER_HEADER_DTMF_TERM_TIMEOUT: + recog_header->dtmf_term_timeout = apt_size_value_parse(value); + break; + case RECOGNIZER_HEADER_DTMF_TERM_CHAR: + recog_header->dtmf_term_char = *value->buf; + break; + case RECOGNIZER_HEADER_FAILED_URI: + apt_string_copy(&recog_header->failed_uri,value,pool); + break; + case RECOGNIZER_HEADER_FAILED_URI_CAUSE: + apt_string_copy(&recog_header->failed_uri_cause,value,pool); + break; + case RECOGNIZER_HEADER_SAVE_WAVEFORM: + apt_boolean_value_parse(value,&recog_header->save_waveform); + break; + case RECOGNIZER_HEADER_NEW_AUDIO_CHANNEL: + apt_boolean_value_parse(value,&recog_header->new_audio_channel); + break; + case RECOGNIZER_HEADER_SPEECH_LANGUAGE: + apt_string_copy(&recog_header->speech_language,value,pool); + break; + case RECOGNIZER_HEADER_INPUT_TYPE: + apt_string_copy(&recog_header->input_type,value,pool); + break; + case RECOGNIZER_HEADER_MEDIA_TYPE: + apt_string_copy(&recog_header->media_type,value,pool); + break; + case RECOGNIZER_HEADER_INPUT_WAVEFORM_URI: + apt_string_copy(&recog_header->input_waveform_uri,value,pool); + break; + case RECOGNIZER_HEADER_COMPLETION_REASON: + apt_string_copy(&recog_header->completion_reason,value,pool); + break; + case RECOGNIZER_HEADER_VER_BUFFER_UTTERANCE: + apt_boolean_value_parse(value,&recog_header->ver_buffer_utterance); + break; + case RECOGNIZER_HEADER_RECOGNITION_MODE: + apt_string_copy(&recog_header->recognition_mode,value,pool); + break; + case RECOGNIZER_HEADER_CANCEL_IF_QUEUE: + apt_boolean_value_parse(value,&recog_header->cancel_if_queue); + break; + case RECOGNIZER_HEADER_HOTWORD_MAX_DURATION: + recog_header->hotword_max_duration = apt_size_value_parse(value); + break; + case RECOGNIZER_HEADER_HOTWORD_MIN_DURATION: + recog_header->hotword_min_duration = apt_size_value_parse(value); + break; + case RECOGNIZER_HEADER_INTERPRET_TEXT: + apt_string_copy(&recog_header->interpret_text,value,pool); + break; + case RECOGNIZER_HEADER_DTMF_BUFFER_TIME: + recog_header->dtmf_buffer_time = apt_size_value_parse(value); + break; + case RECOGNIZER_HEADER_CLEAR_DTMF_BUFFER: + apt_boolean_value_parse(value,&recog_header->clear_dtmf_buffer); + break; + case RECOGNIZER_HEADER_EARLY_NO_MATCH: + apt_boolean_value_parse(value,&recog_header->early_no_match); + break; + default: + status = FALSE; + } + return status; +} + +static APR_INLINE float apt_size_value_parse_as_float(const apt_str_t *value) +{ + float f = (float)apt_size_value_parse(value); + return f / 100; +} + +static APR_INLINE apt_bool_t apt_size_value_generate_from_float(float value, apt_text_stream_t *stream) +{ + apr_size_t s = (apr_size_t)((value + 0.001f) * 100); + return apt_size_value_generate(s,stream); +} + +/** Parse MRCPv1 recognizer header */ +static apt_bool_t mrcp_v1_recog_header_parse(mrcp_header_accessor_t *accessor, apr_size_t id, const apt_str_t *value, apr_pool_t *pool) +{ + mrcp_recog_header_t *recog_header = accessor->data; + if(id == RECOGNIZER_HEADER_CONFIDENCE_THRESHOLD) { + recog_header->confidence_threshold = apt_size_value_parse_as_float(value); + return TRUE; + } + else if(id == RECOGNIZER_HEADER_SENSITIVITY_LEVEL) { + recog_header->sensitivity_level = apt_size_value_parse_as_float(value); + return TRUE; + } + else if(id == RECOGNIZER_HEADER_SPEED_VS_ACCURACY) { + recog_header->speed_vs_accuracy = apt_size_value_parse_as_float(value); + return TRUE; + } + return mrcp_recog_header_parse(recog_header,id,value,pool); +} + +/** Parse MRCPv2 recognizer header */ +static apt_bool_t mrcp_v2_recog_header_parse(mrcp_header_accessor_t *accessor, apr_size_t id, const apt_str_t *value, apr_pool_t *pool) +{ + mrcp_recog_header_t *recog_header = accessor->data; + if(id == RECOGNIZER_HEADER_CONFIDENCE_THRESHOLD) { + recog_header->confidence_threshold = apt_float_value_parse(value); + return TRUE; + } + else if(id == RECOGNIZER_HEADER_SENSITIVITY_LEVEL) { + recog_header->sensitivity_level = apt_float_value_parse(value); + return TRUE; + } + else if(id == RECOGNIZER_HEADER_SPEED_VS_ACCURACY) { + recog_header->speed_vs_accuracy = apt_float_value_parse(value); + return TRUE; + } + return mrcp_recog_header_parse(recog_header,id,value,pool); +} + +/** Generate MRCP recognizer header */ +static apt_bool_t mrcp_recog_header_generate(mrcp_recog_header_t *recog_header, apr_size_t id, apt_text_stream_t *value) +{ + switch(id) { + case RECOGNIZER_HEADER_N_BEST_LIST_LENGTH: + apt_size_value_generate(recog_header->n_best_list_length,value); + break; + case RECOGNIZER_HEADER_NO_INPUT_TIMEOUT: + apt_size_value_generate(recog_header->no_input_timeout,value); + break; + case RECOGNIZER_HEADER_RECOGNITION_TIMEOUT: + apt_size_value_generate(recog_header->recognition_timeout,value); + break; + case RECOGNIZER_HEADER_WAVEFORM_URI: + apt_string_value_generate(&recog_header->waveform_uri,value); + break; + case RECOGNIZER_HEADER_RECOGNIZER_CONTEXT_BLOCK: + apt_string_value_generate(&recog_header->recognizer_context_block,value); + break; + case RECOGNIZER_HEADER_START_INPUT_TIMERS: + apt_boolean_value_generate(recog_header->start_input_timers,value); + break; + case RECOGNIZER_HEADER_SPEECH_COMPLETE_TIMEOUT: + apt_size_value_generate(recog_header->speech_complete_timeout,value); + break; + case RECOGNIZER_HEADER_SPEECH_INCOMPLETE_TIMEOUT: + apt_size_value_generate(recog_header->speech_incomplete_timeout,value); + break; + case RECOGNIZER_HEADER_DTMF_INTERDIGIT_TIMEOUT: + apt_size_value_generate(recog_header->dtmf_interdigit_timeout,value); + break; + case RECOGNIZER_HEADER_DTMF_TERM_TIMEOUT: + apt_size_value_generate(recog_header->dtmf_term_timeout,value); + break; + case RECOGNIZER_HEADER_DTMF_TERM_CHAR: + *value->pos++ = recog_header->dtmf_term_char; + break; + case RECOGNIZER_HEADER_FAILED_URI: + apt_string_value_generate(&recog_header->failed_uri,value); + break; + case RECOGNIZER_HEADER_FAILED_URI_CAUSE: + apt_string_value_generate(&recog_header->failed_uri_cause,value); + break; + case RECOGNIZER_HEADER_SAVE_WAVEFORM: + apt_boolean_value_generate(recog_header->save_waveform,value); + break; + case RECOGNIZER_HEADER_NEW_AUDIO_CHANNEL: + apt_boolean_value_generate(recog_header->new_audio_channel,value); + break; + case RECOGNIZER_HEADER_SPEECH_LANGUAGE: + apt_string_value_generate(&recog_header->speech_language,value); + break; + case RECOGNIZER_HEADER_INPUT_TYPE: + apt_string_value_generate(&recog_header->input_type,value); + break; + case RECOGNIZER_HEADER_INPUT_WAVEFORM_URI: + apt_string_value_generate(&recog_header->input_waveform_uri,value); + break; + case RECOGNIZER_HEADER_COMPLETION_REASON: + apt_string_value_generate(&recog_header->completion_reason,value); + break; + case RECOGNIZER_HEADER_MEDIA_TYPE: + apt_string_value_generate(&recog_header->media_type,value); + break; + case RECOGNIZER_HEADER_VER_BUFFER_UTTERANCE: + apt_boolean_value_generate(recog_header->ver_buffer_utterance,value); + break; + case RECOGNIZER_HEADER_RECOGNITION_MODE: + apt_string_value_generate(&recog_header->recognition_mode,value); + break; + case RECOGNIZER_HEADER_CANCEL_IF_QUEUE: + apt_boolean_value_generate(recog_header->cancel_if_queue,value); + break; + case RECOGNIZER_HEADER_HOTWORD_MAX_DURATION: + apt_size_value_generate(recog_header->hotword_max_duration,value); + break; + case RECOGNIZER_HEADER_HOTWORD_MIN_DURATION: + apt_size_value_generate(recog_header->hotword_min_duration,value); + break; + case RECOGNIZER_HEADER_INTERPRET_TEXT: + apt_string_value_generate(&recog_header->interpret_text,value); + break; + case RECOGNIZER_HEADER_DTMF_BUFFER_TIME: + apt_size_value_generate(recog_header->dtmf_buffer_time,value); + break; + case RECOGNIZER_HEADER_CLEAR_DTMF_BUFFER: + apt_boolean_value_generate(recog_header->clear_dtmf_buffer,value); + break; + case RECOGNIZER_HEADER_EARLY_NO_MATCH: + apt_boolean_value_generate(recog_header->early_no_match,value); + break; + default: + break; + } + return TRUE; +} + +/** Generate MRCPv1 recognizer header */ +static apt_bool_t mrcp_v1_recog_header_generate(mrcp_header_accessor_t *accessor, apr_size_t id, apt_text_stream_t *value) +{ + mrcp_recog_header_t *recog_header = accessor->data; + if(id == RECOGNIZER_HEADER_CONFIDENCE_THRESHOLD) { + return apt_size_value_generate_from_float(recog_header->confidence_threshold,value); + } + else if(id == RECOGNIZER_HEADER_SENSITIVITY_LEVEL) { + return apt_size_value_generate_from_float(recog_header->sensitivity_level,value); + } + else if(id == RECOGNIZER_HEADER_SPEED_VS_ACCURACY) { + return apt_size_value_generate_from_float(recog_header->speed_vs_accuracy,value); + } + else if(id == RECOGNIZER_HEADER_COMPLETION_CAUSE) { + const apt_str_t *name = apt_string_table_str_get( + v1_completion_cause_string_table, + RECOGNIZER_COMPLETION_CAUSE_COUNT, + recog_header->completion_cause); + return mrcp_completion_cause_generate(recog_header->completion_cause,name,value); + } + return mrcp_recog_header_generate(recog_header,id,value); +} + +/** Generate MRCPv2 recognizer header */ +static apt_bool_t mrcp_v2_recog_header_generate(mrcp_header_accessor_t *accessor, apr_size_t id, apt_text_stream_t *value) +{ + mrcp_recog_header_t *recog_header = accessor->data; + if(id == RECOGNIZER_HEADER_CONFIDENCE_THRESHOLD) { + return apt_float_value_generate(recog_header->confidence_threshold,value); + } + else if(id == RECOGNIZER_HEADER_SENSITIVITY_LEVEL) { + return apt_float_value_generate(recog_header->sensitivity_level,value); + } + else if(id == RECOGNIZER_HEADER_SPEED_VS_ACCURACY) { + return apt_float_value_generate(recog_header->speed_vs_accuracy,value); + } + else if(id == RECOGNIZER_HEADER_COMPLETION_CAUSE) { + const apt_str_t *name = apt_string_table_str_get( + v2_completion_cause_string_table, + RECOGNIZER_COMPLETION_CAUSE_COUNT, + recog_header->completion_cause); + return mrcp_completion_cause_generate(recog_header->completion_cause,name,value); + } + return mrcp_recog_header_generate(recog_header,id,value); +} + +/** Duplicate MRCP recognizer header */ +static apt_bool_t mrcp_recog_header_duplicate(mrcp_header_accessor_t *accessor, const mrcp_header_accessor_t *src, apr_size_t id, apr_pool_t *pool) +{ + mrcp_recog_header_t *recog_header = accessor->data; + const mrcp_recog_header_t *src_recog_header = src->data; + apt_bool_t status = TRUE; + + if(!recog_header || !src_recog_header) { + return FALSE; + } + + switch(id) { + case RECOGNIZER_HEADER_CONFIDENCE_THRESHOLD: + recog_header->confidence_threshold = src_recog_header->confidence_threshold; + break; + case RECOGNIZER_HEADER_SENSITIVITY_LEVEL: + recog_header->sensitivity_level = src_recog_header->sensitivity_level; + break; + case RECOGNIZER_HEADER_SPEED_VS_ACCURACY: + recog_header->speed_vs_accuracy = src_recog_header->speed_vs_accuracy; + break; + case RECOGNIZER_HEADER_N_BEST_LIST_LENGTH: + recog_header->n_best_list_length = src_recog_header->n_best_list_length; + break; + case RECOGNIZER_HEADER_NO_INPUT_TIMEOUT: + recog_header->no_input_timeout = src_recog_header->no_input_timeout; + break; + case RECOGNIZER_HEADER_RECOGNITION_TIMEOUT: + recog_header->recognition_timeout = src_recog_header->recognition_timeout; + break; + case RECOGNIZER_HEADER_WAVEFORM_URI: + apt_string_copy(&recog_header->waveform_uri,&src_recog_header->waveform_uri,pool); + break; + case RECOGNIZER_HEADER_COMPLETION_CAUSE: + recog_header->completion_cause = src_recog_header->completion_cause; + break; + case RECOGNIZER_HEADER_RECOGNIZER_CONTEXT_BLOCK: + apt_string_copy(&recog_header->recognizer_context_block,&src_recog_header->recognizer_context_block,pool); + break; + case RECOGNIZER_HEADER_START_INPUT_TIMERS: + recog_header->start_input_timers = src_recog_header->start_input_timers; + break; + case RECOGNIZER_HEADER_SPEECH_COMPLETE_TIMEOUT: + recog_header->speech_complete_timeout = src_recog_header->speech_complete_timeout; + break; + case RECOGNIZER_HEADER_SPEECH_INCOMPLETE_TIMEOUT: + recog_header->speech_incomplete_timeout = src_recog_header->speech_incomplete_timeout; + break; + case RECOGNIZER_HEADER_DTMF_INTERDIGIT_TIMEOUT: + recog_header->dtmf_interdigit_timeout = src_recog_header->dtmf_interdigit_timeout; + break; + case RECOGNIZER_HEADER_DTMF_TERM_TIMEOUT: + recog_header->dtmf_term_timeout = src_recog_header->dtmf_term_timeout; + break; + case RECOGNIZER_HEADER_DTMF_TERM_CHAR: + recog_header->dtmf_term_char = src_recog_header->dtmf_term_char; + break; + case RECOGNIZER_HEADER_FAILED_URI: + apt_string_copy(&recog_header->failed_uri,&src_recog_header->failed_uri,pool); + break; + case RECOGNIZER_HEADER_FAILED_URI_CAUSE: + apt_string_copy(&recog_header->failed_uri_cause,&src_recog_header->failed_uri_cause,pool); + break; + case RECOGNIZER_HEADER_SAVE_WAVEFORM: + recog_header->save_waveform = src_recog_header->save_waveform; + break; + case RECOGNIZER_HEADER_NEW_AUDIO_CHANNEL: + recog_header->new_audio_channel = src_recog_header->new_audio_channel; + break; + case RECOGNIZER_HEADER_SPEECH_LANGUAGE: + apt_string_copy(&recog_header->speech_language,&src_recog_header->speech_language,pool); + break; + case RECOGNIZER_HEADER_INPUT_TYPE: + apt_string_copy(&recog_header->input_type,&src_recog_header->input_type,pool); + break; + case RECOGNIZER_HEADER_INPUT_WAVEFORM_URI: + apt_string_copy(&recog_header->input_waveform_uri,&src_recog_header->input_waveform_uri,pool); + break; + case RECOGNIZER_HEADER_COMPLETION_REASON: + apt_string_copy(&recog_header->completion_reason,&src_recog_header->completion_reason,pool); + break; + case RECOGNIZER_HEADER_MEDIA_TYPE: + apt_string_copy(&recog_header->media_type,&src_recog_header->media_type,pool); + break; + case RECOGNIZER_HEADER_VER_BUFFER_UTTERANCE: + recog_header->ver_buffer_utterance = src_recog_header->ver_buffer_utterance; + break; + case RECOGNIZER_HEADER_RECOGNITION_MODE: + apt_string_copy(&recog_header->recognition_mode,&src_recog_header->recognition_mode,pool); + break; + case RECOGNIZER_HEADER_CANCEL_IF_QUEUE: + recog_header->cancel_if_queue = src_recog_header->cancel_if_queue; + break; + case RECOGNIZER_HEADER_HOTWORD_MAX_DURATION: + recog_header->hotword_max_duration = src_recog_header->hotword_max_duration; + break; + case RECOGNIZER_HEADER_HOTWORD_MIN_DURATION: + recog_header->hotword_min_duration = src_recog_header->hotword_min_duration; + break; + case RECOGNIZER_HEADER_INTERPRET_TEXT: + apt_string_copy(&recog_header->interpret_text,&src_recog_header->interpret_text,pool); + break; + case RECOGNIZER_HEADER_DTMF_BUFFER_TIME: + recog_header->dtmf_buffer_time = src_recog_header->dtmf_buffer_time; + break; + case RECOGNIZER_HEADER_CLEAR_DTMF_BUFFER: + recog_header->clear_dtmf_buffer = src_recog_header->clear_dtmf_buffer; + break; + case RECOGNIZER_HEADER_EARLY_NO_MATCH: + recog_header->early_no_match = src_recog_header->early_no_match; + break; + default: + status = FALSE; + } + return status; +} + +static APR_INLINE const apt_str_table_item_t* recog_header_string_table_get(mrcp_version_e version) +{ + if(version == MRCP_VERSION_1) { + return v1_recog_header_string_table; + } + return v2_recog_header_string_table; +} + +static const mrcp_header_vtable_t v1_vtable = { + mrcp_recog_header_allocate, + NULL, /* nothing to destroy */ + mrcp_v1_recog_header_parse, + mrcp_v1_recog_header_generate, + mrcp_recog_header_duplicate, + v1_recog_header_string_table, + RECOGNIZER_HEADER_COUNT +}; + +static const mrcp_header_vtable_t v2_vtable = { + mrcp_recog_header_allocate, + NULL, /* nothing to destroy */ + mrcp_v2_recog_header_parse, + mrcp_v2_recog_header_generate, + mrcp_recog_header_duplicate, + v2_recog_header_string_table, + RECOGNIZER_HEADER_COUNT +}; + +MRCP_DECLARE(const mrcp_header_vtable_t*) mrcp_recog_header_vtable_get(mrcp_version_e version) +{ + if(version == MRCP_VERSION_1) { + return &v1_vtable; + } + return &v2_vtable; +} + +MRCP_DECLARE(const apt_str_t*) mrcp_recog_completion_cause_get(mrcp_recog_completion_cause_e completion_cause, mrcp_version_e version) +{ + const apt_str_table_item_t *table = v2_completion_cause_string_table; + if(version == MRCP_VERSION_1) { + table = v1_completion_cause_string_table; + } + + return apt_string_table_str_get(table,RECOGNIZER_COMPLETION_CAUSE_COUNT,completion_cause); +} diff --git a/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_resource.c b/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_resource.c new file mode 100644 index 0000000000..819b8b09a6 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_resource.c @@ -0,0 +1,143 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mrcp_recog_resource.h" +#include "mrcp_recog_header.h" +#include "mrcp_recog_state_machine.h" +#include "mrcp_resource.h" +#include "mrcp_message.h" + +/** String table of MRCP recognizer methods (mrcp_recognizer_method_id) */ +static const apt_str_table_item_t v1_recog_method_string_table[] = { + {{"SET-PARAMS", 10},10}, + {{"GET-PARAMS", 10},10}, + {{"DEFINE-GRAMMAR", 14},0}, + {{"RECOGNIZE", 9},7}, + {{"GET-RESULT", 10},4}, + {{"RECOGNITION-START-TIMERS", 24},7}, + {{"STOP", 4},1} +}; + +/** String table of mrcpv2 recognizer methods (mrcp_recognizer_method_id) */ +static const apt_str_table_item_t v2_recog_method_string_table[] = { + {{"SET-PARAMS", 10},10}, + {{"GET-PARAMS", 10},10}, + {{"DEFINE-GRAMMAR", 14},0}, + {{"RECOGNIZE", 9},7}, + {{"GET-RESULT", 10},4}, + {{"START-INPUT-TIMERS", 18},2}, + {{"STOP", 4},2} +}; + +/** String table of MRCP recognizer events (mrcp_recognizer_event_id) */ +static const apt_str_table_item_t v1_recog_event_string_table[] = { + {{"START-OF-SPEECH", 15},0}, + {{"RECOGNITION-COMPLETE", 20},0} +}; + +/** String table of mrcpv2 recognizer events (mrcp_recognizer_event_id) */ +static const apt_str_table_item_t v2_recog_event_string_table[] = { + {{"START-OF-INPUT", 14},0}, + {{"RECOGNITION-COMPLETE", 20},0} +}; + + +static APR_INLINE const apt_str_table_item_t* recog_method_string_table_get(mrcp_version_e version) +{ + if(version == MRCP_VERSION_1) { + return v1_recog_method_string_table; + } + return v2_recog_method_string_table; +} + +static APR_INLINE const apt_str_table_item_t* recog_event_string_table_get(mrcp_version_e version) +{ + if(version == MRCP_VERSION_1) { + return v1_recog_event_string_table; + } + return v2_recog_event_string_table; +} + +/** Set resource specifica data */ +static apt_bool_t recog_message_resourcify_by_id(mrcp_resource_t *resource, mrcp_message_t *message) +{ + /* associate method_name and method_id */ + if(message->start_line.message_type == MRCP_MESSAGE_TYPE_REQUEST) { + const apt_str_t *name = apt_string_table_str_get( + recog_method_string_table_get(message->start_line.version), + RECOGNIZER_METHOD_COUNT, + message->start_line.method_id); + if(!name) { + return FALSE; + } + message->start_line.method_name = *name; + } + else if(message->start_line.message_type == MRCP_MESSAGE_TYPE_EVENT) { + const apt_str_t *name = apt_string_table_str_get( + recog_event_string_table_get(message->start_line.version), + RECOGNIZER_EVENT_COUNT, + message->start_line.method_id); + if(!name) { + return FALSE; + } + message->start_line.method_name = *name; + } + + message->header.resource_header_accessor.vtable = mrcp_recog_header_vtable_get(message->start_line.version); + return TRUE; +} + +/** Set resource specifica data */ +static apt_bool_t recog_message_resourcify_by_name(mrcp_resource_t *resource, mrcp_message_t *message) +{ + /* associate method_name and method_id */ + if(message->start_line.message_type == MRCP_MESSAGE_TYPE_REQUEST) { + message->start_line.method_id = apt_string_table_id_find( + recog_method_string_table_get(message->start_line.version), + RECOGNIZER_METHOD_COUNT, + &message->start_line.method_name); + if(message->start_line.method_id >= RECOGNIZER_METHOD_COUNT) { + return FALSE; + } + } + else if(message->start_line.message_type == MRCP_MESSAGE_TYPE_EVENT) { + message->start_line.method_id = apt_string_table_id_find( + recog_event_string_table_get(message->start_line.version), + RECOGNIZER_EVENT_COUNT, + &message->start_line.method_name); + if(message->start_line.method_id >= RECOGNIZER_EVENT_COUNT) { + return FALSE; + } + } + + message->header.resource_header_accessor.vtable = mrcp_recog_header_vtable_get(message->start_line.version); + return TRUE; +} + + +/** Create MRCP recognizer resource */ +MRCP_DECLARE(mrcp_resource_t*) mrcp_recog_resource_create(apr_pool_t *pool) +{ + mrcp_resource_t *resource = apr_palloc(pool,sizeof(mrcp_resource_t)); + mrcp_resource_init(resource); + + resource->resourcify_message_by_id = recog_message_resourcify_by_id; + resource->resourcify_message_by_name = recog_message_resourcify_by_name; + + resource->create_client_state_machine = mrcp_recog_client_state_machine_create; + resource->create_server_state_machine = mrcp_recog_server_state_machine_create; + return resource; +} diff --git a/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_server_state_machine.c b/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_server_state_machine.c new file mode 100644 index 0000000000..13828ce572 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_server_state_machine.c @@ -0,0 +1,473 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "apt_obj_list.h" +#include "apt_log.h" +#include "mrcp_state_machine.h" +#include "mrcp_recog_state_machine.h" +#include "mrcp_recog_header.h" +#include "mrcp_generic_header.h" +#include "mrcp_recog_resource.h" +#include "mrcp_message.h" + +/** MRCP recognizer states */ +typedef enum { + RECOGNIZER_STATE_IDLE, + RECOGNIZER_STATE_RECOGNIZING, + RECOGNIZER_STATE_RECOGNIZED, + + RECOGNIZER_STATE_COUNT +} mrcp_recog_state_e; + +static const char * state_names[RECOGNIZER_STATE_COUNT] = { + "IDLE", + "RECOGNIZING", + "RECOGNIZED" +}; + +typedef struct mrcp_recog_state_machine_t mrcp_recog_state_machine_t; +struct mrcp_recog_state_machine_t { + /** state machine base */ + mrcp_state_machine_t base; + /** recognizer state */ + mrcp_recog_state_e state; + /** indicate whether active_request was processed from pending request queue */ + apt_bool_t is_pending; + /** request sent to recognition engine and waiting for the response to be received */ + mrcp_message_t *active_request; + /** in-progress recognize request */ + mrcp_message_t *recog; + /** queue of pending recognition requests */ + apt_obj_list_t *queue; + /** properties used in set/get params */ + mrcp_message_header_t properties; +}; + +typedef apt_bool_t (*recog_method_f)(mrcp_recog_state_machine_t *state_machine, mrcp_message_t *message); + +static APR_INLINE apt_bool_t recog_request_dispatch(mrcp_recog_state_machine_t *state_machine, mrcp_message_t *message) +{ + state_machine->active_request = message; + return state_machine->base.dispatcher(&state_machine->base,message); +} + +static APR_INLINE apt_bool_t recog_response_dispatch(mrcp_recog_state_machine_t *state_machine, mrcp_message_t *message) +{ + state_machine->active_request = NULL; + return state_machine->base.dispatcher(&state_machine->base,message); +} + +static APR_INLINE apt_bool_t recog_event_dispatch(mrcp_recog_state_machine_t *state_machine, mrcp_message_t *message) +{ + return state_machine->base.dispatcher(&state_machine->base,message); +} + +static APR_INLINE void recog_state_change(mrcp_recog_state_machine_t *state_machine, mrcp_recog_state_e state) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"State Transition %s -> %s",state_names[state_machine->state],state_names[state]); + state_machine->state = state; + if(state == RECOGNIZER_STATE_IDLE) { + state_machine->recog = NULL; + } +} + + +static apt_bool_t recog_request_set_params(mrcp_recog_state_machine_t *state_machine, mrcp_message_t *message) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process SET-PARAMS Request [%d]",message->start_line.request_id); + mrcp_message_header_set(&state_machine->properties,&message->header,message->pool); + return recog_request_dispatch(state_machine,message); +} + +static apt_bool_t recog_response_set_params(mrcp_recog_state_machine_t *state_machine, mrcp_message_t *message) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process SET-PARAMS Response [%d]",message->start_line.request_id); + return recog_response_dispatch(state_machine,message); +} + +static apt_bool_t recog_request_get_params(mrcp_recog_state_machine_t *state_machine, mrcp_message_t *message) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process GET-PARAMS Request [%d]",message->start_line.request_id); + return recog_request_dispatch(state_machine,message); +} + +static apt_bool_t recog_response_get_params(mrcp_recog_state_machine_t *state_machine, mrcp_message_t *message) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process GET-PARAMS Response [%d]",message->start_line.request_id); + mrcp_message_header_set(&message->header,&state_machine->active_request->header,message->pool); + mrcp_message_header_get(&message->header,&state_machine->properties,message->pool); + return recog_response_dispatch(state_machine,message); +} + +static apt_bool_t recog_request_define_grammar(mrcp_recog_state_machine_t *state_machine, mrcp_message_t *message) +{ + if(state_machine->state == RECOGNIZER_STATE_RECOGNIZING) { + mrcp_message_t *response_message = mrcp_response_create(message,message->pool); + response_message->start_line.status_code = MRCP_STATUS_CODE_METHOD_NOT_VALID; + return recog_response_dispatch(state_machine,response_message); + } + else if(state_machine->state == RECOGNIZER_STATE_RECOGNIZED) { + recog_state_change(state_machine,RECOGNIZER_STATE_IDLE); + } + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process DEFINE-GRAMMAR Request [%d]",message->start_line.request_id); + return recog_request_dispatch(state_machine,message); +} + +static apt_bool_t recog_response_define_grammar(mrcp_recog_state_machine_t *state_machine, mrcp_message_t *message) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process DEFINE-GRAMMAR Response [%d]",message->start_line.request_id); + if(mrcp_resource_header_property_check(message,RECOGNIZER_HEADER_COMPLETION_CAUSE) != TRUE) { + mrcp_recog_header_t *recog_header = mrcp_resource_header_prepare(message); + recog_header->completion_cause = RECOGNIZER_COMPLETION_CAUSE_SUCCESS; + mrcp_resource_header_property_add(message,RECOGNIZER_HEADER_COMPLETION_CAUSE); + } + return recog_response_dispatch(state_machine,message); +} + +static apt_bool_t recog_request_recognize(mrcp_recog_state_machine_t *state_machine, mrcp_message_t *message) +{ + mrcp_message_header_inherit(&message->header,&state_machine->properties,message->pool); + if(state_machine->state == RECOGNIZER_STATE_RECOGNIZING) { + mrcp_message_t *response; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Queue Up RECOGNIZE Request [%d]",message->start_line.request_id); + message->start_line.request_state = MRCP_REQUEST_STATE_PENDING; + apt_list_push_back(state_machine->queue,message,message->pool); + + response = mrcp_response_create(message,message->pool); + response->start_line.request_state = MRCP_REQUEST_STATE_PENDING; + return recog_response_dispatch(state_machine,response); + } + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process RECOGNIZE Request [%d]",message->start_line.request_id); + return recog_request_dispatch(state_machine,message); +} + +static apt_bool_t recog_response_recognize(mrcp_recog_state_machine_t *state_machine, mrcp_message_t *message) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process RECOGNIZE Response [%d]",message->start_line.request_id); + if(message->start_line.request_state == MRCP_REQUEST_STATE_INPROGRESS) { + state_machine->recog = state_machine->active_request; + recog_state_change(state_machine,RECOGNIZER_STATE_RECOGNIZING); + } + if(state_machine->is_pending == TRUE) { + state_machine->is_pending = FALSE; + /* not to send the response for pending request */ + return TRUE; + } + return recog_response_dispatch(state_machine,message); +} + +static apt_bool_t recog_request_get_result(mrcp_recog_state_machine_t *state_machine, mrcp_message_t *message) +{ + mrcp_message_t *response_message; + if(state_machine->state == RECOGNIZER_STATE_RECOGNIZED) { + /* found recognized request */ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process GET-RESULT Request [%d]",message->start_line.request_id); + return recog_request_dispatch(state_machine,message); + } + + /* found no recognized request */ + response_message = mrcp_response_create(message,message->pool); + response_message->start_line.status_code = MRCP_STATUS_CODE_METHOD_NOT_VALID; + return recog_response_dispatch(state_machine,response_message); +} + +static apt_bool_t recog_response_get_result(mrcp_recog_state_machine_t *state_machine, mrcp_message_t *message) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process GET-RESULT Response [%d]",message->start_line.request_id); + return recog_response_dispatch(state_machine,message); +} + +static apt_bool_t recog_request_recognition_start_timers(mrcp_recog_state_machine_t *state_machine, mrcp_message_t *message) +{ + mrcp_message_t *response_message; + if(state_machine->state == RECOGNIZER_STATE_RECOGNIZING) { + /* found in-progress request */ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process START-INPUT-TIMERS Request [%d]",message->start_line.request_id); + return recog_request_dispatch(state_machine,message); + } + + /* found no in-progress request */ + response_message = mrcp_response_create(message,message->pool); + response_message->start_line.status_code = MRCP_STATUS_CODE_METHOD_NOT_VALID; + return recog_response_dispatch(state_machine,response_message); +} + +static apt_bool_t recog_response_recognition_start_timers(mrcp_recog_state_machine_t *state_machine, mrcp_message_t *message) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process START-INPUT-TIMERS Response [%d]",message->start_line.request_id); + return recog_response_dispatch(state_machine,message); +} + +static apt_bool_t recog_pending_requests_remove(mrcp_recog_state_machine_t *state_machine, mrcp_message_t *request_message, mrcp_message_t *response_message) +{ + apt_list_elem_t *elem; + mrcp_message_t *pending_message; + mrcp_request_id_list_t *request_id_list = NULL; + mrcp_generic_header_t *generic_header = mrcp_generic_header_get(request_message); + mrcp_generic_header_t *response_generic_header = mrcp_generic_header_prepare(response_message); + if(generic_header && mrcp_generic_header_property_check(request_message,GENERIC_HEADER_ACTIVE_REQUEST_ID_LIST) == TRUE) { + if(generic_header->active_request_id_list.ids && generic_header->active_request_id_list.count) { + /* selective STOP request */ + request_id_list = &generic_header->active_request_id_list; + } + } + + elem = apt_list_first_elem_get(state_machine->queue); + while(elem) { + pending_message = apt_list_elem_object_get(elem); + if(!request_id_list || active_request_id_list_find(generic_header,pending_message->start_line.request_id) == TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Remove Pending RECOGNIZE Request [%d]",pending_message->start_line.request_id); + elem = apt_list_elem_remove(state_machine->queue,elem); + /* append active id list */ + active_request_id_list_append(response_generic_header,pending_message->start_line.request_id); + } + else { + /* speak request remains in the queue, just proceed to the next one */ + elem = apt_list_next_elem_get(state_machine->queue,elem); + } + } + if(response_generic_header->active_request_id_list.count) { + mrcp_generic_header_property_add(response_message,GENERIC_HEADER_ACTIVE_REQUEST_ID_LIST); + } + return TRUE; +} + +static apt_bool_t recog_request_stop(mrcp_recog_state_machine_t *state_machine, mrcp_message_t *message) +{ + mrcp_message_t *response_message; + if(state_machine->state == RECOGNIZER_STATE_RECOGNIZING) { + mrcp_request_id_list_t *request_id_list = NULL; + mrcp_generic_header_t *generic_header = mrcp_generic_header_get(message); + if(generic_header && mrcp_generic_header_property_check(message,GENERIC_HEADER_ACTIVE_REQUEST_ID_LIST) == TRUE) { + if(generic_header->active_request_id_list.ids && generic_header->active_request_id_list.count) { + /* selective STOP request */ + request_id_list = &generic_header->active_request_id_list; + } + } + + if(!request_id_list || active_request_id_list_find(generic_header,state_machine->recog->start_line.request_id) == TRUE) { + /* found in-progress RECOGNIZE request, stop it */ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process STOP Request [%d]",message->start_line.request_id); + return recog_request_dispatch(state_machine,message); + } + } + else if(state_machine->state == RECOGNIZER_STATE_RECOGNIZED) { + recog_state_change(state_machine,RECOGNIZER_STATE_IDLE); + } + + /* found no in-progress RECOGNIZE request, sending immediate response */ + response_message = mrcp_response_create(message,message->pool); + recog_pending_requests_remove(state_machine,message,response_message); + return recog_response_dispatch(state_machine,response_message); +} + +static apt_bool_t recog_response_stop(mrcp_recog_state_machine_t *state_machine, mrcp_message_t *message) +{ + mrcp_message_t *pending_request; + mrcp_generic_header_t *generic_header = mrcp_generic_header_prepare(message); + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process STOP Response [%d]",message->start_line.request_id); + /* append active id list */ + active_request_id_list_append(generic_header,state_machine->recog->start_line.request_id); + mrcp_generic_header_property_add(message,GENERIC_HEADER_ACTIVE_REQUEST_ID_LIST); + recog_pending_requests_remove(state_machine,state_machine->active_request,message); + recog_state_change(state_machine,RECOGNIZER_STATE_IDLE); + recog_response_dispatch(state_machine,message); + + /* process pending RECOGNIZE requests / if any */ + pending_request = apt_list_pop_front(state_machine->queue); + if(pending_request) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process Pending RECOGNIZE Request [%d]",pending_request->start_line.request_id); + state_machine->is_pending = TRUE; + recog_request_dispatch(state_machine,pending_request); + } + return TRUE; +} + +static apt_bool_t recog_event_start_of_input(mrcp_recog_state_machine_t *state_machine, mrcp_message_t *message) +{ + if(!state_machine->recog) { + /* unexpected event, no in-progress recognition request */ + return FALSE; + } + + if(state_machine->recog->start_line.request_id != message->start_line.request_id) { + /* unexpected event */ + return FALSE; + } + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process START-OF-INPUT Event [%d]",message->start_line.request_id); + message->start_line.request_state = MRCP_REQUEST_STATE_INPROGRESS; + return recog_event_dispatch(state_machine,message); +} + +static apt_bool_t recog_event_recognition_complete(mrcp_recog_state_machine_t *state_machine, mrcp_message_t *message) +{ + mrcp_message_t *pending_request; + if(!state_machine->recog) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Unexpected RECOGNITION-COMPLETE Event [%d]",message->start_line.request_id); + return FALSE; + } + + if(state_machine->recog->start_line.request_id != message->start_line.request_id) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Unexpected RECOGNITION-COMPLETE Event [%d]",message->start_line.request_id); + return FALSE; + } + + if(state_machine->active_request && state_machine->active_request->start_line.method_id == RECOGNIZER_STOP) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Ignore RECOGNITION-COMPLETE Event [%d]: waiting for STOP response",message->start_line.request_id); + return FALSE; + } + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process RECOGNITION-COMPLETE Event [%d]",message->start_line.request_id); + if(mrcp_resource_header_property_check(message,RECOGNIZER_HEADER_COMPLETION_CAUSE) != TRUE) { + mrcp_recog_header_t *recog_header = mrcp_resource_header_prepare(message); + recog_header->completion_cause = RECOGNIZER_COMPLETION_CAUSE_SUCCESS; + mrcp_resource_header_property_add(message,RECOGNIZER_HEADER_COMPLETION_CAUSE); + } + recog_state_change(state_machine,RECOGNIZER_STATE_RECOGNIZED); + recog_event_dispatch(state_machine,message); + + /* process pending RECOGNIZE requests */ + pending_request = apt_list_pop_front(state_machine->queue); + if(pending_request) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process Pending RECOGNIZE Request [%d]",pending_request->start_line.request_id); + state_machine->is_pending = TRUE; + recog_request_dispatch(state_machine,pending_request); + } + return TRUE; +} + +static recog_method_f recog_request_method_array[RECOGNIZER_METHOD_COUNT] = { + recog_request_set_params, + recog_request_get_params, + recog_request_define_grammar, + recog_request_recognize, + recog_request_get_result, + recog_request_recognition_start_timers, + recog_request_stop +}; + +static recog_method_f recog_response_method_array[RECOGNIZER_METHOD_COUNT] = { + recog_response_set_params, + recog_response_get_params, + recog_response_define_grammar, + recog_response_recognize, + recog_response_get_result, + recog_response_recognition_start_timers, + recog_response_stop +}; + +static recog_method_f recog_event_method_array[RECOGNIZER_EVENT_COUNT] = { + recog_event_start_of_input, + recog_event_recognition_complete +}; + +/** Update state according to received incoming request from MRCP client */ +static apt_bool_t recog_request_state_update(mrcp_recog_state_machine_t *state_machine, mrcp_message_t *message) +{ + recog_method_f method; + if(message->start_line.method_id >= RECOGNIZER_METHOD_COUNT) { + return FALSE; + } + + method = recog_request_method_array[message->start_line.method_id]; + if(method) { + return method(state_machine,message); + } + return recog_request_dispatch(state_machine,message); +} + +/** Update state according to received outgoing response from recognition engine */ +static apt_bool_t recog_response_state_update(mrcp_recog_state_machine_t *state_machine, mrcp_message_t *message) +{ + recog_method_f method; + if(!state_machine->active_request) { + /* unexpected response, no active request waiting for response */ + return FALSE; + } + if(state_machine->active_request->start_line.request_id != message->start_line.request_id) { + /* unexpected response, request id doesn't match */ + return FALSE; + } + + if(message->start_line.method_id >= RECOGNIZER_METHOD_COUNT) { + return FALSE; + } + + method = recog_response_method_array[message->start_line.method_id]; + if(method) { + return method(state_machine,message); + } + return recog_response_dispatch(state_machine,message); +} + +/** Update state according to received outgoing event from recognition engine */ +static apt_bool_t recog_event_state_update(mrcp_recog_state_machine_t *state_machine, mrcp_message_t *message) +{ + recog_method_f method; + if(message->start_line.method_id >= RECOGNIZER_EVENT_COUNT) { + return FALSE; + } + + method = recog_event_method_array[message->start_line.method_id]; + if(method) { + return method(state_machine,message); + } + return recog_event_dispatch(state_machine,message); +} + +/** Update state according to request received from MRCP client or response/event received from recognition engine */ +static apt_bool_t recog_state_update(mrcp_state_machine_t *state_machine, mrcp_message_t *message) +{ + mrcp_recog_state_machine_t *recog_state_machine = (mrcp_recog_state_machine_t*)state_machine; + apt_bool_t status = TRUE; + switch(message->start_line.message_type) { + case MRCP_MESSAGE_TYPE_REQUEST: + status = recog_request_state_update(recog_state_machine,message); + break; + case MRCP_MESSAGE_TYPE_RESPONSE: + status = recog_response_state_update(recog_state_machine,message); + break; + case MRCP_MESSAGE_TYPE_EVENT: + status = recog_event_state_update(recog_state_machine,message); + break; + default: + status = FALSE; + break; + } + return status; +} + +/** Create MRCP recognizer server state machine */ +mrcp_state_machine_t* mrcp_recog_server_state_machine_create(void *obj, mrcp_message_dispatcher_f dispatcher, mrcp_version_e version, apr_pool_t *pool) +{ + mrcp_message_header_t *properties; + mrcp_recog_state_machine_t *state_machine = apr_palloc(pool,sizeof(mrcp_recog_state_machine_t)); + mrcp_state_machine_init(&state_machine->base,obj,dispatcher); + state_machine->base.update = recog_state_update; + state_machine->state = RECOGNIZER_STATE_IDLE; + state_machine->is_pending = FALSE; + state_machine->active_request = NULL; + state_machine->recog = NULL; + state_machine->queue = apt_list_create(pool); + properties = &state_machine->properties; + mrcp_message_header_init(properties); + properties->generic_header_accessor.vtable = mrcp_generic_header_vtable_get(version); + properties->resource_header_accessor.vtable = mrcp_recog_header_vtable_get(version); + return &state_machine->base; +} diff --git a/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_client_state_machine.c b/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_client_state_machine.c new file mode 100644 index 0000000000..6dd4da2dd9 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_client_state_machine.c @@ -0,0 +1,36 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mrcp_state_machine.h" +#include "mrcp_synth_state_machine.h" +#include "mrcp_message.h" + + +/** Update state according to request received from user level or response/event received from MRCP server */ +static apt_bool_t synth_state_update(mrcp_state_machine_t *state_machine, mrcp_message_t *message) +{ + /* no actual state machine processing yet, dispatch whatever received */ + return state_machine->dispatcher(state_machine,message); +} + +/** Create MRCP synthesizer client state machine */ +mrcp_state_machine_t* mrcp_synth_client_state_machine_create(void *obj, mrcp_message_dispatcher_f dispatcher, mrcp_version_e version, apr_pool_t *pool) +{ + mrcp_state_machine_t *state_machine = apr_palloc(pool,sizeof(mrcp_state_machine_t)); + mrcp_state_machine_init(state_machine,obj,dispatcher); + state_machine->update = synth_state_update; + return state_machine; +} diff --git a/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_header.c b/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_header.c new file mode 100644 index 0000000000..e7c467f85d --- /dev/null +++ b/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_header.c @@ -0,0 +1,558 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mrcp_synth_header.h" + +/** String table of MRCP synthesizer headers (mrcp_synthesizer_header_id) */ +static const apt_str_table_item_t synth_header_string_table[] = { + {{"Jump-Size", 9},0}, + {{"Kill-On-Barge-In", 16},0}, + {{"Speaker-Profile", 15},8}, + {{"Completion-Cause", 16},16}, + {{"Completion-Reason", 17},13}, + {{"Voice-Gender", 12},6}, + {{"Voice-Age", 9},6}, + {{"Voice-Variant", 13},6}, + {{"Voice-Name", 10},8}, + {{"Prosody-Volume", 14},8}, + {{"Prosody-Rate", 12},12}, + {{"Speech-Marker", 13},7}, + {{"Speech-Language", 15},7}, + {{"Fetch-Hint", 10},2}, + {{"Audio-Fetch-Hint", 16},0}, + {{"Failed-Uri", 10},10}, + {{"Failed-Uri_Cause", 16},10}, + {{"Speak-Restart", 13},13}, + {{"Speak-Length", 12},6}, + {{"Load-Lexicon", 12},2}, + {{"Lexicon-Search-Order",20},2} +}; + +/** String table of MRCP speech-unit fields (mrcp_speech_unit_t) */ +static const apt_str_table_item_t speech_unit_string_table[] = { + {{"Second", 6},2}, + {{"Word", 4},0}, + {{"Sentence", 8},2}, + {{"Paragraph",9},0} +}; + +/** String table of MRCP voice-gender fields (mrcp_voice_gender_t) */ +static const apt_str_table_item_t voice_gender_string_table[] = { + {{"male", 4},0}, + {{"female", 6},0}, + {{"neutral",7},0} +}; + +/** String table of MRCP prosody-volume fields (mrcp_prosody_volume_t) */ +static const apt_str_table_item_t prosody_volume_string_table[] = { + {{"silent", 6},1}, + {{"x-soft", 6},2}, + {{"soft", 4},3}, + {{"medium", 6},0}, + {{"loud", 4},0}, + {{"x-loud", 6},5}, + {{"default",7},0} +}; + +/** String table of MRCP prosody-rate fields (mrcp_prosody_rate_t) */ +static const apt_str_table_item_t prosody_rate_string_table[] = { + {{"x-slow", 6},3}, + {{"slow", 4},0}, + {{"medium", 6},0}, + {{"fast", 4},0}, + {{"x-fast", 6},4}, + {{"default",7},0} +}; + +/** String table of MRCP synthesizer completion-cause fields (mrcp_synthesizer_completion_cause_t) */ +static const apt_str_table_item_t completion_cause_string_table[] = { + {{"normal", 6},0}, + {{"barge-in", 8},0}, + {{"parse-failure", 13},0}, + {{"uri-failure", 11},0}, + {{"error", 5},0}, + {{"language-unsupported",20},4}, + {{"lexicon-load-failure",20},1}, + {{"cancelled", 9},0} +}; + + +static APR_INLINE apr_size_t apt_string_table_value_parse(const apt_str_table_item_t *string_table, size_t count, const apt_str_t *value) +{ + return apt_string_table_id_find(string_table,count,value); +} + +static apt_bool_t apt_string_table_value_generate(const apt_str_table_item_t *string_table, size_t count, size_t id, apt_text_stream_t *stream) +{ + const apt_str_t *name = apt_string_table_str_get(string_table,count,id); + if(!name) { + return FALSE; + } + + memcpy(stream->pos,name->buf,name->length); + stream->pos += name->length; + return TRUE; +} + +/** Parse MRCP prosody-rate value */ +static apt_bool_t mrcp_prosody_param_rate_parse(mrcp_prosody_rate_t *prosody_rate, const apt_str_t *value, apr_pool_t *pool) +{ + if(!value->length) { + return FALSE; + } + + /** For the rate attribute, relative changes are a number. (not preceded by a "+" or "-")(w3c ssml)*/ + if('0'<=value->buf[0] && value->buf[0]<='9') { + prosody_rate->type = PROSODY_RATE_TYPE_RELATIVE_CHANGE; + } + else { + prosody_rate->type = PROSODY_RATE_TYPE_LABEL; + } + + if(prosody_rate->type == PROSODY_RATE_TYPE_RELATIVE_CHANGE) { + prosody_rate->value.relative = apt_float_value_parse(value); + } + else { + prosody_rate->value.label = apt_string_table_value_parse(prosody_rate_string_table,PROSODY_RATE_COUNT,value); + } + + return TRUE; +} + +/** Generate MRCP prosody-rate value */ +static apt_bool_t mrcp_prosody_rate_generate(mrcp_prosody_rate_t *prosody_rate, apt_text_stream_t *stream) +{ + if(prosody_rate->type == PROSODY_RATE_TYPE_LABEL) { + apt_string_table_value_generate(prosody_rate_string_table,PROSODY_RATE_COUNT,prosody_rate->value.label,stream); + } + else { + apt_float_value_generate(prosody_rate->value.relative, stream); + } + + return TRUE; +} + +/** Parse MRCP prosody-volume value */ +static apt_bool_t mrcp_prosody_param_volume_parse(mrcp_prosody_volume_t *prosody_volume, const apt_str_t *value, apr_pool_t *pool) +{ + if(!value->length) { + return FALSE; + } + + /** For the volume attribute, relative changes are a number preceded by "+" or "-" (w3c ssml)*/ + if(value->buf[0]=='+' || value->buf[0]=='-') { + prosody_volume->type = PROSODY_VOLUME_TYPE_RELATIVE_CHANGE; + } + else if('0'<=value->buf[0] && value->buf[0]<='9') { + prosody_volume->type = PROSODY_VOLUME_TYPE_NUMERIC; + } + else { + prosody_volume->type = PROSODY_VOLUME_TYPE_LABEL; + } + + if(prosody_volume->type == PROSODY_VOLUME_TYPE_RELATIVE_CHANGE) { + prosody_volume->value.relative = apt_float_value_parse(value); + } + else if(prosody_volume->type == PROSODY_VOLUME_TYPE_NUMERIC) { + prosody_volume->value.numeric = apt_float_value_parse(value); + } + else { + prosody_volume->value.label = apt_string_table_value_parse(prosody_volume_string_table,PROSODY_VOLUME_COUNT,value); + } + + return TRUE; +} + +/** Generate MRCP prosody-volume value */ +static apt_bool_t mrcp_prosody_volume_generate(mrcp_prosody_volume_t *prosody_volume, apt_text_stream_t *stream) +{ + if(prosody_volume->type == PROSODY_VOLUME_TYPE_LABEL) { + apt_string_table_value_generate(prosody_volume_string_table,PROSODY_VOLUME_COUNT,prosody_volume->value.label,stream); + } + else if (prosody_volume->type == PROSODY_VOLUME_TYPE_NUMERIC) { + apt_float_value_generate(prosody_volume->value.numeric, stream); + } + else { + apt_float_value_generate(prosody_volume->value.relative, stream); + } + + return TRUE; +} + +/** Parse MRCP speech-length value */ +static apt_bool_t mrcp_speech_length_value_parse(mrcp_speech_length_value_t *speech_length, const apt_str_t *value, apr_pool_t *pool) +{ + if(!value->length) { + return FALSE; + } + + switch(*value->buf) { + case '+': speech_length->type = SPEECH_LENGTH_TYPE_NUMERIC_POSITIVE; break; + case '-': speech_length->type = SPEECH_LENGTH_TYPE_NUMERIC_NEGATIVE; break; + default : speech_length->type = SPEECH_LENGTH_TYPE_TEXT; + } + + if(speech_length->type == SPEECH_LENGTH_TYPE_TEXT) { + apt_string_copy(&speech_length->value.tag,value,pool); + } + else { + mrcp_numeric_speech_length_t *numeric = &speech_length->value.numeric; + apt_str_t str; + apt_text_stream_t stream; + stream.text = *value; + stream.pos = stream.text.buf; + stream.pos++; + if(apt_text_field_read(&stream,APT_TOKEN_SP,TRUE,&str) == FALSE) { + return FALSE; + } + numeric->length = apt_size_value_parse(&str); + + if(apt_text_field_read(&stream,APT_TOKEN_SP,TRUE,&str) == FALSE) { + return FALSE; + } + numeric->unit = apt_string_table_value_parse(speech_unit_string_table,SPEECH_UNIT_COUNT,&str); + } + return TRUE; +} + +/** Generate MRCP speech-length value */ +static apt_bool_t mrcp_speech_length_generate(mrcp_speech_length_value_t *speech_length, apt_text_stream_t *stream) +{ + if(speech_length->type == SPEECH_LENGTH_TYPE_TEXT) { + apt_str_t *tag = &speech_length->value.tag; + if(tag->length) { + memcpy(stream->pos,tag->buf,tag->length); + stream->pos += tag->length; + } + } + else { + if(speech_length->type == SPEECH_LENGTH_TYPE_NUMERIC_POSITIVE) { + *stream->pos++ = '+'; + } + else { + *stream->pos++ = '-'; + } + apt_size_value_generate(speech_length->value.numeric.length,stream); + *stream->pos++ = ' '; + apt_string_table_value_generate(speech_unit_string_table,SPEECH_UNIT_COUNT,speech_length->value.numeric.unit,stream); + } + return TRUE; +} + +/** Generate MRCP synthesizer completion-cause */ +static apt_bool_t mrcp_completion_cause_generate(mrcp_synth_completion_cause_e completion_cause, apt_text_stream_t *stream) +{ + int length; + const apt_str_t *name = apt_string_table_str_get(completion_cause_string_table,SYNTHESIZER_COMPLETION_CAUSE_COUNT,completion_cause); + if(!name) { + return FALSE; + } + length = sprintf(stream->pos,"%03"APR_SIZE_T_FMT" ",completion_cause); + if(length <= 0) { + return FALSE; + } + stream->pos += length; + + memcpy(stream->pos,name->buf,name->length); + stream->pos += name->length; + return TRUE; +} + +/** Initialize synthesizer header */ +static void mrcp_synth_header_init(mrcp_synth_header_t *synth_header) +{ + synth_header->jump_size.type = SPEECH_LENGTH_TYPE_UNKNOWN; + synth_header->kill_on_barge_in = FALSE; + apt_string_reset(&synth_header->speaker_profile); + synth_header->completion_cause = SYNTHESIZER_COMPLETION_CAUSE_UNKNOWN; + apt_string_reset(&synth_header->completion_reason); + synth_header->voice_param.gender = VOICE_GENDER_UNKNOWN; + synth_header->voice_param.age = 0; + synth_header->voice_param.variant = 0; + apt_string_reset(&synth_header->voice_param.name); + synth_header->prosody_param.volume.type = PROSODY_VOLUME_TYPE_UNKNOWN; + synth_header->prosody_param.rate.type = PROSODY_RATE_TYPE_UNKNOWN; + apt_string_reset(&synth_header->speech_marker); + apt_string_reset(&synth_header->speech_language); + apt_string_reset(&synth_header->fetch_hint); + apt_string_reset(&synth_header->audio_fetch_hint); + apt_string_reset(&synth_header->failed_uri); + apt_string_reset(&synth_header->failed_uri_cause); + synth_header->speak_restart = FALSE; + synth_header->speak_length.type = SPEECH_LENGTH_TYPE_UNKNOWN; + synth_header->load_lexicon = FALSE; + apt_string_reset(&synth_header->lexicon_search_order); +} + + +/** Allocate MRCP synthesizer header */ +static void* mrcp_synth_header_allocate(mrcp_header_accessor_t *accessor, apr_pool_t *pool) +{ + mrcp_synth_header_t *synth_header = apr_palloc(pool,sizeof(mrcp_synth_header_t)); + mrcp_synth_header_init(synth_header); + accessor->data = synth_header; + return accessor->data; +} + +/** Parse MRCP synthesizer header */ +static apt_bool_t mrcp_synth_header_parse(mrcp_header_accessor_t *accessor, size_t id, const apt_str_t *value, apr_pool_t *pool) +{ + apt_bool_t status = TRUE; + mrcp_synth_header_t *synth_header = accessor->data; + switch(id) { + case SYNTHESIZER_HEADER_JUMP_SIZE: + mrcp_speech_length_value_parse(&synth_header->jump_size,value,pool); + break; + case SYNTHESIZER_HEADER_KILL_ON_BARGE_IN: + apt_boolean_value_parse(value,&synth_header->kill_on_barge_in); + break; + case SYNTHESIZER_HEADER_SPEAKER_PROFILE: + apt_string_copy(&synth_header->speaker_profile,value,pool); + break; + case SYNTHESIZER_HEADER_COMPLETION_CAUSE: + synth_header->completion_cause = apt_size_value_parse(value); + break; + case SYNTHESIZER_HEADER_COMPLETION_REASON: + apt_string_copy(&synth_header->completion_reason,value,pool); + break; + case SYNTHESIZER_HEADER_VOICE_GENDER: + synth_header->voice_param.gender = apt_string_table_value_parse(voice_gender_string_table,VOICE_GENDER_COUNT,value); + break; + case SYNTHESIZER_HEADER_VOICE_AGE: + synth_header->voice_param.age = apt_size_value_parse(value); + break; + case SYNTHESIZER_HEADER_VOICE_VARIANT: + synth_header->voice_param.variant = apt_size_value_parse(value); + break; + case SYNTHESIZER_HEADER_VOICE_NAME: + apt_string_copy(&synth_header->voice_param.name,value,pool); + break; + case SYNTHESIZER_HEADER_PROSODY_VOLUME: + mrcp_prosody_param_volume_parse(&synth_header->prosody_param.volume,value,pool); + break; + case SYNTHESIZER_HEADER_PROSODY_RATE: + mrcp_prosody_param_rate_parse(&synth_header->prosody_param.rate,value,pool); + break; + case SYNTHESIZER_HEADER_SPEECH_MARKER: + apt_string_copy(&synth_header->speech_marker,value,pool); + break; + case SYNTHESIZER_HEADER_SPEECH_LANGUAGE: + apt_string_copy(&synth_header->speech_language,value,pool); + break; + case SYNTHESIZER_HEADER_FETCH_HINT: + apt_string_copy(&synth_header->fetch_hint,value,pool); + break; + case SYNTHESIZER_HEADER_AUDIO_FETCH_HINT: + apt_string_copy(&synth_header->audio_fetch_hint,value,pool); + break; + case SYNTHESIZER_HEADER_FAILED_URI: + apt_string_copy(&synth_header->failed_uri,value,pool); + break; + case SYNTHESIZER_HEADER_FAILED_URI_CAUSE: + apt_string_copy(&synth_header->failed_uri_cause,value,pool); + break; + case SYNTHESIZER_HEADER_SPEAK_RESTART: + apt_boolean_value_parse(value,&synth_header->speak_restart); + break; + case SYNTHESIZER_HEADER_SPEAK_LENGTH: + mrcp_speech_length_value_parse(&synth_header->speak_length,value,pool); + break; + case SYNTHESIZER_HEADER_LOAD_LEXICON: + apt_boolean_value_parse(value,&synth_header->load_lexicon); + break; + case SYNTHESIZER_HEADER_LEXICON_SEARCH_ORDER: + apt_string_copy(&synth_header->lexicon_search_order,value,pool); + break; + default: + status = FALSE; + } + return status; +} + +/** Generate MRCP synthesizer header */ +static apt_bool_t mrcp_synth_header_generate(mrcp_header_accessor_t *accessor, size_t id, apt_text_stream_t *value) +{ + mrcp_synth_header_t *synth_header = accessor->data; + switch(id) { + case SYNTHESIZER_HEADER_JUMP_SIZE: + mrcp_speech_length_generate(&synth_header->jump_size,value); + break; + case SYNTHESIZER_HEADER_KILL_ON_BARGE_IN: + apt_boolean_value_generate(synth_header->kill_on_barge_in,value); + break; + case SYNTHESIZER_HEADER_SPEAKER_PROFILE: + apt_string_value_generate(&synth_header->speaker_profile,value); + break; + case SYNTHESIZER_HEADER_COMPLETION_CAUSE: + mrcp_completion_cause_generate(synth_header->completion_cause,value); + break; + case SYNTHESIZER_HEADER_COMPLETION_REASON: + apt_string_value_generate(&synth_header->completion_reason,value); + break; + case SYNTHESIZER_HEADER_VOICE_GENDER: + apt_string_table_value_generate(voice_gender_string_table,VOICE_GENDER_COUNT,synth_header->voice_param.gender,value); + break; + case SYNTHESIZER_HEADER_VOICE_AGE: + apt_size_value_generate(synth_header->voice_param.age,value); + break; + case SYNTHESIZER_HEADER_VOICE_VARIANT: + apt_size_value_generate(synth_header->voice_param.variant,value); + break; + case SYNTHESIZER_HEADER_VOICE_NAME: + apt_string_value_generate(&synth_header->voice_param.name,value); + break; + case SYNTHESIZER_HEADER_PROSODY_VOLUME: + mrcp_prosody_volume_generate(&synth_header->prosody_param.volume,value); + break; + case SYNTHESIZER_HEADER_PROSODY_RATE: + mrcp_prosody_rate_generate(&synth_header->prosody_param.rate,value); + break; + case SYNTHESIZER_HEADER_SPEECH_MARKER: + apt_string_value_generate(&synth_header->speech_marker,value); + break; + case SYNTHESIZER_HEADER_SPEECH_LANGUAGE: + apt_string_value_generate(&synth_header->speech_language,value); + break; + case SYNTHESIZER_HEADER_FETCH_HINT: + apt_string_value_generate(&synth_header->fetch_hint,value); + break; + case SYNTHESIZER_HEADER_AUDIO_FETCH_HINT: + apt_string_value_generate(&synth_header->audio_fetch_hint,value); + break; + case SYNTHESIZER_HEADER_FAILED_URI: + apt_string_value_generate(&synth_header->failed_uri,value); + break; + case SYNTHESIZER_HEADER_FAILED_URI_CAUSE: + apt_string_value_generate(&synth_header->failed_uri_cause,value); + break; + case SYNTHESIZER_HEADER_SPEAK_RESTART: + apt_boolean_value_generate(synth_header->speak_restart,value); + break; + case SYNTHESIZER_HEADER_SPEAK_LENGTH: + mrcp_speech_length_generate(&synth_header->speak_length,value); + break; + case SYNTHESIZER_HEADER_LOAD_LEXICON: + apt_boolean_value_generate(synth_header->load_lexicon,value); + break; + case SYNTHESIZER_HEADER_LEXICON_SEARCH_ORDER: + apt_string_value_generate(&synth_header->lexicon_search_order,value); + break; + default: + break; + } + return TRUE; +} + +/** Duplicate MRCP synthesizer header */ +static apt_bool_t mrcp_synth_header_duplicate(mrcp_header_accessor_t *accessor, const mrcp_header_accessor_t *src, size_t id, apr_pool_t *pool) +{ + mrcp_synth_header_t *synth_header = accessor->data; + const mrcp_synth_header_t *src_synth_header = src->data; + apt_bool_t status = TRUE; + + if(!synth_header || !src_synth_header) { + return FALSE; + } + + switch(id) { + case SYNTHESIZER_HEADER_JUMP_SIZE: + synth_header->jump_size = src_synth_header->jump_size; + break; + case SYNTHESIZER_HEADER_KILL_ON_BARGE_IN: + synth_header->kill_on_barge_in = src_synth_header->kill_on_barge_in; + break; + case SYNTHESIZER_HEADER_SPEAKER_PROFILE: + apt_string_copy(&synth_header->speaker_profile,&src_synth_header->speaker_profile,pool); + break; + case SYNTHESIZER_HEADER_COMPLETION_CAUSE: + synth_header->completion_cause = src_synth_header->completion_cause; + break; + case SYNTHESIZER_HEADER_COMPLETION_REASON: + apt_string_copy(&synth_header->completion_reason,&src_synth_header->completion_reason,pool); + break; + case SYNTHESIZER_HEADER_VOICE_GENDER: + synth_header->voice_param.gender = src_synth_header->voice_param.gender; + break; + case SYNTHESIZER_HEADER_VOICE_AGE: + synth_header->voice_param.age = src_synth_header->voice_param.age; + break; + case SYNTHESIZER_HEADER_VOICE_VARIANT: + synth_header->voice_param.variant = src_synth_header->voice_param.variant; + break; + case SYNTHESIZER_HEADER_VOICE_NAME: + apt_string_copy(&synth_header->voice_param.name,&src_synth_header->voice_param.name,pool); + break; + case SYNTHESIZER_HEADER_PROSODY_VOLUME: + synth_header->prosody_param.volume = src_synth_header->prosody_param.volume; + break; + case SYNTHESIZER_HEADER_PROSODY_RATE: + synth_header->prosody_param.rate = src_synth_header->prosody_param.rate; + break; + case SYNTHESIZER_HEADER_SPEECH_MARKER: + apt_string_copy(&synth_header->speech_marker,&src_synth_header->speech_marker,pool); + break; + case SYNTHESIZER_HEADER_SPEECH_LANGUAGE: + apt_string_copy(&synth_header->speech_language,&src_synth_header->speech_language,pool); + break; + case SYNTHESIZER_HEADER_FETCH_HINT: + apt_string_copy(&synth_header->fetch_hint,&src_synth_header->fetch_hint,pool); + break; + case SYNTHESIZER_HEADER_AUDIO_FETCH_HINT: + apt_string_copy(&synth_header->audio_fetch_hint,&src_synth_header->audio_fetch_hint,pool); + break; + case SYNTHESIZER_HEADER_FAILED_URI: + apt_string_copy(&synth_header->failed_uri,&src_synth_header->failed_uri,pool); + break; + case SYNTHESIZER_HEADER_FAILED_URI_CAUSE: + apt_string_copy(&synth_header->failed_uri_cause,&src_synth_header->failed_uri_cause,pool); + break; + case SYNTHESIZER_HEADER_SPEAK_RESTART: + synth_header->speak_restart = src_synth_header->speak_restart; + break; + case SYNTHESIZER_HEADER_SPEAK_LENGTH: + synth_header->speak_length = src_synth_header->speak_length; + break; + case SYNTHESIZER_HEADER_LOAD_LEXICON: + synth_header->load_lexicon = src_synth_header->load_lexicon; + break; + case SYNTHESIZER_HEADER_LEXICON_SEARCH_ORDER: + apt_string_copy(&synth_header->lexicon_search_order,&src_synth_header->lexicon_search_order,pool); + break; + default: + status = FALSE; + } + return status; +} + +static const mrcp_header_vtable_t vtable = { + mrcp_synth_header_allocate, + NULL, /* nothing to destroy */ + mrcp_synth_header_parse, + mrcp_synth_header_generate, + mrcp_synth_header_duplicate, + synth_header_string_table, + SYNTHESIZER_HEADER_COUNT +}; + +MRCP_DECLARE(const mrcp_header_vtable_t*) mrcp_synth_header_vtable_get(mrcp_version_e version) +{ + return &vtable; +} + +MRCP_DECLARE(const apt_str_t*) mrcp_synth_completion_cause_get(mrcp_synth_completion_cause_e completion_cause, mrcp_version_e version) +{ + return apt_string_table_str_get(completion_cause_string_table,SYNTHESIZER_COMPLETION_CAUSE_COUNT,completion_cause); +} diff --git a/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_resource.c b/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_resource.c new file mode 100644 index 0000000000..9018f49002 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_resource.c @@ -0,0 +1,113 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mrcp_synth_resource.h" +#include "mrcp_synth_header.h" +#include "mrcp_synth_state_machine.h" +#include "mrcp_resource.h" +#include "mrcp_message.h" + +/** String table of MRCP synthesizer methods (mrcp_synthesizer_method_id) */ +static const apt_str_table_item_t synth_method_string_table[] = { + {{"SET-PARAMS", 10},10}, + {{"GET-PARAMS", 10},0}, + {{"SPEAK", 5},1}, + {{"STOP", 4},1}, + {{"PAUSE", 5},0}, + {{"RESUME", 6},0}, + {{"BARGE-IN-OCCURRED",17},0}, + {{"CONTROL", 7},0}, + {{"DEFINE-LEXICON", 14},0} +}; + +/** String table of MRCP synthesizer events (mrcp_synthesizer_event_id) */ +static const apt_str_table_item_t synth_event_string_table[] = { + {{"SPEECH-MARKER", 13},3}, + {{"SPEAK-COMPLETE",14},3} +}; + +/** Set resource specifica data */ +static apt_bool_t synth_message_resourcify_by_id(mrcp_resource_t *resource, mrcp_message_t *message) +{ + /* associate method_name and method_id */ + if(message->start_line.message_type == MRCP_MESSAGE_TYPE_REQUEST) { + const apt_str_t *name = apt_string_table_str_get( + synth_method_string_table, + SYNTHESIZER_METHOD_COUNT, + message->start_line.method_id); + if(!name) { + return FALSE; + } + message->start_line.method_name = *name; + } + else if(message->start_line.message_type == MRCP_MESSAGE_TYPE_EVENT) { + const apt_str_t *name = apt_string_table_str_get( + synth_event_string_table, + SYNTHESIZER_EVENT_COUNT, + message->start_line.method_id); + if(!name) { + return FALSE; + } + message->start_line.method_name = *name; + } + + message->header.resource_header_accessor.vtable = + mrcp_synth_header_vtable_get(message->start_line.version); + return TRUE; +} + +/** Set resource specifica data */ +static apt_bool_t synth_message_resourcify_by_name(mrcp_resource_t *resource, mrcp_message_t *message) +{ + /* associate method_name and method_id */ + if(message->start_line.message_type == MRCP_MESSAGE_TYPE_REQUEST) { + message->start_line.method_id = apt_string_table_id_find( + synth_method_string_table, + SYNTHESIZER_METHOD_COUNT, + &message->start_line.method_name); + if(message->start_line.method_id >= SYNTHESIZER_METHOD_COUNT) { + return FALSE; + } + } + else if(message->start_line.message_type == MRCP_MESSAGE_TYPE_EVENT) { + message->start_line.method_id = apt_string_table_id_find( + synth_event_string_table, + SYNTHESIZER_EVENT_COUNT, + &message->start_line.method_name); + if(message->start_line.method_id >= SYNTHESIZER_EVENT_COUNT) { + return FALSE; + } + } + + message->header.resource_header_accessor.vtable = + mrcp_synth_header_vtable_get(message->start_line.version); + return TRUE; +} + + +/** Create MRCP synthesizer resource */ +MRCP_DECLARE(mrcp_resource_t*) mrcp_synth_resource_create(apr_pool_t *pool) +{ + mrcp_resource_t *resource = apr_palloc(pool,sizeof(mrcp_resource_t)); + mrcp_resource_init(resource); + + resource->resourcify_message_by_id = synth_message_resourcify_by_id; + resource->resourcify_message_by_name = synth_message_resourcify_by_name; + + resource->create_client_state_machine = mrcp_synth_client_state_machine_create; + resource->create_server_state_machine = mrcp_synth_server_state_machine_create; + return resource; +} diff --git a/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_server_state_machine.c b/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_server_state_machine.c new file mode 100644 index 0000000000..d569bf952c --- /dev/null +++ b/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_server_state_machine.c @@ -0,0 +1,563 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "apt_obj_list.h" +#include "apt_log.h" +#include "mrcp_state_machine.h" +#include "mrcp_synth_state_machine.h" +#include "mrcp_synth_header.h" +#include "mrcp_generic_header.h" +#include "mrcp_synth_resource.h" +#include "mrcp_message.h" + +/** MRCP synthesizer states */ +typedef enum { + SYNTHESIZER_STATE_IDLE, + SYNTHESIZER_STATE_SPEAKING, + SYNTHESIZER_STATE_PAUSED, + + SYNTHESIZER_STATE_COUNT +} mrcp_synth_state_e; + +static const char * state_names[SYNTHESIZER_STATE_COUNT] = { + "IDLE", + "SPEAKING", + "PAUSED" +}; + +typedef struct mrcp_synth_state_machine_t mrcp_synth_state_machine_t; +struct mrcp_synth_state_machine_t { + /** state machine base */ + mrcp_state_machine_t base; + /** synthesizer state */ + mrcp_synth_state_e state; + /** indicate whether active_request was processed from pending request queue */ + apt_bool_t is_pending; + /** request sent to synthesizer engine and waiting for the response to be received */ + mrcp_message_t *active_request; + /** in-progress speak request */ + mrcp_message_t *speaker; + /** queue of pending speak requests */ + apt_obj_list_t *queue; + /** properties used in set/get params */ + mrcp_message_header_t properties; +}; + +typedef apt_bool_t (*synth_method_f)(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message); + +static APR_INLINE apt_bool_t synth_request_dispatch(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + state_machine->active_request = message; + return state_machine->base.dispatcher(&state_machine->base,message); +} + +static APR_INLINE apt_bool_t synth_response_dispatch(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + state_machine->active_request = NULL; + return state_machine->base.dispatcher(&state_machine->base,message); +} + +static APR_INLINE apt_bool_t synth_event_dispatch(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + return state_machine->base.dispatcher(&state_machine->base,message); +} + +static APR_INLINE void synth_state_change(mrcp_synth_state_machine_t *state_machine, mrcp_synth_state_e state) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"State Transition %s -> %s",state_names[state_machine->state],state_names[state]); + state_machine->state = state; + if(state == SYNTHESIZER_STATE_IDLE) { + state_machine->speaker = NULL; + } +} + + +static apt_bool_t synth_request_set_params(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process SET-PARAMS Request [%d]",message->start_line.request_id); + mrcp_message_header_set(&state_machine->properties,&message->header,message->pool); + return synth_request_dispatch(state_machine,message); +} + +static apt_bool_t synth_response_set_params(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process SET-PARAMS Response [%d]",message->start_line.request_id); + return synth_response_dispatch(state_machine,message); +} + +static apt_bool_t synth_request_get_params(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process GET-PARAMS Request [%d]",message->start_line.request_id); + return synth_request_dispatch(state_machine,message); +} + +static apt_bool_t synth_response_get_params(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process GET-PARAMS Response [%d]",message->start_line.request_id); + mrcp_message_header_set(&message->header,&state_machine->active_request->header,message->pool); + mrcp_message_header_get(&message->header,&state_machine->properties,message->pool); + return synth_response_dispatch(state_machine,message); +} + +static apt_bool_t synth_request_speak(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + mrcp_message_header_inherit(&message->header,&state_machine->properties,message->pool); + if(state_machine->speaker) { + mrcp_message_t *response; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Queue Up SPEAK Request [%d]",message->start_line.request_id); + message->start_line.request_state = MRCP_REQUEST_STATE_PENDING; + apt_list_push_back(state_machine->queue,message,message->pool); + + response = mrcp_response_create(message,message->pool); + response->start_line.request_state = MRCP_REQUEST_STATE_PENDING; + return synth_response_dispatch(state_machine,response); + } + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process SPEAK Request [%d]",message->start_line.request_id); + return synth_request_dispatch(state_machine,message); +} + +static apt_bool_t synth_response_speak(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process SPEAK Response [%d]",message->start_line.request_id); + if(message->start_line.request_state == MRCP_REQUEST_STATE_INPROGRESS) { + state_machine->speaker = state_machine->active_request; + synth_state_change(state_machine,SYNTHESIZER_STATE_SPEAKING); + } + if(state_machine->is_pending == TRUE) { + mrcp_message_t *event_message = mrcp_event_create( + state_machine->active_request, + SYNTHESIZER_SPEECH_MARKER, + state_machine->active_request->pool); + event_message->start_line.request_state = MRCP_REQUEST_STATE_INPROGRESS; + state_machine->is_pending = FALSE; + /* not to send the response for pending request, instead send SPEECH-MARKER event */ + return synth_event_dispatch(state_machine,event_message); + } + return synth_response_dispatch(state_machine,message); +} + +static apt_bool_t synth_pending_requests_remove(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *request_message, mrcp_message_t *response_message) +{ + apt_list_elem_t *elem; + mrcp_message_t *pending_message; + mrcp_request_id_list_t *request_id_list = NULL; + mrcp_generic_header_t *generic_header = mrcp_generic_header_get(request_message); + mrcp_generic_header_t *response_generic_header = mrcp_generic_header_prepare(response_message); + if(generic_header && mrcp_generic_header_property_check(request_message,GENERIC_HEADER_ACTIVE_REQUEST_ID_LIST) == TRUE) { + if(generic_header->active_request_id_list.ids && generic_header->active_request_id_list.count) { + /* selective STOP request */ + request_id_list = &generic_header->active_request_id_list; + } + } + + elem = apt_list_first_elem_get(state_machine->queue); + while(elem) { + pending_message = apt_list_elem_object_get(elem); + if(!request_id_list || active_request_id_list_find(generic_header,pending_message->start_line.request_id) == TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Remove Pending SPEAK Request [%d]",pending_message->start_line.request_id); + elem = apt_list_elem_remove(state_machine->queue,elem); + /* append active id list */ + active_request_id_list_append(response_generic_header,pending_message->start_line.request_id); + } + else { + /* speak request remains in the queue, just proceed to the next one */ + elem = apt_list_next_elem_get(state_machine->queue,elem); + } + } + if(response_generic_header->active_request_id_list.count) { + mrcp_generic_header_property_add(response_message,GENERIC_HEADER_ACTIVE_REQUEST_ID_LIST); + } + return TRUE; +} + +static apt_bool_t synth_request_stop(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + mrcp_message_t *response_message; + if(state_machine->speaker) { + mrcp_request_id_list_t *request_id_list = NULL; + mrcp_generic_header_t *generic_header = mrcp_generic_header_get(message); + if(generic_header && mrcp_generic_header_property_check(message,GENERIC_HEADER_ACTIVE_REQUEST_ID_LIST) == TRUE) { + if(generic_header->active_request_id_list.ids && generic_header->active_request_id_list.count) { + /* selective STOP request */ + request_id_list = &generic_header->active_request_id_list; + } + } + + if(!request_id_list || active_request_id_list_find(generic_header,state_machine->speaker->start_line.request_id) == TRUE) { + /* found in-progress SPEAK request, stop it */ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process STOP Request [%d]",message->start_line.request_id); + return synth_request_dispatch(state_machine,message); + } + } + + /* found no in-progress SPEAK request, sending immediate response */ + response_message = mrcp_response_create(message,message->pool); + synth_pending_requests_remove(state_machine,message,response_message); + return synth_response_dispatch(state_machine,response_message); +} + +static apt_bool_t synth_response_stop(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + mrcp_message_t *pending_request; + mrcp_generic_header_t *generic_header = mrcp_generic_header_prepare(message); + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process STOP Response [%d]",message->start_line.request_id); + /* append active id list */ + active_request_id_list_append(generic_header,state_machine->speaker->start_line.request_id); + mrcp_generic_header_property_add(message,GENERIC_HEADER_ACTIVE_REQUEST_ID_LIST); + synth_pending_requests_remove(state_machine,state_machine->active_request,message); + synth_state_change(state_machine,SYNTHESIZER_STATE_IDLE); + synth_response_dispatch(state_machine,message); + + /* process pending SPEAK requests / if any */ + pending_request = apt_list_pop_front(state_machine->queue); + if(pending_request) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process Pending SPEAK Request [%d]",pending_request->start_line.request_id); + state_machine->is_pending = TRUE; + synth_request_dispatch(state_machine,pending_request); + } + return TRUE; +} + +static apt_bool_t synth_request_pause(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + if(state_machine->speaker) { + /* speaking or paused state */ + if(state_machine->state == SYNTHESIZER_STATE_SPEAKING) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process PAUSE Request [%d]",state_machine->speaker->start_line.request_id); + synth_request_dispatch(state_machine,message); + } + else { + /* paused state */ + mrcp_message_t *response_message = mrcp_response_create(message,message->pool); + synth_response_dispatch(state_machine,response_message); + } + } + else { + /* idle state */ + mrcp_message_t *response_message = mrcp_response_create(message,message->pool); + response_message->start_line.status_code = MRCP_STATUS_CODE_METHOD_NOT_VALID; + synth_response_dispatch(state_machine,response_message); + } + return TRUE; +} + +static apt_bool_t synth_response_pause(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process PAUSE Response [%d]",message->start_line.request_id); + if(message->start_line.status_code == MRCP_STATUS_CODE_SUCCESS) { + mrcp_generic_header_t *generic_header = mrcp_generic_header_prepare(message); + /* append active id list */ + active_request_id_list_append(generic_header,state_machine->speaker->start_line.request_id); + mrcp_generic_header_property_add(message,GENERIC_HEADER_ACTIVE_REQUEST_ID_LIST); + synth_state_change(state_machine,SYNTHESIZER_STATE_PAUSED); + } + synth_response_dispatch(state_machine,message); + return TRUE; +} + +static apt_bool_t synth_request_resume(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + if(state_machine->speaker) { + /* speaking or paused state */ + if(state_machine->state == SYNTHESIZER_STATE_PAUSED) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process RESUME Request [%d]",state_machine->speaker->start_line.request_id); + synth_request_dispatch(state_machine,message); + } + else { + /* speaking state */ + mrcp_message_t *response_message = mrcp_response_create(message,message->pool); + synth_response_dispatch(state_machine,response_message); + } + } + else { + /* idle state */ + mrcp_message_t *response_message = mrcp_response_create(message,message->pool); + response_message->start_line.status_code = MRCP_STATUS_CODE_METHOD_NOT_VALID; + synth_response_dispatch(state_machine,response_message); + } + return TRUE; +} + +static apt_bool_t synth_response_resume(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process RESUME Response [%d]",message->start_line.request_id); + if(message->start_line.status_code == MRCP_STATUS_CODE_SUCCESS) { + mrcp_generic_header_t *generic_header = mrcp_generic_header_prepare(message); + /* append active id list */ + active_request_id_list_append(generic_header,state_machine->speaker->start_line.request_id); + mrcp_generic_header_property_add(message,GENERIC_HEADER_ACTIVE_REQUEST_ID_LIST); + synth_state_change(state_machine,SYNTHESIZER_STATE_SPEAKING); + } + synth_response_dispatch(state_machine,message); + return TRUE; +} + +static apt_bool_t synth_request_barge_in_occurred(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + mrcp_message_t *response_message; + if(state_machine->speaker) { + apt_bool_t kill_on_barge_in = TRUE; + mrcp_synth_header_t *synth_header = mrcp_resource_header_get(message); + if(synth_header) { + if(mrcp_resource_header_property_check(message,SYNTHESIZER_HEADER_KILL_ON_BARGE_IN) == TRUE) { + kill_on_barge_in = synth_header->kill_on_barge_in; + } + } + + if(kill_on_barge_in == TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process BARGE-IN Request [%d]",message->start_line.request_id); + return synth_request_dispatch(state_machine,message); + } + } + + /* found no kill-on-bargein enabled in-progress SPEAK request, sending immediate response */ + response_message = mrcp_response_create(message,message->pool); + return synth_response_dispatch(state_machine,response_message); +} + +static apt_bool_t synth_response_barge_in_occurred(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + mrcp_generic_header_t *generic_header = mrcp_generic_header_prepare(message); + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process BARGE-IN Response [%d]",message->start_line.request_id); + /* append active id list */ + active_request_id_list_append(generic_header,state_machine->speaker->start_line.request_id); + mrcp_generic_header_property_add(message,GENERIC_HEADER_ACTIVE_REQUEST_ID_LIST); + synth_pending_requests_remove(state_machine,state_machine->active_request,message); + synth_state_change(state_machine,SYNTHESIZER_STATE_IDLE); + return synth_response_dispatch(state_machine,message); +} + +static apt_bool_t synth_request_control(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + mrcp_message_t *response_message; + if(state_machine->state == SYNTHESIZER_STATE_SPEAKING) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process CONTROL Request [%d]",message->start_line.request_id); + return synth_request_dispatch(state_machine,message); + } + + /* found no in-progress SPEAK request, sending immediate response */ + response_message = mrcp_response_create(message,message->pool); + return synth_response_dispatch(state_machine,response_message); +} + +static apt_bool_t synth_response_control(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + mrcp_generic_header_t *generic_header = mrcp_generic_header_prepare(message); + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process CONTROL Response [%d]",message->start_line.request_id); + /* append active id list */ + active_request_id_list_append(generic_header,state_machine->speaker->start_line.request_id); + mrcp_generic_header_property_add(message,GENERIC_HEADER_ACTIVE_REQUEST_ID_LIST); + return synth_response_dispatch(state_machine,message); +} + +static apt_bool_t synth_request_define_lexicon(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + mrcp_message_t *response_message; + if(state_machine->state == SYNTHESIZER_STATE_IDLE) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process DEFINE-LEXICON Request [%d]",message->start_line.request_id); + return synth_request_dispatch(state_machine,message); + } + + /* sending failure response */ + response_message = mrcp_response_create(message,message->pool); + response_message->start_line.status_code = MRCP_STATUS_CODE_METHOD_NOT_VALID; + return synth_response_dispatch(state_machine,response_message); +} + +static apt_bool_t synth_response_define_lexicon(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process DEFINE-LEXICON Response [%d]",message->start_line.request_id); + return synth_response_dispatch(state_machine,message); +} + +static apt_bool_t synth_event_speech_marker(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + if(!state_machine->speaker) { + /* unexpected event, no in-progress speak request */ + return FALSE; + } + + if(state_machine->speaker->start_line.request_id != message->start_line.request_id) { + /* unexpected event */ + return FALSE; + } + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process SPEECH-MARKER Event [%d]",message->start_line.request_id); + message->start_line.request_state = MRCP_REQUEST_STATE_INPROGRESS; + return synth_event_dispatch(state_machine,message); +} + +static apt_bool_t synth_event_speak_complete(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + mrcp_message_t *pending_request; + if(!state_machine->speaker) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Unexpected SPEAK-COMPLETE Event [%d]",message->start_line.request_id); + return FALSE; + } + + if(state_machine->speaker->start_line.request_id != message->start_line.request_id) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Unexpected SPEAK-COMPLETE Event [%d]",message->start_line.request_id); + return FALSE; + } + + if(state_machine->active_request && state_machine->active_request->start_line.method_id == SYNTHESIZER_STOP) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Ignore SPEAK-COMPLETE Event [%d]: waiting for STOP response",message->start_line.request_id); + return FALSE; + } + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process SPEAK-COMPLETE Event [%d]",message->start_line.request_id); + if(mrcp_resource_header_property_check(message,SYNTHESIZER_HEADER_COMPLETION_CAUSE) != TRUE) { + mrcp_synth_header_t *synth_header = mrcp_resource_header_prepare(message); + synth_header->completion_cause = SYNTHESIZER_COMPLETION_CAUSE_NORMAL; + mrcp_resource_header_property_add(message,SYNTHESIZER_HEADER_COMPLETION_CAUSE); + } + synth_state_change(state_machine,SYNTHESIZER_STATE_IDLE); + synth_event_dispatch(state_machine,message); + + /* process pending SPEAK requests */ + pending_request = apt_list_pop_front(state_machine->queue); + if(pending_request) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process Pending SPEAK Request [%d]",pending_request->start_line.request_id); + state_machine->is_pending = TRUE; + synth_request_dispatch(state_machine,pending_request); + } + return TRUE; +} + +static synth_method_f synth_request_method_array[SYNTHESIZER_METHOD_COUNT] = { + synth_request_set_params, + synth_request_get_params, + synth_request_speak, + synth_request_stop, + synth_request_pause, + synth_request_resume, + synth_request_barge_in_occurred, + synth_request_control, + synth_request_define_lexicon +}; + +static synth_method_f synth_response_method_array[SYNTHESIZER_METHOD_COUNT] = { + synth_response_set_params, + synth_response_get_params, + synth_response_speak, + synth_response_stop, + synth_response_pause, + synth_response_resume, + synth_response_barge_in_occurred, + synth_response_control, + synth_response_define_lexicon, +}; + +static synth_method_f synth_event_method_array[SYNTHESIZER_EVENT_COUNT] = { + synth_event_speech_marker, + synth_event_speak_complete +}; + +/** Update state according to received incoming request from MRCP client */ +static apt_bool_t synth_request_state_update(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + synth_method_f method; + if(message->start_line.method_id >= SYNTHESIZER_METHOD_COUNT) { + return FALSE; + } + + method = synth_request_method_array[message->start_line.method_id]; + if(method) { + return method(state_machine,message); + } + return synth_request_dispatch(state_machine,message); +} + +/** Update state according to received outgoing response from synthesizer engine */ +static apt_bool_t synth_response_state_update(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + synth_method_f method; + if(!state_machine->active_request) { + /* unexpected response, no active request waiting for response */ + return FALSE; + } + if(state_machine->active_request->start_line.request_id != message->start_line.request_id) { + /* unexpected response, request id doesn't match */ + return FALSE; + } + + if(message->start_line.method_id >= SYNTHESIZER_METHOD_COUNT) { + return FALSE; + } + + method = synth_response_method_array[message->start_line.method_id]; + if(method) { + return method(state_machine,message); + } + return synth_response_dispatch(state_machine,message); +} + +/** Update state according to received outgoing event from synthesizer engine */ +static apt_bool_t synth_event_state_update(mrcp_synth_state_machine_t *state_machine, mrcp_message_t *message) +{ + synth_method_f method; + if(message->start_line.method_id >= SYNTHESIZER_EVENT_COUNT) { + return FALSE; + } + + method = synth_event_method_array[message->start_line.method_id]; + if(method) { + return method(state_machine,message); + } + return synth_event_dispatch(state_machine,message); +} + +/** Update state according to request received from MRCP client or response/event received from synthesizer engine */ +static apt_bool_t synth_state_update(mrcp_state_machine_t *state_machine, mrcp_message_t *message) +{ + mrcp_synth_state_machine_t *synth_state_machine = (mrcp_synth_state_machine_t*)state_machine; + apt_bool_t status = TRUE; + switch(message->start_line.message_type) { + case MRCP_MESSAGE_TYPE_REQUEST: + status = synth_request_state_update(synth_state_machine,message); + break; + case MRCP_MESSAGE_TYPE_RESPONSE: + status = synth_response_state_update(synth_state_machine,message); + break; + case MRCP_MESSAGE_TYPE_EVENT: + status = synth_event_state_update(synth_state_machine,message); + break; + default: + status = FALSE; + break; + } + return status; +} + +/** Create MRCP synthesizer server state machine */ +mrcp_state_machine_t* mrcp_synth_server_state_machine_create(void *obj, mrcp_message_dispatcher_f dispatcher, mrcp_version_e version, apr_pool_t *pool) +{ + mrcp_message_header_t *properties; + mrcp_synth_state_machine_t *state_machine = apr_palloc(pool,sizeof(mrcp_synth_state_machine_t)); + mrcp_state_machine_init(&state_machine->base,obj,dispatcher); + state_machine->base.update = synth_state_update; + state_machine->state = SYNTHESIZER_STATE_IDLE; + state_machine->is_pending = FALSE; + state_machine->active_request = NULL; + state_machine->speaker = NULL; + state_machine->queue = apt_list_create(pool); + properties = &state_machine->properties; + mrcp_message_header_init(properties); + properties->generic_header_accessor.vtable = mrcp_generic_header_vtable_get(version); + properties->resource_header_accessor.vtable = mrcp_synth_header_vtable_get(version); + return &state_machine->base; +} diff --git a/libs/unimrcp/libs/mrcpv2-transport/Makefile.am b/libs/unimrcp/libs/mrcpv2-transport/Makefile.am new file mode 100644 index 0000000000..e86055da62 --- /dev/null +++ b/libs/unimrcp/libs/mrcpv2-transport/Makefile.am @@ -0,0 +1,21 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_srcdir)/libs/mrcpv2-transport/include \ + -I$(top_srcdir)/libs/mrcp/include \ + -I$(top_srcdir)/libs/mrcp/message/include \ + -I$(top_srcdir)/libs/mrcp/control/include \ + -I$(top_srcdir)/libs/apr-toolkit/include \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) + +noinst_LTLIBRARIES = libmrcpv2transport.la + +include_HEADERS = include/mrcp_connection_types.h \ + include/mrcp_control_descriptor.h \ + include/mrcp_connection.h \ + include/mrcp_client_connection.h \ + include/mrcp_server_connection.h + +libmrcpv2transport_la_SOURCES = src/mrcp_control_descriptor.c \ + src/mrcp_connection.c \ + src/mrcp_client_connection.c \ + src/mrcp_server_connection.c diff --git a/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_client_connection.h b/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_client_connection.h new file mode 100644 index 0000000000..29181692cc --- /dev/null +++ b/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_client_connection.h @@ -0,0 +1,137 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_CLIENT_CONNECTION_H__ +#define __MRCP_CLIENT_CONNECTION_H__ + +/** + * @file mrcp_client_connection.h + * @brief MRCPv2 Client Connection + */ + +#include "apt_task.h" +#include "mrcp_connection_types.h" + +APT_BEGIN_EXTERN_C + +/** + * Create connection agent. + * @param max_connection_count the number of max MRCPv2 connections + * @param offer_new_connection the connection establishment policy in o/a + * @param pool the pool to allocate memory from + */ +MRCP_DECLARE(mrcp_connection_agent_t*) mrcp_client_connection_agent_create( + apr_size_t max_connection_count, + apt_bool_t offer_new_connection, + apr_pool_t *pool); + +/** + * Destroy connection agent. + * @param agent the agent to destroy + */ +MRCP_DECLARE(apt_bool_t) mrcp_client_connection_agent_destroy(mrcp_connection_agent_t *agent); + +/** + * Start connection agent and wait for incoming requests. + * @param agent the agent to start + */ +MRCP_DECLARE(apt_bool_t) mrcp_client_connection_agent_start(mrcp_connection_agent_t *agent); + +/** + * Terminate connection agent. + * @param agent the agent to terminate + */ +MRCP_DECLARE(apt_bool_t) mrcp_client_connection_agent_terminate(mrcp_connection_agent_t *agent); + + +/** + * Set connection event handler. + * @param agent the agent to set event hadler for + * @param obj the external object to associate with the agent + * @param vtable the event handler virtual methods + */ +MRCP_DECLARE(void) mrcp_client_connection_agent_handler_set( + mrcp_connection_agent_t *agent, + void *obj, + const mrcp_connection_event_vtable_t *vtable); + +/** + * Set MRCP resource factory. + * @param agent the agent to set resource factory for + * @param resource_factory the MRCP resource factory to set + */ +MRCP_DECLARE(void) mrcp_client_connection_resource_factory_set( + mrcp_connection_agent_t *agent, + mrcp_resource_factory_t *resource_factory); + +/** + * Get task. + * @param agent the agent to get task from + */ +MRCP_DECLARE(apt_task_t*) mrcp_client_connection_agent_task_get(mrcp_connection_agent_t *agent); + +/** + * Get external object. + * @param agent the agent to get object from + */ +MRCP_DECLARE(void*) mrcp_client_connection_agent_object_get(mrcp_connection_agent_t *agent); + + +/** + * Create control channel. + * @param agent the agent to create channel for + * @param obj the external object to associate with the control channel + * @param pool the pool to allocate memory from + */ +MRCP_DECLARE(mrcp_control_channel_t*) mrcp_client_control_channel_create(mrcp_connection_agent_t *agent, void *obj, apr_pool_t *pool); + +/** + * Add MRCPv2 control channel. + * @param channel the control channel to add + * @param descriptor the control descriptor + */ +MRCP_DECLARE(apt_bool_t) mrcp_client_control_channel_add(mrcp_control_channel_t *channel, mrcp_control_descriptor_t *descriptor); + +/** + * Modify MRCPv2 control channel. + * @param channel the control channel to modify + * @param descriptor the control descriptor + */ +MRCP_DECLARE(apt_bool_t) mrcp_client_control_channel_modify(mrcp_control_channel_t *channel, mrcp_control_descriptor_t *descriptor); + +/** + * Remove MRCPv2 control channel. + * @param channel the control channel to remove + */ +MRCP_DECLARE(apt_bool_t) mrcp_client_control_channel_remove(mrcp_control_channel_t *channel); + +/** + * Destroy MRCPv2 control channel. + * @param channel the control channel to destroy + */ +MRCP_DECLARE(apt_bool_t) mrcp_client_control_channel_destroy(mrcp_control_channel_t *channel); + +/** + * Send MRCPv2 message. + * @param channel the control channel to send message through + * @param message the message to send + */ +MRCP_DECLARE(apt_bool_t) mrcp_client_control_message_send(mrcp_control_channel_t *channel, mrcp_message_t *message); + + +APT_END_EXTERN_C + +#endif /*__MRCP_CLIENT_CONNECTION_H__*/ diff --git a/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_connection.h b/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_connection.h new file mode 100644 index 0000000000..bcef530f87 --- /dev/null +++ b/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_connection.h @@ -0,0 +1,97 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_CONNECTION_H__ +#define __MRCP_CONNECTION_H__ + +/** + * @file mrcp_connection.h + * @brief MRCP Connection + */ + +#include +#include +#include "apt_obj_list.h" +#include "mrcp_connection_types.h" +#include "mrcp_stream.h" + +APT_BEGIN_EXTERN_C + +/** Size of the buffer used for MRCP rx/tx stream */ +#define MRCP_STREAM_BUFFER_SIZE 1024 + +/** MRCPv2 connection */ +struct mrcp_connection_t { + /** Memory pool */ + apr_pool_t *pool; + + /** Accepted/Connected socket */ + apr_socket_t *sock; + /** Socket poll descriptor */ + apr_pollfd_t sock_pfd; + /** Local sockaddr */ + apr_sockaddr_t *l_sockaddr; + /** Remote sockaddr */ + apr_sockaddr_t *r_sockaddr; + /** Remote IP */ + apt_str_t remote_ip; + /** String identifier used for traces */ + const char *id; + + /** Reference count */ + apr_size_t access_count; + /** Agent list element */ + apt_list_elem_t *it; + /** Opaque agent */ + void *agent; + + /** Table of control channels */ + apr_hash_t *channel_table; + + /** Rx buffer */ + char rx_buffer[MRCP_STREAM_BUFFER_SIZE]; + /** Rx stream */ + apt_text_stream_t rx_stream; + /** MRCP parser to parser MRCP messages out of rx stream */ + mrcp_parser_t *parser; + + /** Tx buffer */ + char tx_buffer[MRCP_STREAM_BUFFER_SIZE]; + /** Tx stream */ + apt_text_stream_t tx_stream; + /** MRCP generator to generate MRCP messages out of tx stream */ + mrcp_generator_t *generator; +}; + +/** Create MRCP connection. */ +mrcp_connection_t* mrcp_connection_create(); + +/** Destroy MRCP connection. */ +void mrcp_connection_destroy(mrcp_connection_t *connection); + +/** Add Control Channel to MRCP connection. */ +apt_bool_t mrcp_connection_channel_add(mrcp_connection_t *connection, mrcp_control_channel_t *channel); + +/** Find Control Channel by Channel Identifier. */ +mrcp_control_channel_t* mrcp_connection_channel_find(mrcp_connection_t *connection, const apt_str_t *identifier); + +/** Remove Control Channel from MRCP connection. */ +apt_bool_t mrcp_connection_channel_remove(mrcp_connection_t *connection, mrcp_control_channel_t *channel); + + +APT_END_EXTERN_C + +#endif /*__MRCP_CONNECTION_H__*/ diff --git a/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_connection_types.h b/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_connection_types.h new file mode 100644 index 0000000000..5374451216 --- /dev/null +++ b/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_connection_types.h @@ -0,0 +1,126 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_CONNECTION_TYPES_H__ +#define __MRCP_CONNECTION_TYPES_H__ + +/** + * @file mrcp_connection_types.h + * @brief MRCP Connection Types Declaration + */ + +#include +#include "apt_string.h" +#include "mrcp_types.h" + +APT_BEGIN_EXTERN_C + +/** Opaque MRCPv2 control descriptor declaration */ +typedef struct mrcp_control_descriptor_t mrcp_control_descriptor_t; + +/** Opaque MRCPv2 connection declaration */ +typedef struct mrcp_connection_t mrcp_connection_t; + +/** Opaque MRCPv2 control channel declaration */ +typedef struct mrcp_control_channel_t mrcp_control_channel_t; + +/** Opaque MRCPv2 connection agent declaration */ +typedef struct mrcp_connection_agent_t mrcp_connection_agent_t; + +/** MRCPv2 connection event vtable declaration */ +typedef struct mrcp_connection_event_vtable_t mrcp_connection_event_vtable_t; + +/** MRCPv2 connection event vtable */ +struct mrcp_connection_event_vtable_t { + /** Channel add event handler */ + apt_bool_t (*on_add)(mrcp_control_channel_t *channel, mrcp_control_descriptor_t *descriptor, apt_bool_t status); + /** Channel modify event handler */ + apt_bool_t (*on_modify)(mrcp_control_channel_t *channel, mrcp_control_descriptor_t *descriptor, apt_bool_t status); + /** Channel remove event handler */ + apt_bool_t (*on_remove)(mrcp_control_channel_t *channel, apt_bool_t status); + /** Message receive event handler */ + apt_bool_t (*on_receive)(mrcp_control_channel_t *channel, mrcp_message_t *message); +}; + +/** MRCPv2 control channel */ +struct mrcp_control_channel_t { + /** MRCPv2 Connection agent */ + mrcp_connection_agent_t *agent; + /** MRCPv2 (shared) connection */ + mrcp_connection_t *connection; + /** Indicate removed connection (safe to destroy) */ + apt_bool_t removed; + /** External object associated with the channel */ + void *obj; + /** Pool to allocate memory from */ + apr_pool_t *pool; + /** Channel identifier (id at resource) */ + apt_str_t identifier; +}; + +/** Send channel add response */ +static APR_INLINE apt_bool_t mrcp_control_channel_add_respond( + const mrcp_connection_event_vtable_t *vtable, + mrcp_control_channel_t *channel, + mrcp_control_descriptor_t *descriptor, + apt_bool_t status) +{ + if(vtable && vtable->on_add) { + return vtable->on_add(channel,descriptor,status); + } + return FALSE; +} + +/** Send channel modify response */ +static APR_INLINE apt_bool_t mrcp_control_channel_modify_respond( + const mrcp_connection_event_vtable_t *vtable, + mrcp_control_channel_t *channel, + mrcp_control_descriptor_t *descriptor, + apt_bool_t status) +{ + if(vtable && vtable->on_modify) { + return vtable->on_modify(channel,descriptor,status); + } + return FALSE; +} + +/** Send channel remove response */ +static APR_INLINE apt_bool_t mrcp_control_channel_remove_respond( + const mrcp_connection_event_vtable_t *vtable, + mrcp_control_channel_t *channel, + apt_bool_t status) +{ + if(vtable && vtable->on_remove) { + return vtable->on_remove(channel,status); + } + return FALSE; +} + +/** Send MRCP message receive event */ +static APR_INLINE apt_bool_t mrcp_connection_message_receive( + const mrcp_connection_event_vtable_t *vtable, + mrcp_control_channel_t *channel, + mrcp_message_t *message) +{ + if(vtable && vtable->on_receive) { + return vtable->on_receive(channel,message); + } + return FALSE; +} + +APT_END_EXTERN_C + +#endif /*__MRCP_CONNECTION_TYPES_H__*/ diff --git a/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_control_descriptor.h b/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_control_descriptor.h new file mode 100644 index 0000000000..a9323ae759 --- /dev/null +++ b/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_control_descriptor.h @@ -0,0 +1,144 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_CONTROL_DESCRIPTOR_H__ +#define __MRCP_CONTROL_DESCRIPTOR_H__ + +/** + * @file mrcp_control_descriptor.h + * @brief MRCPv2 Control Descriptor + */ + +#include "apt_string.h" +#include "mrcp_connection_types.h" + +APT_BEGIN_EXTERN_C + +/** MRCPv2 proto transport */ +typedef enum { + MRCP_PROTO_TCP, + MRCP_PROTO_TLS, + + MRCP_PROTO_COUNT, + MRCP_PROTO_UNKNOWN = MRCP_PROTO_COUNT +}mrcp_proto_type_e; + + +/** MRCPv2 attributes */ +typedef enum { + MRCP_ATTRIB_SETUP, + MRCP_ATTRIB_CONNECTION, + MRCP_ATTRIB_RESOURCE, + MRCP_ATTRIB_CHANNEL, + MRCP_ATTRIB_CMID, + + MRCP_ATTRIB_COUNT, + MRCP_ATTRIB_UNKNOWN = MRCP_ATTRIB_COUNT +}mrcp_attrib_e; + + +/** MRCPv2 setup attributes */ +typedef enum { + MRCP_SETUP_TYPE_ACTIVE, + MRCP_SETUP_TYPE_PASSIVE, + + MRCP_SETUP_TYPE_COUNT, + MRCP_SETUP_TYPE_UNKNOWN = MRCP_SETUP_TYPE_COUNT +} mrcp_setup_type_e; + +/** MRCPv2 connection attributes */ +typedef enum { + MRCP_CONNECTION_TYPE_NEW, + MRCP_CONNECTION_TYPE_EXISTING, + + MRCP_CONNECTION_TYPE_COUNT, + MRCP_CONNECTION_TYPE_UNKNOWN = MRCP_CONNECTION_TYPE_COUNT +} mrcp_connection_type_e; + + +/** MRCPv2 control descriptor */ +struct mrcp_control_descriptor_t { + /** IP address */ + apt_str_t ip; + /** Port */ + apr_port_t port; + /** Protocol type */ + mrcp_proto_type_e proto; + /** Setup type */ + mrcp_setup_type_e setup_type; + /** Connection type */ + mrcp_connection_type_e connection_type; + /** Resource name */ + apt_str_t resource_name; + /** Session identifier */ + apt_str_t session_id; + /** Control media identifier */ + apr_size_t cmid; + /** Base identifier */ + apr_size_t id; +}; + +/** Initialize MRCP control descriptor */ +static APR_INLINE void mrcp_control_descriptor_init(mrcp_control_descriptor_t *descriptor) +{ + apt_string_reset(&descriptor->ip); + descriptor->port = 0; + descriptor->proto = MRCP_PROTO_UNKNOWN; + descriptor->setup_type = MRCP_SETUP_TYPE_UNKNOWN; + descriptor->connection_type = MRCP_CONNECTION_TYPE_UNKNOWN; + apt_string_reset(&descriptor->resource_name); + apt_string_reset(&descriptor->session_id); + descriptor->cmid = 0; + descriptor->id = 0; +} + +/** Create MRCP control offer */ +MRCP_DECLARE(mrcp_control_descriptor_t*) mrcp_control_offer_create(apr_pool_t *pool); + +/** Create MRCP control answer */ +MRCP_DECLARE(mrcp_control_descriptor_t*) mrcp_control_answer_create(mrcp_control_descriptor_t *offer, apr_pool_t *pool); + +/** Get MRCP protocol transport name by identifier */ +MRCP_DECLARE(const apt_str_t*) mrcp_proto_get(mrcp_proto_type_e proto); + +/** Find MRCP protocol transport identifier by name */ +MRCP_DECLARE(mrcp_proto_type_e) mrcp_proto_find(const apt_str_t *attrib); + + +/** Get MRCP attribute name by identifier */ +MRCP_DECLARE(const apt_str_t*) mrcp_attrib_str_get(mrcp_attrib_e attrib_id); + +/** Find MRCP attribute identifier by name */ +MRCP_DECLARE(mrcp_attrib_e) mrcp_attrib_id_find(const apt_str_t *attrib); + + +/** Get MRCP setup type name by identifier */ +MRCP_DECLARE(const apt_str_t*) mrcp_setup_type_get(mrcp_setup_type_e setup_type); + +/** Find MRCP setup type identifier by name */ +MRCP_DECLARE(mrcp_setup_type_e) mrcp_setup_type_find(const apt_str_t *attrib); + + +/** Get MRCP connection type name by identifier */ +MRCP_DECLARE(const apt_str_t*) mrcp_connection_type_get(mrcp_connection_type_e connection_type); + +/** Find MRCP connection type identifier by name */ +MRCP_DECLARE(mrcp_connection_type_e) mrcp_connection_type_find(const apt_str_t *attrib); + + +APT_END_EXTERN_C + +#endif /*__MRCP_CONTROL_DESCRIPTOR_H__*/ diff --git a/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_server_connection.h b/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_server_connection.h new file mode 100644 index 0000000000..c72b9c5ef8 --- /dev/null +++ b/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_server_connection.h @@ -0,0 +1,140 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_SERVER_CONNECTION_H__ +#define __MRCP_SERVER_CONNECTION_H__ + +/** + * @file mrcp_server_connection.h + * @brief MRCPv2 Server Connection + */ + +#include "apt_task.h" +#include "mrcp_connection_types.h" + +APT_BEGIN_EXTERN_C + +/** + * Create connection agent. + * @param listen_ip the listen IP address + * @param listen_port the listen port + * @param max_connection_count the number of max MRCPv2 connections + * @param force_new_connection the connection establishment policy in o/a + * @param pool the pool to allocate memory from + */ +MRCP_DECLARE(mrcp_connection_agent_t*) mrcp_server_connection_agent_create( + const char *listen_ip, + apr_port_t listen_port, + apr_size_t max_connection_count, + apt_bool_t force_new_connection, + apr_pool_t *pool); + +/** + * Destroy connection agent. + * @param agent the agent to destroy + */ +MRCP_DECLARE(apt_bool_t) mrcp_server_connection_agent_destroy(mrcp_connection_agent_t *agent); + +/** + * Start connection agent and wait for incoming requests. + * @param agent the agent to start + */ +MRCP_DECLARE(apt_bool_t) mrcp_server_connection_agent_start(mrcp_connection_agent_t *agent); + +/** + * Terminate connection agent. + * @param agent the agent to terminate + */ +MRCP_DECLARE(apt_bool_t) mrcp_server_connection_agent_terminate(mrcp_connection_agent_t *agent); + +/** + * Set connection event handler. + * @param agent the agent to set event hadler for + * @param obj the external object to associate with the agent + * @param vtable the event handler virtual methods + */ +MRCP_DECLARE(void) mrcp_server_connection_agent_handler_set( + mrcp_connection_agent_t *agent, + void *obj, + const mrcp_connection_event_vtable_t *vtable); + +/** + * Set MRCP resource factory. + * @param agent the agent to set resource factory for + * @param resource_factory the MRCP resource factory to set + */ +MRCP_DECLARE(void) mrcp_server_connection_resource_factory_set( + mrcp_connection_agent_t *agent, + mrcp_resource_factory_t *resource_factory); + +/** + * Get task. + * @param agent the agent to get task from + */ +MRCP_DECLARE(apt_task_t*) mrcp_server_connection_agent_task_get(mrcp_connection_agent_t *agent); + +/** + * Get external object. + * @param agent the agent to get object from + */ +MRCP_DECLARE(void*) mrcp_server_connection_agent_object_get(mrcp_connection_agent_t *agent); + + +/** + * Create control channel. + * @param agent the agent to create channel for + * @param obj the external object to associate with the control channel + * @param pool the pool to allocate memory from + */ +MRCP_DECLARE(mrcp_control_channel_t*) mrcp_server_control_channel_create(mrcp_connection_agent_t *agent, void *obj, apr_pool_t *pool); + +/** + * Add MRCPv2 control channel. + * @param channel the control channel to add + * @param descriptor the control descriptor + */ +MRCP_DECLARE(apt_bool_t) mrcp_server_control_channel_add(mrcp_control_channel_t *channel, mrcp_control_descriptor_t *descriptor); + +/** + * Modify MRCPv2 control channel. + * @param channel the control channel to modify + * @param descriptor the control descriptor + */ +MRCP_DECLARE(apt_bool_t) mrcp_server_control_channel_modify(mrcp_control_channel_t *channel, mrcp_control_descriptor_t *descriptor); + +/** + * Remove MRCPv2 control channel. + * @param channel the control channel to remove + */ +MRCP_DECLARE(apt_bool_t) mrcp_server_control_channel_remove(mrcp_control_channel_t *channel); + +/** + * Destroy MRCPv2 control channel. + * @param channel the control channel to destroy + */ +MRCP_DECLARE(apt_bool_t) mrcp_server_control_channel_destroy(mrcp_control_channel_t *channel); + +/** + * Send MRCPv2 message. + * @param channel the control channel to send message through + * @param message the message to send + */ +MRCP_DECLARE(apt_bool_t) mrcp_server_control_message_send(mrcp_control_channel_t *channel, mrcp_message_t *message); + + +APT_END_EXTERN_C + +#endif /*__MRCP_SERVER_CONNECTION_H__*/ diff --git a/libs/unimrcp/libs/mrcpv2-transport/mrcpv2transport.vcproj b/libs/unimrcp/libs/mrcpv2-transport/mrcpv2transport.vcproj new file mode 100644 index 0000000000..de34484f18 --- /dev/null +++ b/libs/unimrcp/libs/mrcpv2-transport/mrcpv2transport.vcproj @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_client_connection.c b/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_client_connection.c new file mode 100644 index 0000000000..89f636eefa --- /dev/null +++ b/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_client_connection.c @@ -0,0 +1,636 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mrcp_connection.h" +#include "mrcp_client_connection.h" +#include "mrcp_control_descriptor.h" +#include "mrcp_resource_factory.h" +#include "mrcp_message.h" +#include "apt_text_stream.h" +#include "apt_task.h" +#include "apt_pollset.h" +#include "apt_cyclic_queue.h" +#include "apt_log.h" + +#define MRCPV2_CONNECTION_TASK_NAME "TCP/MRCPv2 Connection Agent" + +struct mrcp_connection_agent_t { + apr_pool_t *pool; + apt_task_t *task; + + mrcp_resource_factory_t *resource_factory; + + apt_obj_list_t *connection_list; + + apt_bool_t offer_new_connection; + apr_size_t max_connection_count; + + apr_thread_mutex_t *guard; + apt_cyclic_queue_t *msg_queue; + apt_pollset_t *pollset; + + void *obj; + const mrcp_connection_event_vtable_t *vtable; +}; + +typedef enum { + CONNECTION_TASK_MSG_ADD_CHANNEL, + CONNECTION_TASK_MSG_MODIFY_CHANNEL, + CONNECTION_TASK_MSG_REMOVE_CHANNEL, + CONNECTION_TASK_MSG_SEND_MESSAGE, + CONNECTION_TASK_MSG_TERMINATE +} connection_task_msg_type_e; + +typedef struct connection_task_msg_t connection_task_msg_t; +struct connection_task_msg_t { + connection_task_msg_type_e type; + mrcp_connection_agent_t *agent; + mrcp_control_channel_t *channel; + mrcp_control_descriptor_t *descriptor; + mrcp_message_t *message; +}; + + +static apt_bool_t mrcp_client_agent_task_run(apt_task_t *task); +static apt_bool_t mrcp_client_agent_task_terminate(apt_task_t *task); +static apt_bool_t mrcp_client_agent_task_on_destroy(apt_task_t *task); + +/** Create connection agent. */ +MRCP_DECLARE(mrcp_connection_agent_t*) mrcp_client_connection_agent_create( + apr_size_t max_connection_count, + apt_bool_t offer_new_connection, + apr_pool_t *pool) +{ + apt_task_vtable_t *vtable; + mrcp_connection_agent_t *agent; + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create "MRCPV2_CONNECTION_TASK_NAME" [%d]",max_connection_count); + agent = apr_palloc(pool,sizeof(mrcp_connection_agent_t)); + agent->pool = pool; + agent->pollset = NULL; + agent->max_connection_count = max_connection_count; + agent->offer_new_connection = offer_new_connection; + + agent->task = apt_task_create(agent,NULL,pool); + if(!agent->task) { + return NULL; + } + + apt_task_name_set(agent->task,MRCPV2_CONNECTION_TASK_NAME); + vtable = apt_task_vtable_get(agent->task); + if(vtable) { + vtable->run = mrcp_client_agent_task_run; + vtable->terminate = mrcp_client_agent_task_terminate; + vtable->destroy = mrcp_client_agent_task_on_destroy; + } + + agent->connection_list = apt_list_create(pool); + + agent->msg_queue = apt_cyclic_queue_create(CYCLIC_QUEUE_DEFAULT_SIZE); + apr_thread_mutex_create(&agent->guard,APR_THREAD_MUTEX_UNNESTED,pool); + return agent; +} + +/** Virtual destroy handler. */ +static apt_bool_t mrcp_client_agent_task_on_destroy(apt_task_t *task) +{ + mrcp_connection_agent_t *agent = apt_task_object_get(task); + if(agent->guard) { + apr_thread_mutex_destroy(agent->guard); + agent->guard = NULL; + } + if(agent->msg_queue) { + apt_cyclic_queue_destroy(agent->msg_queue); + agent->msg_queue = NULL; + } + return TRUE; +} + +/** Destroy connection agent. */ +MRCP_DECLARE(apt_bool_t) mrcp_client_connection_agent_destroy(mrcp_connection_agent_t *agent) +{ + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Destroy MRCPv2 Agent"); + return apt_task_destroy(agent->task); +} + +/** Start connection agent. */ +MRCP_DECLARE(apt_bool_t) mrcp_client_connection_agent_start(mrcp_connection_agent_t *agent) +{ + return apt_task_start(agent->task); +} + +/** Terminate connection agent. */ +MRCP_DECLARE(apt_bool_t) mrcp_client_connection_agent_terminate(mrcp_connection_agent_t *agent) +{ + return apt_task_terminate(agent->task,TRUE); +} + +/** Set connection event handler. */ +MRCP_DECLARE(void) mrcp_client_connection_agent_handler_set( + mrcp_connection_agent_t *agent, + void *obj, + const mrcp_connection_event_vtable_t *vtable) +{ + agent->obj = obj; + agent->vtable = vtable; +} + +/** Set MRCP resource factory */ +MRCP_DECLARE(void) mrcp_client_connection_resource_factory_set( + mrcp_connection_agent_t *agent, + mrcp_resource_factory_t *resource_factroy) +{ + agent->resource_factory = resource_factroy; +} + +/** Get task */ +MRCP_DECLARE(apt_task_t*) mrcp_client_connection_agent_task_get(mrcp_connection_agent_t *agent) +{ + return agent->task; +} + +/** Get external object */ +MRCP_DECLARE(void*) mrcp_client_connection_agent_object_get(mrcp_connection_agent_t *agent) +{ + return agent->obj; +} + + +/** Create control channel */ +MRCP_DECLARE(mrcp_control_channel_t*) mrcp_client_control_channel_create(mrcp_connection_agent_t *agent, void *obj, apr_pool_t *pool) +{ + mrcp_control_channel_t *channel = apr_palloc(pool,sizeof(mrcp_control_channel_t)); + channel->agent = agent; + channel->connection = NULL; + channel->removed = FALSE; + channel->obj = obj; + channel->pool = pool; + return channel; +} + +/** Destroy MRCPv2 control channel */ +MRCP_DECLARE(apt_bool_t) mrcp_client_control_channel_destroy(mrcp_control_channel_t *channel) +{ + if(channel && channel->connection && channel->removed == TRUE) { + mrcp_connection_t *connection = channel->connection; + channel->connection = NULL; + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Destroy TCP/MRCPv2 Connection %s",connection->id); + mrcp_connection_destroy(connection); + } + return TRUE; +} + +static apt_bool_t mrcp_client_control_message_signal( + connection_task_msg_type_e type, + mrcp_connection_agent_t *agent, + mrcp_control_channel_t *channel, + mrcp_control_descriptor_t *descriptor, + mrcp_message_t *message) +{ + apt_bool_t status; + connection_task_msg_t *msg = apr_palloc(channel->pool,sizeof(connection_task_msg_t)); + msg->type = type; + msg->agent = agent; + msg->channel = channel; + msg->descriptor = descriptor; + msg->message = message; + + apr_thread_mutex_lock(agent->guard); + status = apt_cyclic_queue_push(agent->msg_queue,msg); + apr_thread_mutex_unlock(agent->guard); + if(apt_pollset_wakeup(agent->pollset) != TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Signal Control Message"); + status = FALSE; + } + return status; +} + +/** Add MRCPv2 control channel */ +MRCP_DECLARE(apt_bool_t) mrcp_client_control_channel_add(mrcp_control_channel_t *channel, mrcp_control_descriptor_t *descriptor) +{ + return mrcp_client_control_message_signal(CONNECTION_TASK_MSG_ADD_CHANNEL,channel->agent,channel,descriptor,NULL); +} + +/** Modify MRCPv2 control channel */ +MRCP_DECLARE(apt_bool_t) mrcp_client_control_channel_modify(mrcp_control_channel_t *channel, mrcp_control_descriptor_t *descriptor) +{ + return mrcp_client_control_message_signal(CONNECTION_TASK_MSG_MODIFY_CHANNEL,channel->agent,channel,descriptor,NULL); +} + +/** Remove MRCPv2 control channel */ +MRCP_DECLARE(apt_bool_t) mrcp_client_control_channel_remove(mrcp_control_channel_t *channel) +{ + return mrcp_client_control_message_signal(CONNECTION_TASK_MSG_REMOVE_CHANNEL,channel->agent,channel,NULL,NULL); +} + +/** Send MRCPv2 message */ +MRCP_DECLARE(apt_bool_t) mrcp_client_control_message_send(mrcp_control_channel_t *channel, mrcp_message_t *message) +{ + return mrcp_client_control_message_signal(CONNECTION_TASK_MSG_SEND_MESSAGE,channel->agent,channel,NULL,message); +} + +static mrcp_connection_t* mrcp_client_agent_connection_create(mrcp_connection_agent_t *agent, mrcp_control_descriptor_t *descriptor) +{ + char *local_ip = NULL; + char *remote_ip = NULL; + mrcp_connection_t *connection = mrcp_connection_create(); + + apr_sockaddr_info_get(&connection->r_sockaddr,descriptor->ip.buf,APR_INET,descriptor->port,0,connection->pool); + if(!connection->r_sockaddr) { + mrcp_connection_destroy(connection); + return NULL; + } + + if(apr_socket_create(&connection->sock,connection->r_sockaddr->family,SOCK_STREAM,APR_PROTO_TCP,connection->pool) != APR_SUCCESS) { + mrcp_connection_destroy(connection); + return NULL; + } + + apr_socket_opt_set(connection->sock, APR_SO_NONBLOCK, 0); + apr_socket_timeout_set(connection->sock, -1); + apr_socket_opt_set(connection->sock, APR_SO_REUSEADDR, 1); + + if(apr_socket_connect(connection->sock, connection->r_sockaddr) != APR_SUCCESS) { + apr_socket_close(connection->sock); + mrcp_connection_destroy(connection); + return NULL; + } + + if(apr_socket_addr_get(&connection->l_sockaddr,APR_LOCAL,connection->sock) != APR_SUCCESS) { + apr_socket_close(connection->sock); + mrcp_connection_destroy(connection); + return NULL; + } + + apr_sockaddr_ip_get(&local_ip,connection->l_sockaddr); + apr_sockaddr_ip_get(&remote_ip,connection->r_sockaddr); + connection->id = apr_psprintf(connection->pool,"%s:%hu <-> %s:%hu", + local_ip,connection->l_sockaddr->port, + remote_ip,connection->r_sockaddr->port); + + memset(&connection->sock_pfd,0,sizeof(apr_pollfd_t)); + connection->sock_pfd.desc_type = APR_POLL_SOCKET; + connection->sock_pfd.reqevents = APR_POLLIN; + connection->sock_pfd.desc.s = connection->sock; + connection->sock_pfd.client_data = connection; + if(apt_pollset_add(agent->pollset, &connection->sock_pfd) != TRUE) { + apr_socket_close(connection->sock); + mrcp_connection_destroy(connection); + return NULL; + } + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Established TCP/MRCPv2 Connection %s",connection->id); + connection->agent = agent; + connection->it = apt_list_push_back(agent->connection_list,connection,connection->pool); + connection->parser = mrcp_parser_create(agent->resource_factory,connection->pool); + connection->generator = mrcp_generator_create(agent->resource_factory,connection->pool); + return connection; +} + +static mrcp_connection_t* mrcp_client_agent_connection_find(mrcp_connection_agent_t *agent, mrcp_control_descriptor_t *descriptor) +{ + apr_sockaddr_t *sockaddr; + mrcp_connection_t *connection = NULL; + apt_list_elem_t *elem = apt_list_first_elem_get(agent->connection_list); + /* walk through the list of connections */ + while(elem) { + connection = apt_list_elem_object_get(elem); + if(connection) { + if(apr_sockaddr_info_get(&sockaddr,descriptor->ip.buf,APR_INET,descriptor->port,0,connection->pool) == APR_SUCCESS) { + if(apr_sockaddr_equal(sockaddr,connection->r_sockaddr) != 0) { + return connection; + } + } + } + elem = apt_list_next_elem_get(agent->connection_list,elem); + } + return NULL; +} + +static apt_bool_t mrcp_client_agent_connection_remove(mrcp_connection_agent_t *agent, mrcp_connection_t *connection) +{ + /* remove from the list */ + if(connection->it) { + apt_list_elem_remove(agent->connection_list,connection->it); + connection->it = NULL; + } + apt_pollset_remove(agent->pollset,&connection->sock_pfd); + if(connection->sock) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Close TCP/MRCPv2 Connection %s",connection->id); + apr_socket_close(connection->sock); + connection->sock = NULL; + } + return TRUE; +} + + +static apt_bool_t mrcp_client_agent_channel_add(mrcp_connection_agent_t *agent, mrcp_control_channel_t *channel, mrcp_control_descriptor_t *descriptor) +{ + if(agent->offer_new_connection == TRUE) { + descriptor->connection_type = MRCP_CONNECTION_TYPE_NEW; + } + else { + descriptor->connection_type = MRCP_CONNECTION_TYPE_EXISTING; + if(apt_list_is_empty(agent->connection_list) == TRUE) { + /* offer new connection if there is no established connection yet */ + descriptor->connection_type = MRCP_CONNECTION_TYPE_NEW; + } + } + /* send response */ + return mrcp_control_channel_add_respond(agent->vtable,channel,descriptor,TRUE); +} + +static apt_bool_t mrcp_client_agent_channel_modify(mrcp_connection_agent_t *agent, mrcp_control_channel_t *channel, mrcp_control_descriptor_t *descriptor) +{ + apt_bool_t status = TRUE; + if(descriptor->port) { + if(!channel->connection) { + mrcp_connection_t *connection = NULL; + apt_id_resource_generate(&descriptor->session_id,&descriptor->resource_name,'@',&channel->identifier,channel->pool); + /* no connection yet */ + if(descriptor->connection_type == MRCP_CONNECTION_TYPE_EXISTING) { + /* try to find existing connection */ + connection = mrcp_client_agent_connection_find(agent,descriptor); + if(!connection) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Found No Existing TCP/MRCPv2 Connection"); + } + } + if(!connection) { + /* create new connection */ + connection = mrcp_client_agent_connection_create(agent,descriptor); + if(!connection) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Establish TCP/MRCPv2 Connection"); + } + } + + if(connection) { + mrcp_connection_channel_add(connection,channel); + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Add Control Channel <%s> %s [%d]", + channel->identifier.buf, + connection->id, + apr_hash_count(connection->channel_table)); + if(descriptor->connection_type == MRCP_CONNECTION_TYPE_NEW) { + /* set connection type to existing for the next offers / if any */ + descriptor->connection_type = MRCP_CONNECTION_TYPE_EXISTING; + } + } + else { + descriptor->port = 0; + status = FALSE; + } + } + } + /* send response */ + return mrcp_control_channel_modify_respond(agent->vtable,channel,descriptor,status); +} + +static apt_bool_t mrcp_client_agent_channel_remove(mrcp_connection_agent_t *agent, mrcp_control_channel_t *channel) +{ + if(channel->connection) { + mrcp_connection_t *connection = channel->connection; + mrcp_connection_channel_remove(connection,channel); + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Remove Control Channel <%s> [%d]", + channel->identifier.buf, + apr_hash_count(connection->channel_table)); + if(!connection->access_count) { + mrcp_client_agent_connection_remove(agent,connection); + /* set connection to be destroyed on channel destroy */ + channel->connection = connection; + channel->removed = TRUE; + } + } + + /* send response */ + return mrcp_control_channel_remove_respond(agent->vtable,channel,TRUE); +} + +static apt_bool_t mrcp_client_agent_messsage_send(mrcp_connection_agent_t *agent, mrcp_control_channel_t *channel, mrcp_message_t *message) +{ + apt_bool_t status = FALSE; + mrcp_connection_t *connection = channel->connection; + apt_text_stream_t *stream; + mrcp_stream_result_e result; + + if(!connection || !connection->sock) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No MRCPv2 Connection"); + return FALSE; + } + stream = &connection->tx_stream; + + mrcp_generator_message_set(connection->generator,message); + do { + apt_text_stream_init(&connection->tx_stream,connection->tx_buffer,sizeof(connection->tx_buffer)-1); + result = mrcp_generator_run(connection->generator,stream); + if(result == MRCP_STREAM_MESSAGE_COMPLETE || result == MRCP_STREAM_MESSAGE_TRUNCATED) { + stream->text.length = stream->pos - stream->text.buf; + *stream->pos = '\0'; + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Send MRCPv2 Stream %s [%lu bytes]\n%s", + connection->id, + stream->text.length, + stream->text.buf); + if(apr_socket_send(connection->sock,stream->text.buf,&stream->text.length) == APR_SUCCESS) { + status = TRUE; + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Send MRCPv2 Stream"); + } + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Generate MRCPv2 Stream"); + } + } + while(result == MRCP_STREAM_MESSAGE_TRUNCATED); + + if(status == FALSE) { + mrcp_message_t *response = mrcp_response_create(message,message->pool); + response->start_line.method_id = message->start_line.method_id; + response->start_line.method_name = message->start_line.method_name; + response->start_line.status_code = MRCP_STATUS_CODE_METHOD_FAILED; + mrcp_connection_message_receive(agent->vtable,channel,response); + } + return TRUE; +} + +static apt_bool_t mrcp_client_message_handler(void *obj, mrcp_message_t *message, mrcp_stream_result_e result) +{ + if(result == MRCP_STREAM_MESSAGE_COMPLETE) { + /* message is completely parsed */ + mrcp_connection_t *connection = obj; + mrcp_control_channel_t *channel; + apt_str_t identifier; + apt_id_resource_generate(&message->channel_id.session_id,&message->channel_id.resource_name,'@',&identifier,message->pool); + channel = mrcp_connection_channel_find(connection,&identifier); + if(channel) { + mrcp_connection_agent_t *agent = connection->agent; + mrcp_connection_message_receive(agent->vtable,channel,message); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Find Channel <%s@%s> in Connection %s [%d]", + message->channel_id.session_id.buf, + message->channel_id.resource_name.buf, + connection->id, + apr_hash_count(connection->channel_table)); + } + } + return TRUE; +} + +static apt_bool_t mrcp_client_agent_messsage_receive(mrcp_connection_agent_t *agent, mrcp_connection_t *connection) +{ + apr_status_t status; + apr_size_t offset; + apr_size_t length; + apt_text_stream_t *stream; + + if(!connection || !connection->sock) { + return FALSE; + } + stream = &connection->rx_stream; + + /* init length of the stream */ + stream->text.length = sizeof(connection->rx_buffer)-1; + /* calculate offset remaining from the previous receive / if any */ + offset = stream->pos - stream->text.buf; + /* calculate available length */ + length = stream->text.length - offset; + status = apr_socket_recv(connection->sock,stream->pos,&length); + if(status == APR_EOF || length == 0) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"TCP/MRCPv2 Peer Disconnected %s",connection->id); + apt_pollset_remove(agent->pollset,&connection->sock_pfd); + apr_socket_close(connection->sock); + connection->sock = NULL; + +// agent->vtable->on_disconnect(agent,connection); + return TRUE; + } + /* calculate actual length of the stream */ + stream->text.length = offset + length; + stream->pos[length] = '\0'; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Receive MRCPv2 Stream %s [%lu bytes]\n%s", + connection->id, + length, + stream->pos); + + /* reset pos */ + stream->pos = stream->text.buf; + /* walk through the stream parsing RTSP messages */ + return mrcp_stream_walk(connection->parser,stream,mrcp_client_message_handler,connection); +} + +static apt_bool_t mrcp_client_agent_control_process(mrcp_connection_agent_t *agent) +{ + apt_bool_t status = TRUE; + apt_bool_t running = TRUE; + connection_task_msg_t *msg; + + do { + apr_thread_mutex_lock(agent->guard); + msg = apt_cyclic_queue_pop(agent->msg_queue); + apr_thread_mutex_unlock(agent->guard); + if(msg) { + switch(msg->type) { + case CONNECTION_TASK_MSG_ADD_CHANNEL: + mrcp_client_agent_channel_add(agent,msg->channel,msg->descriptor); + break; + case CONNECTION_TASK_MSG_MODIFY_CHANNEL: + mrcp_client_agent_channel_modify(agent,msg->channel,msg->descriptor); + break; + case CONNECTION_TASK_MSG_REMOVE_CHANNEL: + mrcp_client_agent_channel_remove(agent,msg->channel); + break; + case CONNECTION_TASK_MSG_SEND_MESSAGE: + mrcp_client_agent_messsage_send(agent,msg->channel,msg->message); + break; + case CONNECTION_TASK_MSG_TERMINATE: + status = FALSE; + break; + } + } + else { + running = FALSE; + } + } + while(running == TRUE); + return status; +} + +static apt_bool_t mrcp_client_agent_task_run(apt_task_t *task) +{ + mrcp_connection_agent_t *agent = apt_task_object_get(task); + apt_bool_t running = TRUE; + apr_status_t status; + apr_int32_t num; + const apr_pollfd_t *ret_pfd; + int i; + + if(!agent) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Start MRCPv2 Agent"); + return FALSE; + } + + agent->pollset = apt_pollset_create((apr_uint32_t)agent->max_connection_count,agent->pool); + if(!agent->pollset) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Pollset"); + return FALSE; + } + + while(running) { + status = apt_pollset_poll(agent->pollset, -1, &num, &ret_pfd); + if(status != APR_SUCCESS) { + continue; + } + for(i = 0; i < num; i++) { + if(apt_pollset_is_wakeup(agent->pollset,&ret_pfd[i])) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Process Control Message"); + if(mrcp_client_agent_control_process(agent) == FALSE) { + running = FALSE; + break; + } + continue; + } + + mrcp_client_agent_messsage_receive(agent,ret_pfd[i].client_data); + } + } + + apt_pollset_destroy(agent->pollset); + agent->pollset = NULL; + + apt_task_child_terminate(agent->task); + return TRUE; +} + +static apt_bool_t mrcp_client_agent_task_terminate(apt_task_t *task) +{ + apt_bool_t status = FALSE; + mrcp_connection_agent_t *agent = apt_task_object_get(task); + if(agent->pollset) { + connection_task_msg_t *msg = apr_pcalloc(agent->pool,sizeof(connection_task_msg_t)); + msg->type = CONNECTION_TASK_MSG_TERMINATE; + + apr_thread_mutex_lock(agent->guard); + status = apt_cyclic_queue_push(agent->msg_queue,msg); + apr_thread_mutex_unlock(agent->guard); + if(apt_pollset_wakeup(agent->pollset) == TRUE) { + status = TRUE; + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Signal Control Message"); + } + } + return status; +} diff --git a/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_connection.c b/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_connection.c new file mode 100644 index 0000000000..079671a491 --- /dev/null +++ b/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_connection.c @@ -0,0 +1,80 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mrcp_connection.h" +#include "apt_pool.h" + +mrcp_connection_t* mrcp_connection_create() +{ + mrcp_connection_t *connection; + apr_pool_t *pool = apt_pool_create(); + if(!pool) { + return NULL; + } + + connection = apr_palloc(pool,sizeof(mrcp_connection_t)); + connection->pool = pool; + apt_string_reset(&connection->remote_ip); + connection->l_sockaddr = NULL; + connection->r_sockaddr = NULL; + connection->sock = NULL; + connection->id = NULL; + connection->access_count = 0; + connection->it = NULL; + connection->channel_table = apr_hash_make(pool); + apt_text_stream_init(&connection->rx_stream,connection->rx_buffer,sizeof(connection->rx_buffer)-1); + apt_text_stream_init(&connection->tx_stream,connection->tx_buffer,sizeof(connection->tx_buffer)-1); + connection->parser = NULL; + connection->generator = NULL; + return connection; +} + +void mrcp_connection_destroy(mrcp_connection_t *connection) +{ + if(connection && connection->pool) { + apr_pool_destroy(connection->pool); + } +} + +apt_bool_t mrcp_connection_channel_add(mrcp_connection_t *connection, mrcp_control_channel_t *channel) +{ + if(!connection || !channel) { + return FALSE; + } + apr_hash_set(connection->channel_table,channel->identifier.buf,channel->identifier.length,channel); + channel->connection = connection; + connection->access_count++; + return TRUE; +} + +mrcp_control_channel_t* mrcp_connection_channel_find(mrcp_connection_t *connection, const apt_str_t *identifier) +{ + if(!connection || !identifier) { + return NULL; + } + return apr_hash_get(connection->channel_table,identifier->buf,identifier->length); +} + +apt_bool_t mrcp_connection_channel_remove(mrcp_connection_t *connection, mrcp_control_channel_t *channel) +{ + if(!connection || !channel) { + return FALSE; + } + apr_hash_set(connection->channel_table,channel->identifier.buf,channel->identifier.length,NULL); + channel->connection = NULL; + connection->access_count--; + return TRUE; +} diff --git a/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_control_descriptor.c b/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_control_descriptor.c new file mode 100644 index 0000000000..2e9a0e43c2 --- /dev/null +++ b/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_control_descriptor.c @@ -0,0 +1,112 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "apt_string_table.h" +#include "mrcp_control_descriptor.h" + +/** String table of mrcp proto types (mrcp_proto_type_e) */ +static const apt_str_table_item_t mrcp_proto_type_table[] = { + {{"TCP/MRCPv2", 10},4}, + {{"TCP/TLS/MRCPv2",14},4} +}; + +/** String table of mrcp attributes (mrcp_attrib_e) */ +static const apt_str_table_item_t mrcp_attrib_table[] = { + {{"setup", 5},0}, + {{"connection",10},1}, + {{"resource", 8},0}, + {{"channel", 7},1}, + {{"cmid", 4},1} +}; + +/** String table of mrcp setup attribute values (mrcp_setup_type_e) */ +static const apt_str_table_item_t mrcp_setup_value_table[] = { + {{"active", 6},0}, + {{"passive", 7},0} +}; + +/** String table of mrcp connection attribute values (mrcp_connection_type_e) */ +static const apt_str_table_item_t mrcp_connection_value_table[] = { + {{"new", 3},0}, + {{"existing", 8},0} +}; + + +MRCP_DECLARE(const apt_str_t*) mrcp_proto_get(mrcp_proto_type_e proto) +{ + return apt_string_table_str_get(mrcp_proto_type_table,MRCP_PROTO_COUNT,proto); +} + +MRCP_DECLARE(mrcp_proto_type_e) mrcp_proto_find(const apt_str_t *attrib) +{ + return apt_string_table_id_find(mrcp_proto_type_table,MRCP_PROTO_COUNT,attrib); +} + +MRCP_DECLARE(const apt_str_t*) mrcp_attrib_str_get(mrcp_attrib_e attrib_id) +{ + return apt_string_table_str_get(mrcp_attrib_table,MRCP_ATTRIB_COUNT,attrib_id); +} + +MRCP_DECLARE(mrcp_attrib_e) mrcp_attrib_id_find(const apt_str_t *attrib) +{ + return apt_string_table_id_find(mrcp_attrib_table,MRCP_ATTRIB_COUNT,attrib); +} + +MRCP_DECLARE(const apt_str_t*) mrcp_setup_type_get(mrcp_setup_type_e setup_type) +{ + return apt_string_table_str_get(mrcp_setup_value_table,MRCP_SETUP_TYPE_COUNT,setup_type); +} + +MRCP_DECLARE(mrcp_setup_type_e) mrcp_setup_type_find(const apt_str_t *attrib) +{ + return apt_string_table_id_find(mrcp_setup_value_table,MRCP_SETUP_TYPE_COUNT,attrib); +} + +MRCP_DECLARE(const apt_str_t*) mrcp_connection_type_get(mrcp_connection_type_e connection_type) +{ + return apt_string_table_str_get(mrcp_connection_value_table,MRCP_CONNECTION_TYPE_COUNT,connection_type); +} + +MRCP_DECLARE(mrcp_connection_type_e) mrcp_connection_type_find(const apt_str_t *attrib) +{ + return apt_string_table_id_find(mrcp_connection_value_table,MRCP_CONNECTION_TYPE_COUNT,attrib); +} + +/** Create MRCP control offer */ +MRCP_DECLARE(mrcp_control_descriptor_t*) mrcp_control_offer_create(apr_pool_t *pool) +{ + mrcp_control_descriptor_t *offer; + offer = apr_palloc(pool,sizeof(mrcp_control_descriptor_t)); + mrcp_control_descriptor_init(offer); + offer->proto = MRCP_PROTO_TCP; + offer->port = 9; + offer->setup_type = MRCP_SETUP_TYPE_ACTIVE; + offer->connection_type = MRCP_CONNECTION_TYPE_EXISTING; + return offer; +} + +/** Create MRCP control answer */ +MRCP_DECLARE(mrcp_control_descriptor_t*) mrcp_control_answer_create(mrcp_control_descriptor_t *offer, apr_pool_t *pool) +{ + mrcp_control_descriptor_t *answer; + answer = apr_palloc(pool,sizeof(mrcp_control_descriptor_t)); + mrcp_control_descriptor_init(answer); + if(offer) { + *answer = *offer; + } + answer->setup_type = MRCP_SETUP_TYPE_PASSIVE; + return answer; +} diff --git a/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_server_connection.c b/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_server_connection.c new file mode 100644 index 0000000000..1655697c89 --- /dev/null +++ b/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_server_connection.c @@ -0,0 +1,766 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "mrcp_connection.h" +#include "mrcp_server_connection.h" +#include "mrcp_control_descriptor.h" +#include "mrcp_resource_factory.h" +#include "mrcp_message.h" +#include "apt_text_stream.h" +#include "apt_task.h" +#include "apt_pool.h" +#include "apt_pollset.h" +#include "apt_cyclic_queue.h" +#include "apt_log.h" + +#define MRCPV2_CONNECTION_TASK_NAME "TCP/MRCPv2 Connection Agent" + +struct mrcp_connection_agent_t { + apr_pool_t *pool; + apt_task_t *task; + mrcp_resource_factory_t *resource_factory; + + apt_obj_list_t *connection_list; + mrcp_connection_t *null_connection; + + apt_bool_t force_new_connection; + apr_size_t max_connection_count; + + apr_sockaddr_t *sockaddr; + /* Listening socket */ + apr_socket_t *listen_sock; + apr_pollfd_t listen_sock_pfd; + + apr_thread_mutex_t *guard; + apt_cyclic_queue_t *msg_queue; + apt_pollset_t *pollset; + + void *obj; + const mrcp_connection_event_vtable_t *vtable; +}; + +typedef enum { + CONNECTION_TASK_MSG_ADD_CHANNEL, + CONNECTION_TASK_MSG_MODIFY_CHANNEL, + CONNECTION_TASK_MSG_REMOVE_CHANNEL, + CONNECTION_TASK_MSG_SEND_MESSAGE, + CONNECTION_TASK_MSG_TERMINATE +} connection_task_msg_type_e; + +typedef struct connection_task_msg_t connection_task_msg_t; +struct connection_task_msg_t { + connection_task_msg_type_e type; + mrcp_connection_agent_t *agent; + mrcp_control_channel_t *channel; + mrcp_control_descriptor_t *descriptor; + mrcp_message_t *message; +}; + +static apt_bool_t mrcp_server_agent_task_run(apt_task_t *task); +static apt_bool_t mrcp_server_agent_task_terminate(apt_task_t *task); +static apt_bool_t mrcp_server_agent_task_on_destroy(apt_task_t *task); + +/** Create connection agent */ +MRCP_DECLARE(mrcp_connection_agent_t*) mrcp_server_connection_agent_create( + const char *listen_ip, + apr_port_t listen_port, + apr_size_t max_connection_count, + apt_bool_t force_new_connection, + apr_pool_t *pool) +{ + apt_task_vtable_t *vtable; + mrcp_connection_agent_t *agent; + + if(!listen_ip) { + return NULL; + } + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create "MRCPV2_CONNECTION_TASK_NAME" %s:%hu [%d]",listen_ip,listen_port,max_connection_count); + agent = apr_palloc(pool,sizeof(mrcp_connection_agent_t)); + agent->pool = pool; + agent->sockaddr = NULL; + agent->listen_sock = NULL; + agent->pollset = NULL; + agent->max_connection_count = max_connection_count; + agent->force_new_connection = force_new_connection; + + apr_sockaddr_info_get(&agent->sockaddr,listen_ip,APR_INET,listen_port,0,agent->pool); + if(!agent->sockaddr) { + return NULL; + } + + agent->task = apt_task_create(agent,NULL,pool); + if(!agent->task) { + return NULL; + } + + apt_task_name_set(agent->task,MRCPV2_CONNECTION_TASK_NAME); + vtable = apt_task_vtable_get(agent->task); + if(vtable) { + vtable->run = mrcp_server_agent_task_run; + vtable->terminate = mrcp_server_agent_task_terminate; + vtable->destroy = mrcp_server_agent_task_on_destroy; + } + + agent->msg_queue = apt_cyclic_queue_create(CYCLIC_QUEUE_DEFAULT_SIZE); + apr_thread_mutex_create(&agent->guard,APR_THREAD_MUTEX_UNNESTED,pool); + + agent->connection_list = NULL; + agent->null_connection = NULL; + return agent; +} + +/** Virtual destroy handler. */ +static apt_bool_t mrcp_server_agent_task_on_destroy(apt_task_t *task) +{ + mrcp_connection_agent_t *agent = apt_task_object_get(task); + if(agent->guard) { + apr_thread_mutex_destroy(agent->guard); + agent->guard = NULL; + } + if(agent->msg_queue) { + apt_cyclic_queue_destroy(agent->msg_queue); + agent->msg_queue = NULL; + } + return TRUE; +} + +/** Destroy connection agent. */ +MRCP_DECLARE(apt_bool_t) mrcp_server_connection_agent_destroy(mrcp_connection_agent_t *agent) +{ + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Destroy MRCPv2 Agent"); + return apt_task_destroy(agent->task); +} + +/** Start connection agent. */ +MRCP_DECLARE(apt_bool_t) mrcp_server_connection_agent_start(mrcp_connection_agent_t *agent) +{ + return apt_task_start(agent->task); +} + +/** Terminate connection agent. */ +MRCP_DECLARE(apt_bool_t) mrcp_server_connection_agent_terminate(mrcp_connection_agent_t *agent) +{ + return apt_task_terminate(agent->task,TRUE); +} + +/** Set connection event handler. */ +MRCP_DECLARE(void) mrcp_server_connection_agent_handler_set( + mrcp_connection_agent_t *agent, + void *obj, + const mrcp_connection_event_vtable_t *vtable) +{ + agent->obj = obj; + agent->vtable = vtable; +} + +/** Set MRCP resource factory */ +MRCP_DECLARE(void) mrcp_server_connection_resource_factory_set( + mrcp_connection_agent_t *agent, + mrcp_resource_factory_t *resource_factroy) +{ + agent->resource_factory = resource_factroy; +} + +/** Get task */ +MRCP_DECLARE(apt_task_t*) mrcp_server_connection_agent_task_get(mrcp_connection_agent_t *agent) +{ + return agent->task; +} + +/** Get external object */ +MRCP_DECLARE(void*) mrcp_server_connection_agent_object_get(mrcp_connection_agent_t *agent) +{ + return agent->obj; +} + +/** Create MRCPv2 control channel */ +MRCP_DECLARE(mrcp_control_channel_t*) mrcp_server_control_channel_create(mrcp_connection_agent_t *agent, void *obj, apr_pool_t *pool) +{ + mrcp_control_channel_t *channel = apr_palloc(pool,sizeof(mrcp_control_channel_t)); + channel->agent = agent; + channel->connection = NULL; + channel->removed = FALSE; + channel->obj = obj; + channel->pool = pool; + return channel; +} + +/** Destroy MRCPv2 control channel */ +MRCP_DECLARE(apt_bool_t) mrcp_server_control_channel_destroy(mrcp_control_channel_t *channel) +{ + if(channel && channel->connection && channel->removed == TRUE) { + mrcp_connection_t *connection = channel->connection; + channel->connection = NULL; + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Destroy TCP/MRCPv2 Connection %s",connection->id); + mrcp_connection_destroy(connection); + } + return TRUE; +} + +static apt_bool_t mrcp_server_control_message_signal( + connection_task_msg_type_e type, + mrcp_connection_agent_t *agent, + mrcp_control_channel_t *channel, + mrcp_control_descriptor_t *descriptor, + mrcp_message_t *message) +{ + apt_bool_t status; + connection_task_msg_t *msg = apr_palloc(channel->pool,sizeof(connection_task_msg_t)); + msg->type = type; + msg->agent = agent; + msg->channel = channel; + msg->descriptor = descriptor; + msg->message = message; + + apr_thread_mutex_lock(agent->guard); + status = apt_cyclic_queue_push(agent->msg_queue,msg); + apr_thread_mutex_unlock(agent->guard); + if(apt_pollset_wakeup(agent->pollset) != TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Signal Control Message"); + status = FALSE; + } + return status; +} + +/** Add MRCPv2 control channel */ +MRCP_DECLARE(apt_bool_t) mrcp_server_control_channel_add(mrcp_control_channel_t *channel, mrcp_control_descriptor_t *descriptor) +{ + return mrcp_server_control_message_signal(CONNECTION_TASK_MSG_ADD_CHANNEL,channel->agent,channel,descriptor,NULL); +} + +/** Modify MRCPv2 control channel */ +MRCP_DECLARE(apt_bool_t) mrcp_server_control_channel_modify(mrcp_control_channel_t *channel, mrcp_control_descriptor_t *descriptor) +{ + return mrcp_server_control_message_signal(CONNECTION_TASK_MSG_MODIFY_CHANNEL,channel->agent,channel,descriptor,NULL); +} + +/** Remove MRCPv2 control channel */ +MRCP_DECLARE(apt_bool_t) mrcp_server_control_channel_remove(mrcp_control_channel_t *channel) +{ + return mrcp_server_control_message_signal(CONNECTION_TASK_MSG_REMOVE_CHANNEL,channel->agent,channel,NULL,NULL); +} + +/** Send MRCPv2 message */ +MRCP_DECLARE(apt_bool_t) mrcp_server_control_message_send(mrcp_control_channel_t *channel, mrcp_message_t *message) +{ + return mrcp_server_control_message_signal(CONNECTION_TASK_MSG_SEND_MESSAGE,channel->agent,channel,NULL,message); +} + + +static apt_bool_t mrcp_server_agent_listen_socket_create(mrcp_connection_agent_t *agent) +{ + apr_status_t status; + if(!agent->sockaddr) { + return FALSE; + } + + /* create listening socket */ + status = apr_socket_create(&agent->listen_sock, agent->sockaddr->family, SOCK_STREAM, APR_PROTO_TCP, agent->pool); + if(status != APR_SUCCESS) { + return FALSE; + } + + apr_socket_opt_set(agent->listen_sock, APR_SO_NONBLOCK, 0); + apr_socket_timeout_set(agent->listen_sock, -1); + apr_socket_opt_set(agent->listen_sock, APR_SO_REUSEADDR, 1); + + status = apr_socket_bind(agent->listen_sock, agent->sockaddr); + if(status != APR_SUCCESS) { + apr_socket_close(agent->listen_sock); + agent->listen_sock = NULL; + return FALSE; + } + status = apr_socket_listen(agent->listen_sock, SOMAXCONN); + if(status != APR_SUCCESS) { + apr_socket_close(agent->listen_sock); + agent->listen_sock = NULL; + return FALSE; + } + + return TRUE; +} + +static void mrcp_server_agent_listen_socket_destroy(mrcp_connection_agent_t *agent) +{ + if(agent->listen_sock) { + apr_socket_close(agent->listen_sock); + agent->listen_sock = NULL; + } +} + +static apt_bool_t mrcp_server_agent_pollset_create(mrcp_connection_agent_t *agent) +{ + /* create pollset */ + agent->pollset = apt_pollset_create((apr_uint32_t)agent->max_connection_count + 1, agent->pool); + if(!agent->pollset) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Pollset"); + return FALSE; + } + + if(mrcp_server_agent_listen_socket_create(agent) == TRUE) { + /* add listen socket to pollset */ + memset(&agent->listen_sock_pfd,0,sizeof(apr_pollfd_t)); + agent->listen_sock_pfd.desc_type = APR_POLL_SOCKET; + agent->listen_sock_pfd.reqevents = APR_POLLIN; + agent->listen_sock_pfd.desc.s = agent->listen_sock; + agent->listen_sock_pfd.client_data = agent->listen_sock; + if(apt_pollset_add(agent->pollset, &agent->listen_sock_pfd) != TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Add Listen Socket to Pollset"); + mrcp_server_agent_listen_socket_destroy(agent); + } + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Listen Socket"); + } + + return TRUE; +} + +static void mrcp_server_agent_pollset_destroy(mrcp_connection_agent_t *agent) +{ + mrcp_server_agent_listen_socket_destroy(agent); + if(agent->pollset) { + apt_pollset_destroy(agent->pollset); + agent->pollset = NULL; + } +} + + +static mrcp_control_channel_t* mrcp_connection_channel_associate(mrcp_connection_agent_t *agent, mrcp_connection_t *connection, const mrcp_message_t *message) +{ + apt_str_t identifier; + mrcp_control_channel_t *channel; + if(!connection || !message) { + return NULL; + } + apt_id_resource_generate(&message->channel_id.session_id,&message->channel_id.resource_name,'@',&identifier,connection->pool); + channel = mrcp_connection_channel_find(connection,&identifier); + if(!channel) { + channel = mrcp_connection_channel_find(agent->null_connection,&identifier); + if(channel) { + mrcp_connection_channel_remove(agent->null_connection,channel); + mrcp_connection_channel_add(connection,channel); + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Move Control Channel <%s> to Connection %s [%d]", + channel->identifier.buf, + connection->id, + apr_hash_count(connection->channel_table)); + } + } + return channel; +} + +static mrcp_connection_t* mrcp_connection_find(mrcp_connection_agent_t *agent, const apt_str_t *remote_ip) +{ + mrcp_connection_t *connection = NULL; + apt_list_elem_t *elem; + if(!agent || !agent->connection_list || !remote_ip) { + return NULL; + } + + elem = apt_list_first_elem_get(agent->connection_list); + /* walk through the list of connections */ + while(elem) { + connection = apt_list_elem_object_get(elem); + if(connection) { + if(apt_string_compare(&connection->remote_ip,remote_ip) == TRUE) { + return connection; + } + } + elem = apt_list_next_elem_get(agent->connection_list,elem); + } + return NULL; +} + +static apt_bool_t mrcp_connection_remove(mrcp_connection_agent_t *agent, mrcp_connection_t *connection) +{ + if(connection->it) { + apt_list_elem_remove(agent->connection_list,connection->it); + connection->it = NULL; + } + if(agent->null_connection) { + if(apt_list_is_empty(agent->connection_list) == TRUE && apr_hash_count(agent->null_connection->channel_table) == 0) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Destroy Pending Connection"); + mrcp_connection_destroy(agent->null_connection); + agent->null_connection = NULL; + agent->connection_list = NULL; + } + } + return TRUE; +} + +static apt_bool_t mrcp_server_agent_connection_accept(mrcp_connection_agent_t *agent) +{ + char *local_ip = NULL; + char *remote_ip = NULL; + apr_socket_t *sock; + apr_pool_t *pool; + mrcp_connection_t *connection; + + if(!agent->null_connection) { + pool = apt_pool_create(); + if(apr_socket_accept(&sock,agent->listen_sock,pool) != APR_SUCCESS) { + return FALSE; + } + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Rejected TCP/MRCPv2 Connection"); + apr_socket_close(sock); + apr_pool_destroy(pool); + return FALSE; + } + + pool = agent->null_connection->pool; + if(apr_socket_accept(&sock,agent->listen_sock,pool) != APR_SUCCESS) { + return FALSE; + } + + connection = mrcp_connection_create(); + connection->sock = sock; + + if(apr_socket_addr_get(&connection->r_sockaddr,APR_REMOTE,sock) != APR_SUCCESS || + apr_socket_addr_get(&connection->l_sockaddr,APR_LOCAL,sock) != APR_SUCCESS) { + apr_socket_close(sock); + mrcp_connection_destroy(connection); + return FALSE; + } + + apr_sockaddr_ip_get(&local_ip,connection->l_sockaddr); + apr_sockaddr_ip_get(&remote_ip,connection->r_sockaddr); + apt_string_set(&connection->remote_ip,remote_ip); + connection->id = apr_psprintf(connection->pool,"%s:%hu <-> %s:%hu", + local_ip,connection->l_sockaddr->port, + remote_ip,connection->r_sockaddr->port); + + memset(&connection->sock_pfd,0,sizeof(apr_pollfd_t)); + connection->sock_pfd.desc_type = APR_POLL_SOCKET; + connection->sock_pfd.reqevents = APR_POLLIN; + connection->sock_pfd.desc.s = connection->sock; + connection->sock_pfd.client_data = connection; + if(apt_pollset_add(agent->pollset, &connection->sock_pfd) != TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Add to Pollset"); + apr_socket_close(sock); + mrcp_connection_destroy(connection); + return FALSE; + } + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Accepted TCP/MRCPv2 Connection %s",connection->id); + connection->agent = agent; + connection->it = apt_list_push_back(agent->connection_list,connection,connection->pool); + connection->parser = mrcp_parser_create(agent->resource_factory,connection->pool); + connection->generator = mrcp_generator_create(agent->resource_factory,connection->pool); + return TRUE; +} + +static apt_bool_t mrcp_server_agent_connection_close(mrcp_connection_agent_t *agent, mrcp_connection_t *connection) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"TCP/MRCPv2 Peer Disconnected %s",connection->id); + apt_pollset_remove(agent->pollset,&connection->sock_pfd); + apr_socket_close(connection->sock); + connection->sock = NULL; + if(!connection->access_count) { + mrcp_connection_remove(agent,connection); + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Destroy TCP/MRCPv2 Connection %s",connection->id); + mrcp_connection_destroy(connection); + } + return TRUE; +} + +static apt_bool_t mrcp_server_agent_channel_add(mrcp_connection_agent_t *agent, mrcp_control_channel_t *channel, mrcp_control_descriptor_t *offer) +{ + mrcp_control_descriptor_t *answer = mrcp_control_answer_create(offer,channel->pool); + apt_id_resource_generate(&offer->session_id,&offer->resource_name,'@',&channel->identifier,channel->pool); + if(offer->port) { + answer->port = agent->sockaddr->port; + } + if(offer->connection_type == MRCP_CONNECTION_TYPE_EXISTING) { + if(agent->force_new_connection == TRUE) { + /* force client to establish new connection */ + answer->connection_type = MRCP_CONNECTION_TYPE_NEW; + } + else { + mrcp_connection_t *connection = NULL; + /* try to find any existing connection */ + connection = mrcp_connection_find(agent,&offer->ip); + if(!connection) { + /* no existing conection found, force the new one */ + answer->connection_type = MRCP_CONNECTION_TYPE_NEW; + } + } + } + + if(!agent->null_connection) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Create Pending Connection"); + agent->null_connection = mrcp_connection_create(); + agent->connection_list = apt_list_create(agent->null_connection->pool); + } + mrcp_connection_channel_add(agent->null_connection,channel); + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Add Control Channel <%s> to Pending Connection [%d]", + channel->identifier.buf, + apr_hash_count(agent->null_connection->channel_table)); + /* send response */ + return mrcp_control_channel_add_respond(agent->vtable,channel,answer,TRUE); +} + +static apt_bool_t mrcp_server_agent_channel_modify(mrcp_connection_agent_t *agent, mrcp_control_channel_t *channel, mrcp_control_descriptor_t *offer) +{ + mrcp_control_descriptor_t *answer = mrcp_control_answer_create(offer,channel->pool); + if(offer->port) { + answer->port = agent->sockaddr->port; + } + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Modify Control Channel <%s>",channel->identifier.buf); + /* send response */ + return mrcp_control_channel_modify_respond(agent->vtable,channel,answer,TRUE); +} + +static apt_bool_t mrcp_server_agent_channel_remove(mrcp_connection_agent_t *agent, mrcp_control_channel_t *channel) +{ + mrcp_connection_t *connection = channel->connection; + mrcp_connection_channel_remove(connection,channel); + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Remove Control Channel <%s> [%d]", + channel->identifier.buf, + apr_hash_count(connection->channel_table)); + if(!connection->access_count) { + if(connection == agent->null_connection) { + if(apt_list_is_empty(agent->connection_list) == TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Destroy Pending Connection"); + mrcp_connection_destroy(agent->null_connection); + agent->null_connection = NULL; + agent->connection_list = NULL; + } + } + else if(!connection->sock) { + mrcp_connection_remove(agent,connection); + /* set connection to be destroyed on channel destroy */ + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Mark Connection for Late Destroy"); + channel->connection = connection; + channel->removed = TRUE; + } + } + /* send response */ + return mrcp_control_channel_remove_respond(agent->vtable,channel,TRUE); +} + +static apt_bool_t mrcp_server_agent_messsage_send(mrcp_connection_agent_t *agent, mrcp_connection_t *connection, mrcp_message_t *message) +{ + apt_bool_t status = FALSE; + apt_text_stream_t *stream; + mrcp_stream_result_e result; + if(!connection || !connection->sock) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No MRCPv2 Connection"); + return FALSE; + } + stream = &connection->tx_stream; + + mrcp_generator_message_set(connection->generator,message); + do { + apt_text_stream_init(&connection->tx_stream,connection->tx_buffer,sizeof(connection->tx_buffer)-1); + result = mrcp_generator_run(connection->generator,stream); + if(result == MRCP_STREAM_MESSAGE_COMPLETE || result == MRCP_STREAM_MESSAGE_TRUNCATED) { + stream->text.length = stream->pos - stream->text.buf; + *stream->pos = '\0'; + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Send MRCPv2 Stream %s [%lu bytes]\n%s", + connection->id, + stream->text.length, + stream->text.buf); + if(apr_socket_send(connection->sock,stream->text.buf,&stream->text.length) == APR_SUCCESS) { + status = TRUE; + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Send MRCPv2 Stream"); + } + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Generate MRCPv2 Stream"); + } + } + while(result == MRCP_STREAM_MESSAGE_TRUNCATED); + + return status; +} + +static apt_bool_t mrcp_server_message_handler(void *obj, mrcp_message_t *message, mrcp_stream_result_e result) +{ + mrcp_connection_t *connection = obj; + mrcp_connection_agent_t *agent = connection->agent; + if(result == MRCP_STREAM_MESSAGE_COMPLETE) { + /* message is completely parsed */ + mrcp_control_channel_t *channel = mrcp_connection_channel_associate(agent,connection,message); + if(channel) { + mrcp_connection_message_receive(agent->vtable,channel,message); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Find Channel <%s@%s> in Connection %s", + message->channel_id.session_id.buf, + message->channel_id.resource_name.buf, + connection->id); + } + } + else if(result == MRCP_STREAM_MESSAGE_INVALID) { + /* error case */ + mrcp_message_t *response; + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Parse MRCPv2 Stream"); + response = mrcp_response_create(message,message->pool); + response->start_line.status_code = MRCP_STATUS_CODE_UNRECOGNIZED_MESSAGE; + if(mrcp_server_agent_messsage_send(agent,connection,response) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Send MRCPv2 Response"); + } + } + return TRUE; +} + +static apt_bool_t mrcp_server_agent_messsage_receive(mrcp_connection_agent_t *agent, mrcp_connection_t *connection) +{ + apr_status_t status; + apr_size_t offset; + apr_size_t length; + apt_text_stream_t *stream; + + if(!connection || !connection->sock) { + return FALSE; + } + stream = &connection->rx_stream; + + /* init length of the stream */ + stream->text.length = sizeof(connection->rx_buffer)-1; + /* calculate offset remaining from the previous receive / if any */ + offset = stream->pos - stream->text.buf; + /* calculate available length */ + length = stream->text.length - offset; + status = apr_socket_recv(connection->sock,stream->pos,&length); + if(status == APR_EOF || length == 0) { + return mrcp_server_agent_connection_close(agent,connection); + } + /* calculate actual length of the stream */ + stream->text.length = offset + length; + stream->pos[length] = '\0'; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Receive MRCPv2 Stream %s [%lu bytes]\n%s", + connection->id, + length, + stream->pos); + + /* reset pos */ + stream->pos = stream->text.buf; + /* walk through the stream parsing RTSP messages */ + return mrcp_stream_walk(connection->parser,stream,mrcp_server_message_handler,connection); +} + +static apt_bool_t mrcp_server_agent_control_process(mrcp_connection_agent_t *agent) +{ + apt_bool_t status = TRUE; + apt_bool_t running = TRUE; + connection_task_msg_t *msg; + + do { + apr_thread_mutex_lock(agent->guard); + msg = apt_cyclic_queue_pop(agent->msg_queue); + apr_thread_mutex_unlock(agent->guard); + if(msg) { + switch(msg->type) { + case CONNECTION_TASK_MSG_ADD_CHANNEL: + mrcp_server_agent_channel_add(agent,msg->channel,msg->descriptor); + break; + case CONNECTION_TASK_MSG_MODIFY_CHANNEL: + mrcp_server_agent_channel_modify(agent,msg->channel,msg->descriptor); + break; + case CONNECTION_TASK_MSG_REMOVE_CHANNEL: + mrcp_server_agent_channel_remove(agent,msg->channel); + break; + case CONNECTION_TASK_MSG_SEND_MESSAGE: + mrcp_server_agent_messsage_send(agent,msg->channel->connection,msg->message); + break; + case CONNECTION_TASK_MSG_TERMINATE: + status = FALSE; + break; + } + } + else { + running = FALSE; + } + } + while(running == TRUE); + return status; +} + +static apt_bool_t mrcp_server_agent_task_run(apt_task_t *task) +{ + mrcp_connection_agent_t *agent = apt_task_object_get(task); + apt_bool_t running = TRUE; + apr_status_t status; + apr_int32_t num; + const apr_pollfd_t *ret_pfd; + int i; + + if(!agent) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Start MRCPv2 Agent"); + return FALSE; + } + + if(mrcp_server_agent_pollset_create(agent) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Pollset"); + return FALSE; + } + + while(running) { + status = apt_pollset_poll(agent->pollset, -1, &num, &ret_pfd); + if(status != APR_SUCCESS) { + continue; + } + for(i = 0; i < num; i++) { + if(ret_pfd[i].desc.s == agent->listen_sock) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Accept MRCPv2 Connection"); + mrcp_server_agent_connection_accept(agent); + continue; + } + if(apt_pollset_is_wakeup(agent->pollset,&ret_pfd[i])) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Process Control Message"); + if(mrcp_server_agent_control_process(agent) == FALSE) { + running = FALSE; + break; + } + continue; + } + + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Process MRCPv2 Message"); + mrcp_server_agent_messsage_receive(agent,ret_pfd[i].client_data); + } + } + + mrcp_server_agent_pollset_destroy(agent); + + apt_task_child_terminate(agent->task); + return TRUE; +} + +static apt_bool_t mrcp_server_agent_task_terminate(apt_task_t *task) +{ + apt_bool_t status = FALSE; + mrcp_connection_agent_t *agent = apt_task_object_get(task); + if(agent->pollset) { + connection_task_msg_t *msg = apr_pcalloc(agent->pool,sizeof(connection_task_msg_t)); + msg->type = CONNECTION_TASK_MSG_TERMINATE; + + apr_thread_mutex_lock(agent->guard); + status = apt_cyclic_queue_push(agent->msg_queue,msg); + apr_thread_mutex_unlock(agent->guard); + if(apt_pollset_wakeup(agent->pollset) == TRUE) { + status = TRUE; + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Signal Control Message"); + } + } + return status; +} diff --git a/libs/unimrcp/libs/uni-rtsp/Makefile.am b/libs/unimrcp/libs/uni-rtsp/Makefile.am new file mode 100644 index 0000000000..00f5ec5189 --- /dev/null +++ b/libs/unimrcp/libs/uni-rtsp/Makefile.am @@ -0,0 +1,22 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_srcdir)/libs/uni-rtsp/include \ + -I$(top_srcdir)/libs/apr-toolkit/include \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) + +noinst_LTLIBRARIES = libunirtsp.la + +include_HEADERS = include/rtsp.h \ + include/rtsp_header.h \ + include/rtsp_start_line.h \ + include/rtsp_message.h \ + include/rtsp_stream.h \ + include/rtsp_server.h \ + include/rtsp_client.h + +libunirtsp_la_SOURCES = src/rtsp_header.c \ + src/rtsp_start_line.c \ + src/rtsp_message.c \ + src/rtsp_stream.c \ + src/rtsp_server.c \ + src/rtsp_client.c diff --git a/libs/unimrcp/libs/uni-rtsp/include/rtsp.h b/libs/unimrcp/libs/uni-rtsp/include/rtsp.h new file mode 100644 index 0000000000..d2d029f635 --- /dev/null +++ b/libs/unimrcp/libs/uni-rtsp/include/rtsp.h @@ -0,0 +1,43 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __RTSP_H__ +#define __RTSP_H__ + +/** + * @file rtsp.h + * @brief RTSP Core Definitions + */ + +#include +#include + +/** Library export/import defines */ +#ifdef WIN32 +#ifdef RTSP_STATIC_LIB +#define RTSP_DECLARE(type) type __stdcall +#else +#ifdef RTSP_LIB_EXPORT +#define RTSP_DECLARE(type) __declspec(dllexport) type __stdcall +#else +#define RTSP_DECLARE(type) __declspec(dllimport) type __stdcall +#endif +#endif +#else +#define RTSP_DECLARE(type) type +#endif + +#endif /*__RTSP_H__*/ diff --git a/libs/unimrcp/libs/uni-rtsp/include/rtsp_client.h b/libs/unimrcp/libs/uni-rtsp/include/rtsp_client.h new file mode 100644 index 0000000000..4d3bd1f1c6 --- /dev/null +++ b/libs/unimrcp/libs/uni-rtsp/include/rtsp_client.h @@ -0,0 +1,149 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __RTSP_CLIENT_H__ +#define __RTSP_CLIENT_H__ + +/** + * @file rtsp_client.h + * @brief RTSP Client + */ + +#include "apt_task.h" +#include "rtsp_message.h" + +APT_BEGIN_EXTERN_C + +/** Opaque RTSP client declaration */ +typedef struct rtsp_client_t rtsp_client_t; +/** Opaque RTSP client session declaration */ +typedef struct rtsp_client_session_t rtsp_client_session_t; + +/** RTSP client vtable declaration */ +typedef struct rtsp_client_vtable_t rtsp_client_vtable_t; + +/** RTSP client vtable */ +struct rtsp_client_vtable_t { + /** Sesssion termination response handler */ + apt_bool_t (*on_session_terminate_response)(rtsp_client_t *client, rtsp_client_session_t *session); + /** Sesssion termination event handler */ + apt_bool_t (*on_session_terminate_event)(rtsp_client_t *client, rtsp_client_session_t *session); + /** Sesssion setup response handler */ + apt_bool_t (*on_session_response)(rtsp_client_t *client, rtsp_client_session_t *session, rtsp_message_t *request, rtsp_message_t *response); + /** Sesssion event handler */ + apt_bool_t (*on_session_event)(rtsp_client_t *client, rtsp_client_session_t *session, rtsp_message_t *message); +}; + +/** + * Create RTSP client. + * @param max_connection_count the number of max RTSP connections + * @param obj the external object to send event to + * @param handler the response/event handler + * @param pool the pool to allocate memory from + */ +RTSP_DECLARE(rtsp_client_t*) rtsp_client_create( + apr_size_t max_connection_count, + void *obj, + const rtsp_client_vtable_t *handler, + apr_pool_t *pool); + +/** + * Destroy RTSP client. + * @param client the client to destroy + */ +RTSP_DECLARE(apt_bool_t) rtsp_client_destroy(rtsp_client_t *client); + +/** + * Start client and wait for incoming requests. + * @param client the client to start + */ +RTSP_DECLARE(apt_bool_t) rtsp_client_start(rtsp_client_t *client); + +/** + * Terminate client. + * @param client the client to terminate + */ +RTSP_DECLARE(apt_bool_t) rtsp_client_terminate(rtsp_client_t *client); + +/** + * Get task. + * @param client the client to get task from + */ +RTSP_DECLARE(apt_task_t*) rtsp_client_task_get(rtsp_client_t *client); + +/** + * Get external object. + * @param client the client to get object from + */ +RTSP_DECLARE(void*) rtsp_client_object_get(rtsp_client_t *client); + + +/** + * Create RTSP session. + * @param client the client to create session for + * @param server_ip the IP address of RTSP server + * @param server_port the port of RTSP server + * @param resource_location the location of RTSP resource (path in RTSP URI) + */ +RTSP_DECLARE(rtsp_client_session_t*) rtsp_client_session_create( + rtsp_client_t *client, + const char *server_ip, + apr_port_t server_port, + const char *resource_location); + +/** + * Destroy RTSP session. + * @param session the session to destroy + */ +RTSP_DECLARE(void) rtsp_client_session_destroy(rtsp_client_session_t *session); + +/** + * Terminate RTSP session. + * @param client the client to use + * @param session the session to terminate + */ +RTSP_DECLARE(apt_bool_t) rtsp_client_session_terminate(rtsp_client_t *client, rtsp_client_session_t *session); + +/** + * Send RTSP message. + * @param client the client to use + * @param session the session to send RTSP request for + * @param message the RTSP request to send + */ +RTSP_DECLARE(apt_bool_t) rtsp_client_session_request(rtsp_client_t *client, rtsp_client_session_t *session, rtsp_message_t *message); + +/** + * Get object associated with the session. + * @param session the session to get object from + */ +RTSP_DECLARE(void*) rtsp_client_session_object_get(const rtsp_client_session_t *session); + +/** + * Set object associated with the session. + * @param session the session to set object for + * @param obj the object to set + */ +RTSP_DECLARE(void) rtsp_client_session_object_set(rtsp_client_session_t *session, void *obj); + +/** + * Get the session identifier. + * @param session the session to get identifier from + */ +RTSP_DECLARE(const apt_str_t*) rtsp_client_session_id_get(const rtsp_client_session_t *session); + +APT_END_EXTERN_C + +#endif /*__RTSP_CLIENT_H__*/ diff --git a/libs/unimrcp/libs/uni-rtsp/include/rtsp_header.h b/libs/unimrcp/libs/uni-rtsp/include/rtsp_header.h new file mode 100644 index 0000000000..68b5ec2bbe --- /dev/null +++ b/libs/unimrcp/libs/uni-rtsp/include/rtsp_header.h @@ -0,0 +1,228 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __RTSP_HEADER_H__ +#define __RTSP_HEADER_H__ + +/** + * @file rtsp_header.h + * @brief RTSP Header + */ + +#include "rtsp.h" +#include "apt_text_stream.h" + +APT_BEGIN_EXTERN_C + +/** RTSP transport protocol */ +typedef enum{ + RTSP_TRANSPORT_RTP, + + RTSP_TRANSPORT_COUNT, + RTSP_TRANSPORT_NONE = RTSP_TRANSPORT_COUNT +} rtsp_transport_e; + +/** RTSP transport profile */ +typedef enum{ + RTSP_PROFILE_AVP, + RTSP_PROFILE_SAVP, + + RTSP_PROFILE_COUNT, + RTSP_PROFILE_NONE = RTSP_PROFILE_COUNT +} rtsp_profile_e; + +/** RTSP lower-transport */ +typedef enum{ + RTSP_LOWER_TRANSPORT_UDP, + RTSP_LOWER_TRANSPORT_TCP, + + RTSP_LOWER_TRANSPORT_COUNT, + RTSP_LOWER_TRANSPORT_NONE = RTSP_LOWER_TRANSPORT_COUNT +} rtsp_lower_transport_e; + +/** RTSP transport attributes */ +typedef enum{ + RTSP_TRANSPORT_ATTRIB_CLIENT_PORT, + RTSP_TRANSPORT_ATTRIB_SERVER_PORT, + RTSP_TRANSPORT_ATTRIB_SOURCE, + RTSP_TRANSPORT_ATTRIB_DESTINATION, + RTSP_TRANSPORT_ATTRIB_UNICAST, + RTSP_TRANSPORT_ATTRIB_MULTICAST, + + RTSP_TRANSPORT_ATTRIB_COUNT, + RTSP_TRANSPORT_ATTRIB_NONE = RTSP_TRANSPORT_ATTRIB_COUNT +} rtsp_transport_attrib_e; + +/** RTSP delivery */ +typedef enum{ + RTSP_DELIVERY_UNICAST, + RTSP_DELIVERY_MULTICAST, + + RTSP_DELIVERY_COUNT, + RTSP_DELIVERY_NONE = RTSP_DELIVERY_COUNT +} rtsp_delivery_e; + +/** RTSP header fields */ +typedef enum{ + RTSP_HEADER_FIELD_CSEQ, + RTSP_HEADER_FIELD_TRANSPORT, + RTSP_HEADER_FIELD_SESSION_ID, + RTSP_HEADER_FIELD_RTP_INFO, + RTSP_HEADER_FIELD_CONTENT_TYPE, + RTSP_HEADER_FIELD_CONTENT_LENGTH, + + RTSP_HEADER_FIELD_COUNT, + RTSP_HEADER_FIELD_UNKNOWN = RTSP_HEADER_FIELD_COUNT +} rtsp_header_field_id; + +/** RTSP content types */ +typedef enum { + RTSP_CONTENT_TYPE_SDP, + RTSP_CONTENT_TYPE_MRCP, + + RTSP_CONTENT_TYPE_COUNT, + RTSP_CONTENT_TYPE_NONE = RTSP_CONTENT_TYPE_COUNT +} rtsp_content_type_e; + + +/** Bit field masks are used to define property set */ +typedef int rtsp_header_property_t; + + +/** RTSP/RTP port range declaration */ +typedef struct rtsp_port_range_t rtsp_port_range_t; +/** RTSP transport declaration */ +typedef struct rtsp_transport_t rtsp_transport_t; +/** RTSP header declaration */ +typedef struct rtsp_header_t rtsp_header_t; + +/** RTSP/RTP port range */ +struct rtsp_port_range_t { + /** Min (low) port */ + apr_port_t min; + /** Max (high) port */ + apr_port_t max; +}; + +/** RTSP transport */ +struct rtsp_transport_t { + /** Transport profile */ + rtsp_transport_e protocol; + /** Transport profile */ + rtsp_profile_e profile; + /** Lower transport */ + rtsp_lower_transport_e lower_protocol; + /** Delivery method */ + rtsp_delivery_e delivery; + /** Client port range */ + rtsp_port_range_t client_port_range; + /** Server port range */ + rtsp_port_range_t server_port_range; + /** Source IP address */ + apt_str_t source; + /** Destination IP address */ + apt_str_t destination; +}; + +/** RTSP header */ +struct rtsp_header_t { + /** Sequence number */ + apr_size_t cseq; + /** Transport */ + rtsp_transport_t transport; + /** Session identifier */ + apt_str_t session_id; + /** RTP-info */ + apt_str_t rtp_info; + + /** Content type */ + rtsp_content_type_e content_type; + /** Content length */ + apr_size_t content_length; + + /** Property set */ + rtsp_header_property_t property_set; +}; + + +/** Initialize port range */ +static APR_INLINE void rtsp_port_range_init(rtsp_port_range_t *port_range) +{ + port_range->min = 0; + port_range->max = 0; +} + +/** Initialize port range */ +static APR_INLINE apt_bool_t rtsp_port_range_is_valid(const rtsp_port_range_t *port_range) +{ + return (port_range->min == 0 && port_range->max == 0) == FALSE; +} + +/** Initialize transport */ +static APR_INLINE void rtsp_transport_init(rtsp_transport_t *transport) +{ + transport->protocol = RTSP_TRANSPORT_RTP; + transport->profile = RTSP_PROFILE_NONE; + transport->lower_protocol = RTSP_LOWER_TRANSPORT_NONE; + rtsp_port_range_init(&transport->client_port_range); + rtsp_port_range_init(&transport->server_port_range); + apt_string_reset(&transport->source); + apt_string_reset(&transport->destination); +} + +/** Initialize header */ +static APR_INLINE void rtsp_header_init(rtsp_header_t *header) +{ + header->cseq = 0; + rtsp_transport_init(&header->transport); + apt_string_reset(&header->session_id); + apt_string_reset(&header->rtp_info); + header->content_type = RTSP_CONTENT_TYPE_NONE; + header->content_length = 0; + header->property_set = 0; +} + +/** Parse RTSP message */ +RTSP_DECLARE(apt_bool_t) rtsp_header_parse(rtsp_header_t *header, apt_text_stream_t *text_stream, apr_pool_t *pool); +/** Generate RTSP message */ +RTSP_DECLARE(apt_bool_t) rtsp_header_generate(rtsp_header_t *header, apt_text_stream_t *text_stream); + + + +/** Add property to property set */ +static APR_INLINE void rtsp_header_property_add(rtsp_header_property_t *property_set, apr_size_t id) +{ + int mask = 1 << id; + *property_set |= mask; +} + +/** Remove property from property set */ +static APR_INLINE void rtsp_header_property_remove(rtsp_header_property_t *property_set, apr_size_t id) +{ + int mask = 1 << id; + *property_set &= ~mask; +} + +/** Check property in property set */ +static APR_INLINE apt_bool_t rtsp_header_property_check(const rtsp_header_property_t *property_set, apr_size_t id) +{ + int mask = 1 << id; + return ((*property_set & mask) == mask) ? TRUE : FALSE; +} + +APT_END_EXTERN_C + +#endif /*__RTSP_HEADER_H__*/ diff --git a/libs/unimrcp/libs/uni-rtsp/include/rtsp_message.h b/libs/unimrcp/libs/uni-rtsp/include/rtsp_message.h new file mode 100644 index 0000000000..ae57237614 --- /dev/null +++ b/libs/unimrcp/libs/uni-rtsp/include/rtsp_message.h @@ -0,0 +1,76 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __RTSP_MESSAGE_H__ +#define __RTSP_MESSAGE_H__ + +/** + * @file rtsp_message.h + * @brief RTSP Message Definition + */ + +#include "rtsp_start_line.h" +#include "rtsp_header.h" + +APT_BEGIN_EXTERN_C + +/** RTSP message declaration */ +typedef struct rtsp_message_t rtsp_message_t; + +/** RTSP message */ +struct rtsp_message_t { + /** RTSP mesage type (request/response) */ + rtsp_start_line_t start_line; + /** RTSP header */ + rtsp_header_t header; + /** RTSP message body */ + apt_str_t body; + + /** Pool to allocate memory from */ + apr_pool_t *pool; +}; + +/** + * Create RTSP message. + * @param message_type the message type + * @param pool the pool to allocate memory from + */ +RTSP_DECLARE(rtsp_message_t*) rtsp_message_create(rtsp_message_type_e message_type, apr_pool_t *pool); + +/** + * Create RTSP request message. + * @param pool the pool to allocate memory from + */ +RTSP_DECLARE(rtsp_message_t*) rtsp_request_create(apr_pool_t *pool); + +/** + * Create RTSP response message. + * @param request the request to create response to + * @param status_code the status code of the response + * @param reason the reason phrase id of the response + * @param pool the pool to allocate memory from + */ +RTSP_DECLARE(rtsp_message_t*) rtsp_response_create(const rtsp_message_t *request, rtsp_status_code_e status_code, rtsp_reason_phrase_e reason, apr_pool_t *pool); + +/** + * Destroy RTSP message + * @param message the message to destroy + */ +RTSP_DECLARE(void) rtsp_message_destroy(rtsp_message_t *message); + +APT_END_EXTERN_C + +#endif /*__RTSP_MESSAGE_H__*/ diff --git a/libs/unimrcp/libs/uni-rtsp/include/rtsp_server.h b/libs/unimrcp/libs/uni-rtsp/include/rtsp_server.h new file mode 100644 index 0000000000..bf57ce176f --- /dev/null +++ b/libs/unimrcp/libs/uni-rtsp/include/rtsp_server.h @@ -0,0 +1,143 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __RTSP_SERVER_H__ +#define __RTSP_SERVER_H__ + +/** + * @file rtsp_server.h + * @brief RTSP Server + */ + +#include "apt_task.h" +#include "rtsp_message.h" + +APT_BEGIN_EXTERN_C + +/** Opaque RTSP server declaration */ +typedef struct rtsp_server_t rtsp_server_t; +/** Opaque RTSP server session declaration */ +typedef struct rtsp_server_session_t rtsp_server_session_t; + +/** RTSP server vtable declaration */ +typedef struct rtsp_server_vtable_t rtsp_server_vtable_t; + +/** RTSP server vtable declaration */ +struct rtsp_server_vtable_t { + /** Virtual create session */ + apt_bool_t (*create_session)(rtsp_server_t *server, rtsp_server_session_t *session); + /** Virtual terminate session */ + apt_bool_t (*terminate_session)(rtsp_server_t *server, rtsp_server_session_t *session); + /** Virtual message handler */ + apt_bool_t (*handle_message)(rtsp_server_t *server, rtsp_server_session_t *session, rtsp_message_t *message); +}; + +/** + * Create RTSP server. + * @param listen_ip the listen IP address + * @param listen_port the listen port + * @param max_connection_count the number of max RTSP connections + * @param obj the external object to send events to + * @param handler the request handler + * @param pool the pool to allocate memory from + */ +RTSP_DECLARE(rtsp_server_t*) rtsp_server_create( + const char *listen_ip, + apr_port_t listen_port, + apr_size_t max_connection_count, + void *obj, + const rtsp_server_vtable_t *handler, + apr_pool_t *pool); + +/** + * Destroy RTSP server. + * @param server the server to destroy + */ +RTSP_DECLARE(apt_bool_t) rtsp_server_destroy(rtsp_server_t *server); + +/** + * Start server and wait for incoming requests. + * @param server the server to start + */ +RTSP_DECLARE(apt_bool_t) rtsp_server_start(rtsp_server_t *server); + +/** + * Terminate server. + * @param server the server to terminate + */ +RTSP_DECLARE(apt_bool_t) rtsp_server_terminate(rtsp_server_t *server); + +/** + * Get task. + * @param server the server to get task from + */ +RTSP_DECLARE(apt_task_t*) rtsp_server_task_get(rtsp_server_t *server); + +/** + * Get external object. + * @param server the server to get object from + */ +RTSP_DECLARE(void*) rtsp_server_object_get(rtsp_server_t *server); + +/** + * Send RTSP message. + * @param server the server to use + * @param session the session to send RTSP response for + * @param message the RTSP response to send + */ +RTSP_DECLARE(apt_bool_t) rtsp_server_session_respond(rtsp_server_t *server, rtsp_server_session_t *session, rtsp_message_t *message); + +/** + * Terminate RTSP session (respond to terminate request). + * @param server the server to use + * @param session the session to terminate + */ +RTSP_DECLARE(apt_bool_t) rtsp_server_session_terminate(rtsp_server_t *server, rtsp_server_session_t *session); + +/** + * Get object associated with the session. + * @param session the session to get object from + */ +RTSP_DECLARE(void*) rtsp_server_session_object_get(const rtsp_server_session_t *session); + +/** + * Set object associated with the session. + * @param session the session to set object for + * @param obj the object to set + */ +RTSP_DECLARE(void) rtsp_server_session_object_set(rtsp_server_session_t *session, void *obj); + +/** + * Get the session identifier. + * @param session the session to get identifier from + */ +RTSP_DECLARE(const apt_str_t*) rtsp_server_session_id_get(const rtsp_server_session_t *session); + +/** + * Get active (in-progress) session request. + * @param session the session to get from + */ +RTSP_DECLARE(const rtsp_message_t*) rtsp_server_session_request_get(const rtsp_server_session_t *session); + +/** + * Get the session destination (client) ip address. + * @param session the session to get ip address from + */ +RTSP_DECLARE(const char*) rtsp_server_session_destination_get(const rtsp_server_session_t *session); + +APT_END_EXTERN_C + +#endif /*__RTSP_SERVER_H__*/ diff --git a/libs/unimrcp/libs/uni-rtsp/include/rtsp_start_line.h b/libs/unimrcp/libs/uni-rtsp/include/rtsp_start_line.h new file mode 100644 index 0000000000..99b73880e6 --- /dev/null +++ b/libs/unimrcp/libs/uni-rtsp/include/rtsp_start_line.h @@ -0,0 +1,175 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __RTSP_START_LINE_H__ +#define __RTSP_START_LINE_H__ + +/** + * @file rtsp_start_line.h + * @brief RTSP Start Line (request-line/status-line) + */ + +#include "rtsp.h" +#include "apt_text_stream.h" + +APT_BEGIN_EXTERN_C + +/** Protocol version */ +typedef enum { + /** Unknown version */ + RTSP_VERSION_UNKNOWN = 0, + /** RTSP 1.0 */ + RTSP_VERSION_1 = 1, +} rtsp_version_e; + +/** RTSP message types */ +typedef enum { + RTSP_MESSAGE_TYPE_UNKNOWN, + RTSP_MESSAGE_TYPE_REQUEST, + RTSP_MESSAGE_TYPE_RESPONSE +} rtsp_message_type_e; + +/** RTSP methods */ +typedef enum{ + RTSP_METHOD_SETUP, + RTSP_METHOD_ANNOUNCE, + RTSP_METHOD_TEARDOWN, + RTSP_METHOD_DESCRIBE, + + RTSP_METHOD_COUNT, + RTSP_METHOD_UNKNOWN = RTSP_METHOD_COUNT +} rtsp_method_id; + +/** Status codes */ +typedef enum { + RTSP_STATUS_CODE_UNKNOWN = 0, + /** Success codes (2xx) */ + RTSP_STATUS_CODE_OK = 200, + RTSP_STATUS_CODE_CREATED = 201, + /** Failure codec (4xx) */ + RTSP_STATUS_CODE_BAD_REQUEST = 400, + RTSP_STATUS_CODE_UNAUTHORIZED = 401, + RTSP_STATUS_CODE_NOT_FOUND = 404, + RTSP_STATUS_CODE_METHOD_NOT_ALLOWED = 405, + RTSP_STATUS_CODE_NOT_ACCEPTABLE = 406, + RTSP_STATUS_CODE_SESSION_NOT_FOUND = 454, + + RTSP_STATUS_CODE_INTERNAL_SERVER_ERROR = 500, + RTSP_STATUS_CODE_NOT_IMPLEMENTED = 501, +} rtsp_status_code_e; + +/** Reason phrases */ +typedef enum { + RTSP_REASON_PHRASE_OK, + RTSP_REASON_PHRASE_CREATED, + RTSP_REASON_PHRASE_BAD_REQUEST, + RTSP_REASON_PHRASE_UNAUTHORIZED, + RTSP_REASON_PHRASE_NOT_FOUND, + RTSP_REASON_PHRASE_METHOD_NOT_ALLOWED, + RTSP_REASON_PHRASE_NOT_ACCEPTABLE, + RTSP_REASON_PHRASE_SESSION_NOT_FOUND, + RTSP_REASON_PHRASE_INTERNAL_SERVER_ERROR, + RTSP_REASON_PHRASE_NOT_IMPLEMENTED, + RTSP_REASON_PHRASE_COUNT, + + /** Unknown reason phrase */ + RTSP_REASON_PHRASE_UNKNOWN = RTSP_REASON_PHRASE_COUNT +} rtsp_reason_phrase_e; + + +/** RTSP request-line declaration */ +typedef struct rtsp_request_line_t rtsp_request_line_t; +/** RTSP status-line declaration */ +typedef struct rtsp_status_line_t rtsp_status_line_t; +/** RTSP start-line declaration */ +typedef struct rtsp_start_line_t rtsp_start_line_t; + +/** RTSP request-line */ +struct rtsp_request_line_t { + /** Method name */ + apt_str_t method_name; + /** Method id */ + rtsp_method_id method_id; + /** RTSP URL */ + apt_str_t url; + /** Resource name parsed from RTSP URL */ + const char *resource_name; + /** Version of protocol in use */ + rtsp_version_e version; +}; + +/** RTSP status-line */ +struct rtsp_status_line_t { + /** Version of protocol in use */ + rtsp_version_e version; + /** success or failure or other status of the request */ + rtsp_status_code_e status_code; + /** Reason phrase */ + apt_str_t reason; +}; + +/** RTSP start-line */ +struct rtsp_start_line_t { + /** RTSP message type */ + rtsp_message_type_e message_type; + /** RTSP start-line */ + union { + rtsp_request_line_t request_line; + rtsp_status_line_t status_line; + } common; +}; + + +static APR_INLINE void rtsp_request_line_init(rtsp_request_line_t *request_line) +{ + apt_string_reset(&request_line->method_name); + request_line->method_id = RTSP_METHOD_UNKNOWN; + apt_string_reset(&request_line->url); + request_line->resource_name = NULL; + request_line->version = RTSP_VERSION_1; +} + +static APR_INLINE void rtsp_status_line_init(rtsp_status_line_t *status_line) +{ + status_line->version = RTSP_VERSION_1; + status_line->status_code = RTSP_STATUS_CODE_OK; + apt_string_reset(&status_line->reason); +} + +/** Initialize RTSP start-line */ +static APR_INLINE void rtsp_start_line_init(rtsp_start_line_t *start_line, rtsp_message_type_e message_type) +{ + start_line->message_type = message_type; + if(message_type == RTSP_MESSAGE_TYPE_REQUEST) { + rtsp_request_line_init(&start_line->common.request_line); + } + else if(message_type == RTSP_MESSAGE_TYPE_RESPONSE) { + rtsp_status_line_init(&start_line->common.status_line); + } +} + +/** Parse RTSP start-line */ +RTSP_DECLARE(apt_bool_t) rtsp_start_line_parse(rtsp_start_line_t *start_line, apt_text_stream_t *text_stream, apr_pool_t *pool); + +/** Generate RTSP start-line */ +RTSP_DECLARE(apt_bool_t) rtsp_start_line_generate(rtsp_start_line_t *start_line, apt_text_stream_t *text_stream); + +/** Get reason phrase by status code */ +RTSP_DECLARE(const apt_str_t*) rtsp_reason_phrase_get(rtsp_reason_phrase_e reason); + +APT_END_EXTERN_C + +#endif /*__RTSP_START_LINE_H__*/ diff --git a/libs/unimrcp/libs/uni-rtsp/include/rtsp_stream.h b/libs/unimrcp/libs/uni-rtsp/include/rtsp_stream.h new file mode 100644 index 0000000000..343c1f949d --- /dev/null +++ b/libs/unimrcp/libs/uni-rtsp/include/rtsp_stream.h @@ -0,0 +1,69 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __RTSP_STREAM_H__ +#define __RTSP_STREAM_H__ + +/** + * @file rtsp_stream.h + * @brief RTSP Stream Parser and Generator + */ + +#include "rtsp_message.h" + +APT_BEGIN_EXTERN_C + +/** Result of RTSP stream processing (parse/generate) */ +typedef enum { + RTSP_STREAM_MESSAGE_COMPLETE, + RTSP_STREAM_MESSAGE_TRUNCATED, + RTSP_STREAM_MESSAGE_INVALID +} rtsp_stream_result_e; + +/** Opaque RTSP parser declaration */ +typedef struct rtsp_parser_t rtsp_parser_t; +/** Opaque RTSP generator declaration */ +typedef struct rtsp_generator_t rtsp_generator_t; + +/** RTSP message handler */ +typedef apt_bool_t (*rtsp_message_handler_f)(void *obj, rtsp_message_t *message, rtsp_stream_result_e result); + +/** Create RTSP stream parser */ +RTSP_DECLARE(rtsp_parser_t*) rtsp_parser_create(apr_pool_t *pool); + +/** Parse RTSP stream */ +RTSP_DECLARE(rtsp_stream_result_e) rtsp_parser_run(rtsp_parser_t *parser, apt_text_stream_t *stream); + +/** Get parsed RTSP message */ +RTSP_DECLARE(rtsp_message_t*) rtsp_parser_message_get(const rtsp_parser_t *parser); + + +/** Create RTSP stream generator */ +RTSP_DECLARE(rtsp_generator_t*) rtsp_generator_create(apr_pool_t *pool); + +/** Set RTSP message to generate */ +RTSP_DECLARE(apt_bool_t) rtsp_generator_message_set(rtsp_generator_t *generator, rtsp_message_t *message); + +/** Generate RTSP stream */ +RTSP_DECLARE(rtsp_stream_result_e) rtsp_generator_run(rtsp_generator_t *generator, apt_text_stream_t *stream); + + +/** Walk through RTSP stream and call message handler for each parsed message */ +RTSP_DECLARE(apt_bool_t) rtsp_stream_walk(rtsp_parser_t *parser, apt_text_stream_t *stream, rtsp_message_handler_f handler, void *obj); + +APT_END_EXTERN_C + +#endif /*__RTSP_STREAM_H__*/ diff --git a/libs/unimrcp/libs/uni-rtsp/src/rtsp_client.c b/libs/unimrcp/libs/uni-rtsp/src/rtsp_client.c new file mode 100644 index 0000000000..825e1f359d --- /dev/null +++ b/libs/unimrcp/libs/uni-rtsp/src/rtsp_client.c @@ -0,0 +1,824 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include "rtsp_client.h" +#include "rtsp_stream.h" +#include "apt_net_client_task.h" +#include "apt_text_stream.h" +#include "apt_pool.h" +#include "apt_obj_list.h" +#include "apt_log.h" + +#define RTSP_STREAM_BUFFER_SIZE 1024 + +typedef struct rtsp_client_connection_t rtsp_client_connection_t; + +typedef enum { + TERMINATION_STATE_NONE, + TERMINATION_STATE_REQUESTED, + TERMINATION_STATE_INPROGRESS +} termination_state_e; + +/** RTSP client */ +struct rtsp_client_t { + apr_pool_t *pool; + apt_net_client_task_t *task; + + apr_pool_t *sub_pool; + apt_obj_list_t *connection_list; + + void *obj; + const rtsp_client_vtable_t *vtable; +}; + +/** RTSP connection */ +struct rtsp_client_connection_t { + /** Connection base */ + apt_net_client_connection_t *base; + /** RTSP client, connection belongs to */ + rtsp_client_t *client; + /** Element of the connection list in agent */ + apt_list_elem_t *it; + + /** Handle table (rtsp_client_session_t*) */ + apr_hash_t *handle_table; + /** Session table (rtsp_client_session_t*) */ + apr_hash_t *session_table; + + /** Inprogress request/session queue (rtsp_client_session_t*) */ + apt_obj_list_t *inprogress_request_queue; + + /** Last CSeq sent */ + apr_size_t last_cseq; + + char rx_buffer[RTSP_STREAM_BUFFER_SIZE]; + apt_text_stream_t rx_stream; + rtsp_parser_t *parser; + + char tx_buffer[RTSP_STREAM_BUFFER_SIZE]; + apt_text_stream_t tx_stream; + rtsp_generator_t *generator; +}; + +/** RTSP session */ +struct rtsp_client_session_t { + apr_pool_t *pool; + void *obj; + + /** Connection */ + rtsp_client_connection_t *connection; + /** Session identifier */ + apt_str_t id; + + apt_str_t server_ip; + apr_port_t server_port; + apt_str_t resource_location; + + /** In-progress request */ + rtsp_message_t *active_request; + /** Pending request queue (rtsp_message_t*) */ + apt_obj_list_t *pending_request_queue; + + /** Resource table */ + apr_hash_t *resource_table; + + /** termination state (none -> requested -> terminating) */ + termination_state_e term_state; +}; + +typedef enum { + TASK_MSG_SEND_MESSAGE, + TASK_MSG_TERMINATE_SESSION +} task_msg_data_type_e; + +typedef struct task_msg_data_t task_msg_data_t; + +struct task_msg_data_t { + task_msg_data_type_e type; + rtsp_client_t *client; + rtsp_client_session_t *session; + rtsp_message_t *message; +}; + +static apt_bool_t rtsp_client_task_msg_process(apt_task_t *task, apt_task_msg_t *msg); + +static apt_bool_t rtsp_client_message_receive(apt_net_client_task_t *task, apt_net_client_connection_t *connection); + +static const apt_net_client_vtable_t client_vtable = { + rtsp_client_message_receive +}; + +static apt_bool_t rtsp_client_message_send(rtsp_client_t *client, apt_net_client_connection_t *connection, rtsp_message_t *message); +static apt_bool_t rtsp_client_session_message_process(rtsp_client_t *client, rtsp_client_session_t *session, rtsp_message_t *message); +static apt_bool_t rtsp_client_session_request_process(rtsp_client_t *client, rtsp_client_session_t *session, rtsp_message_t *message); +static apt_bool_t rtsp_client_session_response_process(rtsp_client_t *client, rtsp_client_session_t *session, rtsp_message_t *request, rtsp_message_t *response); + +/** Create RTSP client */ +RTSP_DECLARE(rtsp_client_t*) rtsp_client_create( + apr_size_t max_connection_count, + void *obj, + const rtsp_client_vtable_t *handler, + apr_pool_t *pool) +{ + apt_task_vtable_t *vtable; + apt_task_msg_pool_t *msg_pool; + rtsp_client_t *client; + + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Create RTSP Client [%d]",max_connection_count); + client = apr_palloc(pool,sizeof(rtsp_client_t)); + client->pool = pool; + client->obj = obj; + client->vtable = handler; + + msg_pool = apt_task_msg_pool_create_dynamic(sizeof(task_msg_data_t),pool); + + client->task = apt_net_client_task_create(max_connection_count,client,&client_vtable,msg_pool,pool); + if(!client->task) { + return NULL; + } + + vtable = apt_net_client_task_vtable_get(client->task); + if(vtable) { + vtable->process_msg = rtsp_client_task_msg_process; + } + + client->sub_pool = apt_subpool_create(pool); + client->connection_list = NULL; + return client; +} + +/** Destroy RTSP client */ +RTSP_DECLARE(apt_bool_t) rtsp_client_destroy(rtsp_client_t *client) +{ + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Destroy RTSP Client"); + return apt_net_client_task_destroy(client->task); +} + +/** Start connection agent */ +RTSP_DECLARE(apt_bool_t) rtsp_client_start(rtsp_client_t *client) +{ + return apt_net_client_task_start(client->task); +} + +/** Terminate connection agent */ +RTSP_DECLARE(apt_bool_t) rtsp_client_terminate(rtsp_client_t *client) +{ + return apt_net_client_task_terminate(client->task); +} + +/** Get task */ +RTSP_DECLARE(apt_task_t*) rtsp_client_task_get(rtsp_client_t *client) +{ + return apt_net_client_task_base_get(client->task); +} + +/** Get external object */ +RTSP_DECLARE(void*) rtsp_client_object_get(rtsp_client_t *client) +{ + return client->obj; +} + +/** Get object associated with the session */ +RTSP_DECLARE(void*) rtsp_client_session_object_get(const rtsp_client_session_t *session) +{ + return session->obj; +} + +/** Set object associated with the session */ +RTSP_DECLARE(void) rtsp_client_session_object_set(rtsp_client_session_t *session, void *obj) +{ + session->obj = obj; +} + +/** Get the session identifier */ +RTSP_DECLARE(const apt_str_t*) rtsp_client_session_id_get(const rtsp_client_session_t *session) +{ + return &session->id; +} + +/** Signal task message */ +static apt_bool_t rtsp_client_control_message_signal( + task_msg_data_type_e type, + rtsp_client_t *client, + rtsp_client_session_t *session, + rtsp_message_t *message) +{ + apt_task_t *task = apt_net_client_task_base_get(client->task); + apt_task_msg_t *task_msg = apt_task_msg_get(task); + if(task_msg) { + task_msg_data_t *data = (task_msg_data_t*)task_msg->data; + data->type = type; + data->client = client; + data->session = session; + data->message = message; + apt_task_msg_signal(task,task_msg); + } + return TRUE; +} + +/** Create RTSP session handle */ +RTSP_DECLARE(rtsp_client_session_t*) rtsp_client_session_create( + rtsp_client_t *client, + const char *server_ip, + apr_port_t server_port, + const char *resource_location) +{ + rtsp_client_session_t *session; + apr_pool_t *pool = apt_pool_create(); + session = apr_palloc(pool,sizeof(rtsp_client_session_t)); + session->pool = pool; + session->obj = NULL; + session->connection = NULL; + session->active_request = NULL; + session->pending_request_queue = apt_list_create(pool); + session->resource_table = apr_hash_make(pool); + session->term_state = TERMINATION_STATE_NONE; + + apt_string_assign(&session->server_ip,server_ip,pool); + session->server_port = server_port; + apt_string_assign(&session->resource_location,resource_location,pool); + apt_string_reset(&session->id); + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create RTSP Handle "APT_PTR_FMT,session); + return session; +} + +/** Destroy RTSP session handle */ +RTSP_DECLARE(void) rtsp_client_session_destroy(rtsp_client_session_t *session) +{ + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Destroy RTSP Handle "APT_PTR_FMT,session); + if(session && session->pool) { + apr_pool_destroy(session->pool); + } +} + +/** Signal terminate request */ +RTSP_DECLARE(apt_bool_t) rtsp_client_session_terminate(rtsp_client_t *client, rtsp_client_session_t *session) +{ + return rtsp_client_control_message_signal(TASK_MSG_TERMINATE_SESSION,client,session,NULL); +} + +/** Signal RTSP message */ +RTSP_DECLARE(apt_bool_t) rtsp_client_session_request(rtsp_client_t *client, rtsp_client_session_t *session, rtsp_message_t *message) +{ + return rtsp_client_control_message_signal(TASK_MSG_SEND_MESSAGE,client,session,message); +} + +/* Create RTSP connection */ +static apt_bool_t rtsp_client_connection_create(rtsp_client_t *client, rtsp_client_session_t *session) +{ + rtsp_client_connection_t *rtsp_connection; + apt_net_client_connection_t *connection = apt_net_client_connect(client->task,session->server_ip.buf,session->server_port); + if(!connection) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Connect to RTSP Server %s:%d",session->server_ip.buf,session->server_port); + return FALSE; + } + rtsp_connection = apr_palloc(connection->pool,sizeof(rtsp_client_connection_t)); + rtsp_connection->handle_table = apr_hash_make(connection->pool); + rtsp_connection->session_table = apr_hash_make(connection->pool); + rtsp_connection->inprogress_request_queue = apt_list_create(connection->pool); + apt_text_stream_init(&rtsp_connection->rx_stream,rtsp_connection->rx_buffer,sizeof(rtsp_connection->rx_buffer)-1); + apt_text_stream_init(&rtsp_connection->tx_stream,rtsp_connection->tx_buffer,sizeof(rtsp_connection->tx_buffer)-1); + rtsp_connection->parser = rtsp_parser_create(connection->pool); + rtsp_connection->generator = rtsp_generator_create(connection->pool); + rtsp_connection->last_cseq = 0; + rtsp_connection->base = connection; + connection->obj = rtsp_connection; + if(!client->connection_list) { + client->connection_list = apt_list_create(client->sub_pool); + } + rtsp_connection->client = client; + rtsp_connection->it = apt_list_push_back(client->connection_list,rtsp_connection,connection->pool); + session->connection = rtsp_connection; + return TRUE; +} + +/* Destroy RTSP connection */ +static apt_bool_t rtsp_client_connection_destroy(rtsp_client_t *client, rtsp_client_connection_t *rtsp_connection) +{ + apt_list_elem_remove(client->connection_list,rtsp_connection->it); + apt_net_client_disconnect(client->task,rtsp_connection->base); + + if(apt_list_is_empty(client->connection_list) == TRUE) { + apr_pool_clear(client->sub_pool); + client->connection_list = NULL; + } + return TRUE; +} + +/* Respond to session termination request */ +static apt_bool_t rtsp_client_session_terminate_respond(rtsp_client_t *client, rtsp_client_session_t *session) +{ + rtsp_client_connection_t *rtsp_connection = session->connection; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Remove RTSP Handle "APT_PTR_FMT,session); + apr_hash_set(rtsp_connection->handle_table,session,sizeof(session),NULL); + + session->term_state = TERMINATION_STATE_NONE; + client->vtable->on_session_terminate_response(client,session); + + if(apr_hash_count(rtsp_connection->handle_table) == 0) { + rtsp_client_connection_destroy(client,rtsp_connection); + } + return TRUE; +} + +/* Teardown session resources */ +static apt_bool_t rtsp_client_session_resources_teardown(rtsp_client_t *client, rtsp_client_session_t *session) +{ + void *val; + rtsp_message_t *setup_request; + rtsp_message_t *teardown_request; + apr_hash_index_t *it; + + /* set termination state to in-progress and teardown remaining resources */ + session->term_state = TERMINATION_STATE_INPROGRESS; + it = apr_hash_first(session->pool,session->resource_table); + for(; it; it = apr_hash_next(it)) { + apr_hash_this(it,NULL,NULL,&val); + setup_request = val; + if(!setup_request) continue; + + teardown_request = rtsp_request_create(session->pool); + teardown_request->start_line.common.request_line.resource_name = setup_request->start_line.common.request_line.resource_name; + teardown_request->start_line.common.request_line.method_id = RTSP_METHOD_TEARDOWN; + rtsp_client_session_message_process(client,session,teardown_request); + } + return TRUE; +} + +/* Process session termination request */ +static apt_bool_t rtsp_client_session_terminate_process(rtsp_client_t *client, rtsp_client_session_t *session) +{ + rtsp_client_connection_t *rtsp_connection = session->connection; + if(!rtsp_connection) { + client->vtable->on_session_terminate_response(client,session); + return FALSE; + } + + if(session->active_request) { + /* set termination state to requested */ + session->term_state = TERMINATION_STATE_REQUESTED; + } + else { + rtsp_client_session_resources_teardown(client,session); + + /* respond immediately if no resources left */ + if(apr_hash_count(session->resource_table) == 0) { + rtsp_client_session_terminate_respond(client,session); + } + } + + return TRUE; +} + +static apt_bool_t rtsp_client_session_url_generate(rtsp_client_session_t *session, rtsp_message_t *message) +{ + apt_str_t *url = &message->start_line.common.request_line.url; + if(session->resource_location.length) { + url->buf = apr_psprintf(message->pool,"rtsp://%s:%d/%s/%s", + session->server_ip.buf, + session->server_port, + session->resource_location.buf, + message->start_line.common.request_line.resource_name); + } + else { + url->buf = apr_psprintf(message->pool,"rtsp://%s:%d/%s", + session->server_ip.buf, + session->server_port, + message->start_line.common.request_line.resource_name); + } + url->length = strlen(url->buf); + return TRUE; +} + +static apt_bool_t rtsp_client_request_push(rtsp_client_connection_t *rtsp_connection, rtsp_client_session_t *session, rtsp_message_t *message) +{ + /* add request to inprogress request queue */ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Push RTSP Request to In-Progress Queue "APT_PTRSID_FMT" CSeq:%d", + session, + message->header.session_id.buf ? message->header.session_id.buf : "new", + message->header.cseq); + apt_list_push_back(rtsp_connection->inprogress_request_queue,session,session->pool); + session->active_request = message; + return TRUE; +} + +static apt_bool_t rtsp_client_request_pop(rtsp_client_connection_t *rtsp_connection, rtsp_message_t *response, rtsp_message_t **ret_request, rtsp_client_session_t **ret_session) +{ + rtsp_client_session_t *session; + apt_list_elem_t *elem = apt_list_first_elem_get(rtsp_connection->inprogress_request_queue); + while(elem) { + session = apt_list_elem_object_get(elem); + if(session->active_request && session->active_request->header.cseq == response->header.cseq) { + if(ret_session) { + *ret_session = session; + } + if(ret_request) { + *ret_request = session->active_request; + } + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Pop In-Progress RTSP Request "APT_PTR_FMT" CSeq:%d", + session, + response->header.cseq); + apt_list_elem_remove(rtsp_connection->inprogress_request_queue,elem); + session->active_request = NULL; + return TRUE; + } + elem = apt_list_next_elem_get(rtsp_connection->inprogress_request_queue,elem); + } + return FALSE; +} + +/* Process outgoing RTSP request */ +static apt_bool_t rtsp_client_session_request_process(rtsp_client_t *client, rtsp_client_session_t *session, rtsp_message_t *message) +{ + if(!session->connection) { + /* create RTSP connection */ + if(rtsp_client_connection_create(client,session) == FALSE) { + /* respond with error */ + return FALSE; + } + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Add RTSP Handle "APT_PTR_FMT,session); + apr_hash_set(session->connection->handle_table,session,sizeof(session),session); + } + + rtsp_client_session_url_generate(session,message); + + if(session->id.length) { + message->header.session_id = session->id; + rtsp_header_property_add(&message->header.property_set,RTSP_HEADER_FIELD_SESSION_ID); + } + + message->header.cseq = ++session->connection->last_cseq; + rtsp_header_property_add(&message->header.property_set,RTSP_HEADER_FIELD_CSEQ); + + if(rtsp_client_message_send(client,session->connection->base,message) == FALSE) { + /* respond with error */ + return FALSE; + } + + return rtsp_client_request_push(session->connection,session,message); +} + +/* Process pending RTSP requests */ +static apt_bool_t rtsp_client_session_pending_requests_process(rtsp_client_t *client, rtsp_client_session_t *session) +{ + rtsp_message_t *request = apt_list_pop_front(session->pending_request_queue); + if(!request) { + /* pending queue is empty, no in-progress request */ + return FALSE; + } + + /* process pending request; get the next one, if current is failed */ + do { + rtsp_message_t *response; + if(rtsp_client_session_request_process(client,session,request) == TRUE) { + return TRUE; + } + + /* respond with error */ + response = rtsp_response_create( + request, + RTSP_STATUS_CODE_INTERNAL_SERVER_ERROR, + RTSP_REASON_PHRASE_INTERNAL_SERVER_ERROR, + session->pool); + rtsp_client_session_response_process(client,session,request,response); + + /* process the next pending request / if any */ + request = apt_list_pop_front(session->pending_request_queue); + } + while(request); + + /* no in-progress request */ + return FALSE; +} + + +/* Process outgoing RTSP message */ +static apt_bool_t rtsp_client_session_message_process(rtsp_client_t *client, rtsp_client_session_t *session, rtsp_message_t *message) +{ + if(session->active_request) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Push RTSP Request to Pending Queue "APT_PTR_FMT,session); + apt_list_push_back(session->pending_request_queue,message,message->pool); + return TRUE; + } + + if(rtsp_client_session_request_process(client,session,message) == FALSE) { + /* respond with error in case request cannot be processed */ + rtsp_message_t *response = rtsp_response_create( + message, + RTSP_STATUS_CODE_INTERNAL_SERVER_ERROR, + RTSP_REASON_PHRASE_INTERNAL_SERVER_ERROR, + session->pool); + rtsp_client_session_response_process(client,session,message,response); + } + return TRUE; +} + +/* Process incoming RTSP event (request) */ +static apt_bool_t rtsp_client_session_event_process(rtsp_client_t *client, rtsp_client_connection_t *rtsp_connection, rtsp_message_t *message) +{ + rtsp_message_t *response = NULL; + rtsp_client_session_t *session = NULL; + if(rtsp_header_property_check(&message->header.property_set,RTSP_HEADER_FIELD_SESSION_ID) == TRUE) { + /* find existing session */ + session = apr_hash_get( + rtsp_connection->session_table, + message->header.session_id.buf, + message->header.session_id.length); + } + + if(session) { + response = rtsp_response_create(message,RTSP_STATUS_CODE_OK,RTSP_REASON_PHRASE_OK,message->pool); + if(rtsp_header_property_check(&message->header.property_set,RTSP_HEADER_FIELD_SESSION_ID) == TRUE) { + response->header.session_id = message->header.session_id; + rtsp_header_property_add(&response->header.property_set,RTSP_HEADER_FIELD_SESSION_ID); + } + client->vtable->on_session_event(client,session,message); + } + else { + response = rtsp_response_create(message,RTSP_STATUS_CODE_NOT_FOUND,RTSP_REASON_PHRASE_NOT_FOUND,message->pool); + } + + return rtsp_client_message_send(client,rtsp_connection->base,response); +} + +/* Process incoming RTSP response */ +static apt_bool_t rtsp_client_session_response_process(rtsp_client_t *client, rtsp_client_session_t *session, rtsp_message_t *request, rtsp_message_t *response) +{ + const char *resource_name; + if(request->start_line.common.request_line.method_id == RTSP_METHOD_SETUP && + response->start_line.common.status_line.status_code == RTSP_STATUS_CODE_OK) { + + if(apr_hash_count(session->resource_table) == 0) { + if(rtsp_header_property_check(&response->header.property_set,RTSP_HEADER_FIELD_SESSION_ID) == TRUE) { + session->id = response->header.session_id; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Add RTSP Session "APT_PTRSID_FMT, + session, + session->id.buf); + apr_hash_set(session->connection->session_table,session->id.buf,session->id.length,session); + } + } + + /* add resource */ + resource_name = request->start_line.common.request_line.resource_name; + apr_hash_set(session->resource_table,resource_name,APR_HASH_KEY_STRING,request); + } + else if(request->start_line.common.request_line.method_id == RTSP_METHOD_TEARDOWN) { + /* remove resource */ + resource_name = request->start_line.common.request_line.resource_name; + apr_hash_set(session->resource_table,resource_name,APR_HASH_KEY_STRING,NULL); + + if(apr_hash_count(session->resource_table) == 0) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Remove RTSP Session "APT_PTRSID_FMT, + session, + session->id.buf); + apr_hash_set(session->connection->session_table,session->id.buf,session->id.length,NULL); + } + } + + if(session->term_state != TERMINATION_STATE_INPROGRESS) { + client->vtable->on_session_response(client,session,request,response); + } + + return TRUE; +} + +/* Raise RTSP session terminate event */ +static apt_bool_t rtsp_client_session_terminate_raise(rtsp_client_t *client, rtsp_client_session_t *session) +{ + rtsp_message_t *request; + rtsp_message_t *response; + + /* cancel pending requests */ + do { + request = apt_list_pop_front(session->pending_request_queue); + if(request) { + response = rtsp_response_create( + session->active_request, + RTSP_STATUS_CODE_INTERNAL_SERVER_ERROR, + RTSP_REASON_PHRASE_INTERNAL_SERVER_ERROR, + session->pool); + rtsp_client_session_response_process(client,session,request,response); + } + } + while(request); + + + if(session->term_state == TERMINATION_STATE_NONE) { + client->vtable->on_session_terminate_event(client,session); + } + else { + rtsp_client_session_terminate_respond(client,session); + } + return TRUE; +} + +/* RTSP connection disconnected */ +static apt_bool_t rtsp_client_on_disconnect(rtsp_client_t *client, rtsp_client_connection_t *rtsp_connection) +{ + rtsp_client_session_t *session; + rtsp_message_t *request; + rtsp_message_t *response; + apr_size_t remaining_handles = 0; + apr_size_t cancelled_requests = 0; + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"TCP Peer Disconnected %s", rtsp_connection->base->id); + apt_net_client_connection_close(client->task,rtsp_connection->base); + + /* Cancel in-progreess requests */ + do { + session = apt_list_pop_front(rtsp_connection->inprogress_request_queue); + if(session && session->active_request) { + request = session->active_request; + session->active_request = NULL; + cancelled_requests++; + + response = rtsp_response_create( + request, + RTSP_STATUS_CODE_INTERNAL_SERVER_ERROR, + RTSP_REASON_PHRASE_INTERNAL_SERVER_ERROR, + session->pool); + rtsp_client_session_response_process(client,session,request,response); + } + } + while(session); + + /* Walk through RTSP handles and raise termination event for them */ + remaining_handles = apr_hash_count(rtsp_connection->handle_table); + if(remaining_handles) { + void *val; + apr_hash_index_t *it; + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Terminate Remaining RTSP Handles [%d]",remaining_handles); + it = apr_hash_first(rtsp_connection->base->pool,rtsp_connection->session_table); + for(; it; it = apr_hash_next(it)) { + apr_hash_this(it,NULL,NULL,&val); + session = val; + if(session) { + rtsp_client_session_terminate_raise(client,session); + } + } + remaining_handles = apr_hash_count(rtsp_connection->session_table); + } + + if(!remaining_handles && !cancelled_requests) { + rtsp_client_connection_destroy(client,rtsp_connection); + } + return TRUE; +} + +/* Send RTSP message through RTSP connection */ +static apt_bool_t rtsp_client_message_send(rtsp_client_t *client, apt_net_client_connection_t *connection, rtsp_message_t *message) +{ + apt_bool_t status = FALSE; + rtsp_client_connection_t *rtsp_connection; + apt_text_stream_t *stream; + rtsp_stream_result_e result; + + if(!connection || !connection->sock) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No RTSP Connection"); + return FALSE; + } + rtsp_connection = connection->obj; + stream = &rtsp_connection->tx_stream; + + rtsp_generator_message_set(rtsp_connection->generator,message); + do { + stream->text.length = sizeof(rtsp_connection->tx_buffer)-1; + stream->pos = stream->text.buf; + result = rtsp_generator_run(rtsp_connection->generator,stream); + if(result == RTSP_STREAM_MESSAGE_COMPLETE || result == RTSP_STREAM_MESSAGE_TRUNCATED) { + stream->text.length = stream->pos - stream->text.buf; + *stream->pos = '\0'; + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Send RTSP Stream %s [%lu bytes]\n%s", + connection->id, + stream->text.length, + stream->text.buf); + if(apr_socket_send(connection->sock,stream->text.buf,&stream->text.length) == APR_SUCCESS) { + status = TRUE; + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Send RTSP Stream"); + } + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Generate RTSP Stream"); + } + } + while(result == RTSP_STREAM_MESSAGE_TRUNCATED); + + return status; +} + +static apt_bool_t rtsp_client_message_handler(void *obj, rtsp_message_t *message, rtsp_stream_result_e result) +{ + rtsp_client_connection_t *rtsp_connection = obj; + if(result != RTSP_STREAM_MESSAGE_COMPLETE) { + /* message is not completely parsed, nothing to do */ + return TRUE; + } + /* process parsed message */ + if(message->start_line.message_type == RTSP_MESSAGE_TYPE_RESPONSE) { + rtsp_message_t *request; + rtsp_client_session_t *session; + /* at first, pop in-progress request/session */ + if(rtsp_client_request_pop(rtsp_connection,message,&request,&session) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unexpected RTSP Response Received CSeq:%d",message->header.cseq); + return FALSE; + } + + /* next, process session response */ + rtsp_client_session_response_process(rtsp_connection->client,session,request,message); + + /* process session pending requests */ + if(rtsp_client_session_pending_requests_process(rtsp_connection->client,session) == FALSE) { + /* no in-progress request, check the termination state now */ + if(session->term_state != TERMINATION_STATE_NONE) { + if(session->term_state == TERMINATION_STATE_REQUESTED) { + rtsp_client_session_resources_teardown(rtsp_connection->client,session); + } + + /* respond if no resources left */ + if(apr_hash_count(session->resource_table) == 0) { + rtsp_client_session_terminate_respond(rtsp_connection->client,session); + } + } + } + } + else if(message->start_line.message_type == RTSP_MESSAGE_TYPE_REQUEST) { + rtsp_client_session_event_process(rtsp_connection->client,rtsp_connection,message); + } + return TRUE; +} + +/* Receive RTSP message through RTSP connection */ +static apt_bool_t rtsp_client_message_receive(apt_net_client_task_t *task, apt_net_client_connection_t *connection) +{ + rtsp_client_t *client = apt_net_client_task_object_get(task); + rtsp_client_connection_t *rtsp_connection; + apr_status_t status; + apr_size_t offset; + apr_size_t length; + apt_text_stream_t *stream; + + if(!connection || !connection->sock) { + return FALSE; + } + rtsp_connection = connection->obj; + stream = &rtsp_connection->rx_stream; + + /* init length of the stream */ + stream->text.length = sizeof(rtsp_connection->rx_buffer)-1; + /* calculate offset remaining from the previous receive / if any */ + offset = stream->pos - stream->text.buf; + /* calculate available length */ + length = stream->text.length - offset; + status = apr_socket_recv(connection->sock,stream->pos,&length); + if(status == APR_EOF || length == 0) { + return rtsp_client_on_disconnect(client,rtsp_connection); + } + /* calculate actual length of the stream */ + stream->text.length = offset + length; + stream->pos[length] = '\0'; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Receive RTSP Stream %s [%lu bytes]\n%s", + connection->id, + length, + stream->pos); + + /* reset pos */ + stream->pos = stream->text.buf; + /* walk through the stream parsing RTSP messages */ + return rtsp_stream_walk(rtsp_connection->parser,stream,rtsp_client_message_handler,rtsp_connection); +} + +/* Process task message */ +static apt_bool_t rtsp_client_task_msg_process(apt_task_t *task, apt_task_msg_t *task_msg) +{ + apt_net_client_task_t *net_task = apt_task_object_get(task); + rtsp_client_t *client = apt_net_client_task_object_get(net_task); + + task_msg_data_t *data = (task_msg_data_t*) task_msg->data; + switch(data->type) { + case TASK_MSG_SEND_MESSAGE: + rtsp_client_session_message_process(client,data->session,data->message); + break; + case TASK_MSG_TERMINATE_SESSION: + rtsp_client_session_terminate_process(client,data->session); + break; + } + + return TRUE; +} diff --git a/libs/unimrcp/libs/uni-rtsp/src/rtsp_header.c b/libs/unimrcp/libs/uni-rtsp/src/rtsp_header.c new file mode 100644 index 0000000000..9d6e12c532 --- /dev/null +++ b/libs/unimrcp/libs/uni-rtsp/src/rtsp_header.c @@ -0,0 +1,394 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "rtsp_header.h" +#include "apt_string_table.h" + +/** String table of RTSP header fields (rtsp_header_field_id) */ +static const apt_str_table_item_t rtsp_header_string_table[] = { + {{"CSeq", 4},1}, + {{"Transport", 9},0}, + {{"Session", 7},0}, + {{"RTP-Info", 8},0}, + {{"Content-Type", 12},8}, + {{"Content-Length",14},8} +}; + +/** String table of RTSP content types (rtsp_content_type) */ +static const apt_str_table_item_t rtsp_content_type_string_table[] = { + {{"application/sdp", 15},12}, + {{"application/mrcp",16},12} +}; + +/** String table of RTSP transport protocols (rtsp_transport_e) */ +static const apt_str_table_item_t rtsp_transport_string_table[] = { + {{"RTP", 3},0} +}; + +/** String table of RTSP lower transport protocols (rtsp_lower_transport_e) */ +static const apt_str_table_item_t rtsp_lower_transport_string_table[] = { + {{"UDP", 3},0}, + {{"TCP", 3},0} +}; + +/** String table of RTSP transport profiles (rtsp_profile_e) */ +static const apt_str_table_item_t rtsp_profile_string_table[] = { + {{"AVP", 3},0}, + {{"SAVP",4},0} +}; + +/** String table of RTSP transport attributes (rtsp_transport_attrib_e) */ +static const apt_str_table_item_t rtsp_transport_attrib_string_table[] = { + {{"client_port", 11},0}, + {{"server_port", 11},1}, + {{"source", 6}, 1}, + {{"destination", 11},0}, + {{"unicast", 7}, 0}, + {{"multicast", 9}, 0} +}; + +/** Parse RTSP transport port range */ +static apt_bool_t rtsp_port_range_parse(rtsp_port_range_t *port_range, apt_text_stream_t *stream) +{ + apt_str_t value; + /* read min value */ + if(apt_text_field_read(stream,'-',TRUE,&value) == FALSE) { + return FALSE; + } + port_range->min = (apr_port_t)apt_size_value_parse(&value); + + /* read optional max value */ + if(apt_text_field_read(stream,';',TRUE,&value) == TRUE) { + port_range->max = (apr_port_t)apt_size_value_parse(&value); + } + + return TRUE; +} + +/** Generate RTSP transport port range */ +static apt_bool_t rtsp_port_range_generate(rtsp_transport_attrib_e attrib, const rtsp_port_range_t *port_range, apt_text_stream_t *text_stream) +{ + const apt_str_t *str; + str = apt_string_table_str_get(rtsp_transport_attrib_string_table,RTSP_TRANSPORT_ATTRIB_COUNT,attrib); + if(!str) { + return FALSE; + } + apt_string_value_generate(str,text_stream); + apt_text_char_insert(text_stream,'='); + apt_size_value_generate(port_range->min,text_stream); + if(port_range->max > port_range->min) { + apt_text_char_insert(text_stream,'-'); + apt_size_value_generate(port_range->max,text_stream); + } + return TRUE; +} + +/** Parse RTSP source/destination address */ +static apt_bool_t rtsp_address_parse(apt_str_t *address, apt_text_stream_t *stream, apr_pool_t *pool) +{ + apt_str_t value; + /* read min value */ + if(apt_text_field_read(stream,';',TRUE,&value) == FALSE) { + return FALSE; + } + apt_string_copy(address,&value,pool); + return TRUE; +} + +/** Parse RTSP transport */ +static apt_bool_t rtsp_transport_attrib_parse(rtsp_transport_t *transport, const apt_str_t *field, apr_pool_t *pool) +{ + rtsp_transport_attrib_e attrib; + apt_str_t name; + apt_text_stream_t stream; + + stream.text = *field; + stream.pos = stream.text.buf; + + /* read attrib name */ + if(apt_text_field_read(&stream,'=',TRUE,&name) == FALSE) { + return FALSE; + } + + attrib = apt_string_table_id_find(rtsp_transport_attrib_string_table,RTSP_TRANSPORT_ATTRIB_COUNT,&name); + switch(attrib) { + case RTSP_TRANSPORT_ATTRIB_CLIENT_PORT: + rtsp_port_range_parse(&transport->client_port_range,&stream); + break; + case RTSP_TRANSPORT_ATTRIB_SERVER_PORT: + rtsp_port_range_parse(&transport->client_port_range,&stream); + break; + case RTSP_TRANSPORT_ATTRIB_SOURCE: + rtsp_address_parse(&transport->source,&stream,pool); + break; + case RTSP_TRANSPORT_ATTRIB_DESTINATION: + rtsp_address_parse(&transport->destination,&stream,pool); + break; + case RTSP_TRANSPORT_ATTRIB_UNICAST: + transport->delivery = RTSP_DELIVERY_UNICAST; + break; + case RTSP_TRANSPORT_ATTRIB_MULTICAST: + transport->delivery = RTSP_DELIVERY_MULTICAST; + break; + default: + break; + } + return TRUE; +} + +/** Parse RTSP transport protocol (RTP/AVP[/UDP]) */ +static apt_bool_t rtsp_transport_protocol_parse(rtsp_transport_t *transport, const apt_str_t *value) +{ + apt_str_t field; + apt_text_stream_t stream; + + stream.text = *value; + stream.pos = stream.text.buf; + + /* set the defaults */ + transport->protocol = RTSP_TRANSPORT_RTP; + transport->profile = RTSP_PROFILE_AVP; + transport->lower_protocol = RTSP_LOWER_TRANSPORT_UDP; + + /* read transport protocol (RTP) */ + if(apt_text_field_read(&stream,'/',TRUE,&field) == FALSE) { + return FALSE; + } + transport->protocol = apt_string_table_id_find(rtsp_transport_string_table,RTSP_TRANSPORT_COUNT,&field); + if(transport->protocol >= RTSP_TRANSPORT_COUNT) { + return FALSE; + } + + /* read transport profile (AVP) */ + if(apt_text_field_read(&stream,'/',TRUE,&field) == FALSE) { + return FALSE; + } + transport->profile = apt_string_table_id_find(rtsp_profile_string_table,RTSP_PROFILE_COUNT,&field); + if(transport->profile >= RTSP_PROFILE_COUNT) { + return FALSE; + } + + /* read optional lower transport protocol (UDP) */ + if(apt_text_field_read(&stream,'/',TRUE,&field) == TRUE) { + transport->lower_protocol = apt_string_table_id_find(rtsp_lower_transport_string_table,RTSP_LOWER_TRANSPORT_COUNT,&field); + if(transport->lower_protocol >= RTSP_LOWER_TRANSPORT_COUNT) { + return FALSE; + } + } + + return TRUE; +} + +/** Parse RTSP transport */ +static apt_bool_t rtsp_transport_parse(rtsp_transport_t *transport, const apt_str_t *line, apr_pool_t *pool) +{ + apt_str_t field; + apt_text_stream_t stream; + + stream.text = *line; + stream.pos = stream.text.buf; + /* read transport protocol (RTP/AVP[/UDP]) */ + if(apt_text_field_read(&stream,';',TRUE,&field) == FALSE) { + return FALSE; + } + + /* parse transport protocol (RTP/AVP[/UDP]) */ + if(rtsp_transport_protocol_parse(transport,&field) == FALSE) { + return FALSE; + } + + /* read transport attributes */ + while(apt_text_field_read(&stream,';',TRUE,&field) == TRUE) { + rtsp_transport_attrib_parse(transport,&field,pool); + } + + return TRUE; +} + +/** Generate RTSP transport */ +static apt_bool_t rtsp_transport_generate(rtsp_transport_t *transport, apt_text_stream_t *text_stream) +{ + const apt_str_t *protocol = apt_string_table_str_get(rtsp_transport_string_table,RTSP_TRANSPORT_COUNT,transport->protocol); + const apt_str_t *profile = apt_string_table_str_get(rtsp_profile_string_table,RTSP_PROFILE_COUNT,transport->profile); + if(!protocol || !profile) { + return FALSE; + } + apt_string_value_generate(protocol,text_stream); + apt_text_char_insert(text_stream,'/'); + apt_string_value_generate(profile,text_stream); + + if(transport->delivery != RTSP_DELIVERY_NONE) { + const apt_str_t *delivery = NULL; + rtsp_transport_attrib_e attrib = RTSP_TRANSPORT_ATTRIB_NONE; + if(transport->delivery == RTSP_DELIVERY_UNICAST) { + attrib = RTSP_TRANSPORT_ATTRIB_UNICAST; + } + else if(transport->delivery == RTSP_DELIVERY_MULTICAST) { + attrib = RTSP_TRANSPORT_ATTRIB_MULTICAST; + } + delivery = apt_string_table_str_get(rtsp_transport_attrib_string_table,RTSP_TRANSPORT_ATTRIB_COUNT,attrib); + if(!delivery) { + return FALSE; + } + + apt_text_char_insert(text_stream,';'); + apt_string_value_generate(delivery,text_stream); + } + + if(rtsp_port_range_is_valid(&transport->client_port_range) == TRUE) { + apt_text_char_insert(text_stream,';'); + rtsp_port_range_generate(RTSP_TRANSPORT_ATTRIB_CLIENT_PORT,&transport->client_port_range,text_stream); + } + if(rtsp_port_range_is_valid(&transport->server_port_range) == TRUE) { + apt_text_char_insert(text_stream,';'); + rtsp_port_range_generate(RTSP_TRANSPORT_ATTRIB_SERVER_PORT,&transport->server_port_range,text_stream); + } + return TRUE; +} + +/** Parse RTSP transport */ +static apt_bool_t rtsp_session_id_parse(apt_str_t *session_id, const apt_str_t *value, apr_pool_t *pool) +{ + char *sep; + if(!value->buf) { + return FALSE; + } + apt_string_copy(session_id,value,pool); + sep = strchr(session_id->buf,';'); + if(sep) { + session_id->length = sep - session_id->buf; + *sep = '\0'; + } + return TRUE; +} + +/** Parse RTSP header field */ +static apt_bool_t rtsp_header_field_parse(rtsp_header_t *header, rtsp_header_field_id id, const apt_str_t *value, apr_pool_t *pool) +{ + apt_bool_t status = TRUE; + switch(id) { + case RTSP_HEADER_FIELD_CSEQ: + header->cseq = apt_size_value_parse(value); + break; + case RTSP_HEADER_FIELD_TRANSPORT: + status = rtsp_transport_parse(&header->transport,value,pool); + break; + case RTSP_HEADER_FIELD_SESSION_ID: + status = rtsp_session_id_parse(&header->session_id,value,pool); + break; + case RTSP_HEADER_FIELD_RTP_INFO: + apt_string_copy(&header->rtp_info,value,pool); + break; + case RTSP_HEADER_FIELD_CONTENT_TYPE: + header->content_type = apt_string_table_id_find(rtsp_content_type_string_table,RTSP_CONTENT_TYPE_COUNT,value); + break; + case RTSP_HEADER_FIELD_CONTENT_LENGTH: + header->content_length = apt_size_value_parse(value); + break; + default: + status = FALSE; + } + return status; +} + +/** Generate RTSP header field */ +static apr_size_t rtsp_header_field_generate(rtsp_header_t *header, apr_size_t id, apt_text_stream_t *value) +{ + switch(id) { + case RTSP_HEADER_FIELD_CSEQ: + apt_size_value_generate(header->cseq,value); + break; + case RTSP_HEADER_FIELD_TRANSPORT: + rtsp_transport_generate(&header->transport,value); + break; + case RTSP_HEADER_FIELD_SESSION_ID: + apt_string_value_generate(&header->session_id,value); + break; + case RTSP_HEADER_FIELD_RTP_INFO: + apt_string_value_generate(&header->rtp_info,value); + break; + case RTSP_HEADER_FIELD_CONTENT_TYPE: + { + const apt_str_t *name = apt_string_table_str_get(rtsp_content_type_string_table,RTSP_CONTENT_TYPE_COUNT,header->content_type); + if(name) { + apt_string_value_generate(name,value); + } + break; + } + case RTSP_HEADER_FIELD_CONTENT_LENGTH: + apt_size_value_generate(header->content_length,value); + break; + default: + break; + } + return TRUE; +} + +/** Parse RTSP header */ +RTSP_DECLARE(apt_bool_t) rtsp_header_parse(rtsp_header_t *header, apt_text_stream_t *text_stream, apr_pool_t *pool) +{ + apt_pair_t pair; + apt_bool_t result = FALSE; + + do { + if(apt_text_header_read(text_stream,&pair) == TRUE) { + if(pair.name.length) { + /* parse header_field (name/value) */ + rtsp_header_field_id id = apt_string_table_id_find(rtsp_header_string_table,RTSP_HEADER_FIELD_COUNT,&pair.name); + if(id < RTSP_HEADER_FIELD_COUNT) { + if(rtsp_header_field_parse(header,id,&pair.value,pool) == TRUE) { + rtsp_header_property_add(&header->property_set,id); + } + } + } + else { + /* empty header -> exit */ + result = TRUE; + break; + } + } + } + while(apt_text_is_eos(text_stream) == FALSE); + + return result; +} + +/** Generate RTSP header */ +RTSP_DECLARE(apt_bool_t) rtsp_header_generate(rtsp_header_t *header, apt_text_stream_t *text_stream) +{ + const apt_str_t *name; + apr_size_t i; + rtsp_header_property_t property_set; + + property_set = header->property_set; + for(i=0; ipool = pool; + rtsp_start_line_init(&message->start_line,message_type); + rtsp_header_init(&message->header); + apt_string_reset(&message->body); +} + +/** Create RTSP message */ +RTSP_DECLARE(rtsp_message_t*) rtsp_message_create(rtsp_message_type_e message_type, apr_pool_t *pool) +{ + rtsp_message_t *message = apr_palloc(pool,sizeof(rtsp_message_t)); + rtsp_message_init(message,message_type,pool); + return message; +} + +/** Create RTSP request message */ +RTSP_DECLARE(rtsp_message_t*) rtsp_request_create(apr_pool_t *pool) +{ + rtsp_message_t *request = rtsp_message_create(RTSP_MESSAGE_TYPE_REQUEST,pool); + request->start_line.common.request_line.version = RTSP_VERSION_1; + return request; +} + +/** Create RTSP response message */ +RTSP_DECLARE(rtsp_message_t*) rtsp_response_create(const rtsp_message_t *request, rtsp_status_code_e status_code, rtsp_reason_phrase_e reason, apr_pool_t *pool) +{ + const apt_str_t *reason_str; + rtsp_status_line_t *status_line; + rtsp_message_t *response = rtsp_message_create(RTSP_MESSAGE_TYPE_RESPONSE,request->pool); + status_line = &response->start_line.common.status_line; + status_line->version = request->start_line.common.request_line.version; + status_line->status_code = status_code; + reason_str = rtsp_reason_phrase_get(reason); + if(reason_str) { + apt_string_copy(&status_line->reason,reason_str,request->pool); + } + + if(rtsp_header_property_check(&request->header.property_set,RTSP_HEADER_FIELD_CSEQ) == TRUE) { + response->header.cseq = request->header.cseq; + rtsp_header_property_add(&response->header.property_set,RTSP_HEADER_FIELD_CSEQ); + } + + return response; +} + +/** Destroy RTSP message */ +RTSP_DECLARE(void) rtsp_message_destroy(rtsp_message_t *message) +{ + /* nothing to do message is allocated from pool */ +} diff --git a/libs/unimrcp/libs/uni-rtsp/src/rtsp_server.c b/libs/unimrcp/libs/uni-rtsp/src/rtsp_server.c new file mode 100644 index 0000000000..d568e9741c --- /dev/null +++ b/libs/unimrcp/libs/uni-rtsp/src/rtsp_server.c @@ -0,0 +1,650 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include "rtsp_server.h" +#include "rtsp_stream.h" +#include "apt_net_server_task.h" +#include "apt_text_stream.h" +#include "apt_pool.h" +#include "apt_obj_list.h" +#include "apt_log.h" + +#define RTSP_SESSION_ID_HEX_STRING_LENGTH 16 +#define RTSP_STREAM_BUFFER_SIZE 1024 + +typedef struct rtsp_server_connection_t rtsp_server_connection_t; + +/** RTSP server */ +struct rtsp_server_t { + apr_pool_t *pool; + apt_net_server_task_t *task; + + apr_pool_t *sub_pool; + apt_obj_list_t *connection_list; + + void *obj; + const rtsp_server_vtable_t *vtable; +}; + +/** RTSP connection */ +struct rtsp_server_connection_t { + /** Connection base */ + apt_net_server_connection_t *base; + + /** RTSP server, connection belongs to */ + rtsp_server_t *server; + /** Element of the connection list in agent */ + apt_list_elem_t *it; + + /** Session table (rtsp_server_session_t*) */ + apr_hash_t *session_table; + + char rx_buffer[RTSP_STREAM_BUFFER_SIZE]; + apt_text_stream_t rx_stream; + rtsp_parser_t *parser; + + char tx_buffer[RTSP_STREAM_BUFFER_SIZE]; + apt_text_stream_t tx_stream; + rtsp_generator_t *generator; +}; + +/** RTSP session */ +struct rtsp_server_session_t { + apr_pool_t *pool; + void *obj; + rtsp_server_connection_t *connection; + + /** Session identifier */ + apt_str_t id; + apt_str_t url; + + /** Last cseq sent */ + apr_size_t last_cseq; + + /** In-progress request */ + rtsp_message_t *active_request; + /** request queue */ + apt_obj_list_t *request_queue; + + /** In-progress termination request */ + apt_bool_t terminating; +}; + +typedef enum { + TASK_MSG_SEND_MESSAGE, + TASK_MSG_TERMINATE_SESSION +} task_msg_data_type_e; + +typedef struct task_msg_data_t task_msg_data_t; + +struct task_msg_data_t { + task_msg_data_type_e type; + rtsp_server_t *server; + rtsp_server_session_t *session; + rtsp_message_t *message; +}; + +static apt_bool_t rtsp_server_task_msg_process(apt_task_t *task, apt_task_msg_t *msg); + +static apt_bool_t rtsp_server_on_connect(apt_net_server_task_t *task, apt_net_server_connection_t *connection); +static apt_bool_t rtsp_server_on_disconnect(apt_net_server_task_t *task, apt_net_server_connection_t *connection); +static apt_bool_t rtsp_server_message_receive(apt_net_server_task_t *task, apt_net_server_connection_t *connection); + +static const apt_net_server_vtable_t server_vtable = { + rtsp_server_on_connect, + rtsp_server_on_disconnect, + rtsp_server_message_receive +}; + +static apt_bool_t rtsp_server_message_send(rtsp_server_t *server, apt_net_server_connection_t *connection, rtsp_message_t *message); + +/** Create RTSP server */ +RTSP_DECLARE(rtsp_server_t*) rtsp_server_create( + const char *listen_ip, + apr_port_t listen_port, + apr_size_t max_connection_count, + void *obj, + const rtsp_server_vtable_t *handler, + apr_pool_t *pool) +{ + apt_task_vtable_t *vtable; + apt_task_msg_pool_t *msg_pool; + rtsp_server_t *server; + + if(!listen_ip) { + return NULL; + } + + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Create RTSP Server %s:%hu [%d]",listen_ip,listen_port,max_connection_count); + server = apr_palloc(pool,sizeof(rtsp_server_t)); + server->pool = pool; + server->obj = obj; + server->vtable = handler; + + msg_pool = apt_task_msg_pool_create_dynamic(sizeof(task_msg_data_t),pool); + + server->task = apt_net_server_task_create( + listen_ip,listen_port,max_connection_count, + server,&server_vtable,msg_pool,pool); + if(!server->task) { + return NULL; + } + + vtable = apt_net_server_task_vtable_get(server->task); + if(vtable) { + vtable->process_msg = rtsp_server_task_msg_process; + } + + server->sub_pool = apt_subpool_create(pool); + server->connection_list = NULL; + return server; +} + +/** Destroy RTSP server */ +RTSP_DECLARE(apt_bool_t) rtsp_server_destroy(rtsp_server_t *server) +{ + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Destroy RTSP Server"); + return apt_net_server_task_destroy(server->task); +} + +/** Start connection agent. */ +RTSP_DECLARE(apt_bool_t) rtsp_server_start(rtsp_server_t *server) +{ + return apt_net_server_task_start(server->task); +} + +/** Terminate connection agent. */ +RTSP_DECLARE(apt_bool_t) rtsp_server_terminate(rtsp_server_t *server) +{ + return apt_net_server_task_terminate(server->task); +} + +/** Get task */ +RTSP_DECLARE(apt_task_t*) rtsp_server_task_get(rtsp_server_t *server) +{ + return apt_net_server_task_base_get(server->task); +} + +/** Get external object */ +RTSP_DECLARE(void*) rtsp_server_object_get(rtsp_server_t *server) +{ + return server->obj; +} + +/** Get object associated with the session */ +RTSP_DECLARE(void*) rtsp_server_session_object_get(const rtsp_server_session_t *session) +{ + return session->obj; +} + +/** Set object associated with the session */ +RTSP_DECLARE(void) rtsp_server_session_object_set(rtsp_server_session_t *session, void *obj) +{ + session->obj = obj; +} + +/** Get the session identifier */ +RTSP_DECLARE(const apt_str_t*) rtsp_server_session_id_get(const rtsp_server_session_t *session) +{ + return &session->id; +} + +/** Get active request */ +RTSP_DECLARE(const rtsp_message_t*) rtsp_server_session_request_get(const rtsp_server_session_t *session) +{ + return session->active_request; +} + +/** Get the session destination (client) ip address */ +RTSP_DECLARE(const char*) rtsp_server_session_destination_get(const rtsp_server_session_t *session) +{ + if(session->connection) { + return session->connection->base->client_ip; + } + return NULL; +} + +/** Signal task message */ +static apt_bool_t rtsp_server_control_message_signal( + task_msg_data_type_e type, + rtsp_server_t *server, + rtsp_server_session_t *session, + rtsp_message_t *message) +{ + apt_task_t *task = apt_net_server_task_base_get(server->task); + apt_task_msg_t *task_msg = apt_task_msg_get(task); + if(task_msg) { + task_msg_data_t *data = (task_msg_data_t*)task_msg->data; + data->type = type; + data->server = server; + data->session = session; + data->message = message; + apt_task_msg_signal(task,task_msg); + } + return TRUE; +} + +/** Signal RTSP message */ +RTSP_DECLARE(apt_bool_t) rtsp_server_session_respond(rtsp_server_t *server, rtsp_server_session_t *session, rtsp_message_t *message) +{ + return rtsp_server_control_message_signal(TASK_MSG_SEND_MESSAGE,server,session,message); +} + +/** Signal terminate response */ +RTSP_DECLARE(apt_bool_t) rtsp_server_session_terminate(rtsp_server_t *server, rtsp_server_session_t *session) +{ + return rtsp_server_control_message_signal(TASK_MSG_TERMINATE_SESSION,server,session,NULL); +} + +/* Create RTSP session */ +static rtsp_server_session_t* rtsp_server_session_create(rtsp_server_t *server) +{ + rtsp_server_session_t *session; + apr_pool_t *pool = apt_pool_create(); + session = apr_palloc(pool,sizeof(rtsp_server_session_t)); + session->pool = pool; + session->obj = NULL; + session->last_cseq = 0; + session->active_request = NULL; + session->request_queue = apt_list_create(pool); + session->terminating = FALSE; + + apt_string_reset(&session->url); + apt_unique_id_generate(&session->id,RTSP_SESSION_ID_HEX_STRING_LENGTH,pool); + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create RTSP Session "APT_SID_FMT,session->id.buf); + if(server->vtable->create_session(server,session) != TRUE) { + apr_pool_destroy(pool); + return NULL; + } + return session; +} + +/* Destroy RTSP session */ +static void rtsp_server_session_destroy(rtsp_server_session_t *session) +{ + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Destroy RTSP Session "APT_SID_FMT,session->id.buf); + if(session && session->pool) { + apr_pool_destroy(session->pool); + } +} + +/* Finally terminate RTSP session */ +static apt_bool_t rtsp_server_session_do_terminate(rtsp_server_t *server, rtsp_server_session_t *session) +{ + rtsp_server_connection_t *rtsp_connection = session->connection; + + if(session->active_request) { + rtsp_message_t *response = rtsp_response_create(session->active_request, + RTSP_STATUS_CODE_OK,RTSP_REASON_PHRASE_OK,session->active_request->pool); + if(response) { + if(session->id.buf) { + response->header.session_id = session->id; + rtsp_header_property_add(&response->header.property_set,RTSP_HEADER_FIELD_SESSION_ID); + } + + if(rtsp_connection) { + rtsp_server_message_send(server,rtsp_connection->base,response); + } + } + } + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Remove RTSP Session "APT_SID_FMT,session->id.buf); + apr_hash_set(rtsp_connection->session_table,session->id.buf,session->id.length,NULL); + rtsp_server_session_destroy(session); + + if(rtsp_connection && !rtsp_connection->it) { + if(apr_hash_count(rtsp_connection->session_table) == 0) { + apt_net_server_connection_destroy(rtsp_connection->base); + } + } + return TRUE; +} + +static apt_bool_t rtsp_server_error_respond(rtsp_server_t *server, rtsp_server_connection_t *rtsp_connection, rtsp_message_t *request, + rtsp_status_code_e status_code, rtsp_reason_phrase_e reason) +{ + /* send error response to client */ + rtsp_message_t *response = rtsp_response_create(request,status_code,reason,request->pool); + if(rtsp_server_message_send(server,rtsp_connection->base,response) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Send RTSP Response"); + return FALSE; + } + return TRUE; +} + +static apt_bool_t rtsp_server_session_terminate_request(rtsp_server_t *server, rtsp_server_session_t *session) +{ + session->terminating = TRUE; + return server->vtable->terminate_session(server,session); +} + +static apt_bool_t rtsp_server_session_message_handle(rtsp_server_t *server, rtsp_server_session_t *session, rtsp_message_t *message) +{ + if(message->start_line.common.request_line.method_id == RTSP_METHOD_TEARDOWN) { + rtsp_server_session_terminate_request(server,session); + return TRUE; + } + + if(server->vtable->handle_message(server,session,message) != TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Handle Message "APT_SID_FMT,session->id.buf); + rtsp_server_error_respond(server,session->connection,message, + RTSP_STATUS_CODE_INTERNAL_SERVER_ERROR, + RTSP_REASON_PHRASE_INTERNAL_SERVER_ERROR); + return FALSE; + } + return TRUE; +} + +/* Process incoming SETUP request */ +static rtsp_server_session_t* rtsp_server_session_setup_process(rtsp_server_t *server, rtsp_server_connection_t *rtsp_connection, rtsp_message_t *message) +{ + rtsp_server_session_t *session = NULL; + if(message->start_line.common.request_line.method_id == RTSP_METHOD_SETUP) { + /* create new session */ + session = rtsp_server_session_create(server); + session->connection = rtsp_connection; + session->url = message->start_line.common.request_line.url; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Add RTSP Session "APT_SID_FMT,session->id.buf); + apr_hash_set(rtsp_connection->session_table,session->id.buf,session->id.length,session); + } + else if(message->start_line.common.request_line.method_id == RTSP_METHOD_DESCRIBE) { + /* create new session as a communication object */ + session = rtsp_server_session_create(server); + session->connection = rtsp_connection; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Add RTSP Session "APT_SID_FMT,session->id.buf); + apr_hash_set(rtsp_connection->session_table,session->id.buf,session->id.length,session); + } + else { + /* error case */ + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Missing RTSP Session-ID"); + rtsp_server_error_respond(server,rtsp_connection,message, + RTSP_STATUS_CODE_BAD_REQUEST, + RTSP_REASON_PHRASE_BAD_REQUEST); + } + return session; +} + +/* Process incoming RTSP request */ +static apt_bool_t rtsp_server_session_request_process(rtsp_server_t *server, rtsp_server_connection_t *rtsp_connection, rtsp_message_t *message) +{ + rtsp_server_session_t *session = NULL; + if(message->start_line.message_type != RTSP_MESSAGE_TYPE_REQUEST) { + /* received response to ANNOUNCE request/event */ + return TRUE; + } + + if(rtsp_header_property_check(&message->header.property_set,RTSP_HEADER_FIELD_SESSION_ID) != TRUE) { + /* no session-id specified */ + session = rtsp_server_session_setup_process(server,rtsp_connection,message); + if(session) { + session->active_request = message; + if(rtsp_server_session_message_handle(server,session,message) != TRUE) { + rtsp_server_session_destroy(session); + } + } + return TRUE; + } + + /* existing session */ + session = apr_hash_get( + rtsp_connection->session_table, + message->header.session_id.buf, + message->header.session_id.length); + if(!session) { + /* error case */ + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No Such RTSP Session "APT_SID_FMT,message->header.session_id.buf); + return rtsp_server_error_respond(server,rtsp_connection,message, + RTSP_STATUS_CODE_NOT_FOUND, + RTSP_REASON_PHRASE_NOT_FOUND); + } + + if(session->active_request) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Push RTSP Request to Queue "APT_SID_FMT,session->id.buf); + apt_list_push_back(session->request_queue,message,message->pool); + return TRUE; + } + + /* handle the request */ + session->active_request = message; + rtsp_server_session_message_handle(server,session,message); + return TRUE; +} + +/* Process outgoing RTSP response */ +static apt_bool_t rtsp_server_session_response_process(rtsp_server_t *server, rtsp_server_session_t *session, rtsp_message_t *message) +{ + if(session->id.buf) { + message->header.session_id = session->id; + rtsp_header_property_add(&message->header.property_set,RTSP_HEADER_FIELD_SESSION_ID); + } + + if(message->start_line.message_type == RTSP_MESSAGE_TYPE_REQUEST) { + /* RTSP ANNOUNCE request (asynch event) */ + message->start_line.common.request_line.url = session->url; + message->header.cseq = session->last_cseq; + rtsp_header_property_add(&message->header.property_set,RTSP_HEADER_FIELD_CSEQ); + + rtsp_server_message_send(server,session->connection->base,message); + return TRUE; + } + + session->last_cseq = message->header.cseq; + rtsp_server_message_send(server,session->connection->base,message); + + if(session->active_request) { + rtsp_message_t *request = session->active_request; + if(request->start_line.common.request_line.method_id == RTSP_METHOD_SETUP) { + if(message->start_line.common.status_line.status_code != RTSP_STATUS_CODE_OK) { + rtsp_server_session_terminate_request(server,session); + } + } + else if(request->start_line.common.request_line.method_id == RTSP_METHOD_DESCRIBE) { + rtsp_server_session_terminate_request(server,session); + } + } + + session->active_request = apt_list_pop_front(session->request_queue); + if(session->active_request) { + rtsp_server_session_message_handle(server,session,session->active_request); + } + return TRUE; +} + +/* Send RTSP message through RTSP connection */ +static apt_bool_t rtsp_server_message_send(rtsp_server_t *server, apt_net_server_connection_t *connection, rtsp_message_t *message) +{ + apt_bool_t status = FALSE; + rtsp_server_connection_t *rtsp_connection; + apt_text_stream_t *stream; + rtsp_stream_result_e result; + + if(!connection || !connection->sock) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No RTSP Connection"); + return FALSE; + } + rtsp_connection = connection->obj; + stream = &rtsp_connection->tx_stream; + + rtsp_generator_message_set(rtsp_connection->generator,message); + do { + stream->text.length = sizeof(rtsp_connection->tx_buffer)-1; + stream->pos = stream->text.buf; + result = rtsp_generator_run(rtsp_connection->generator,stream); + if(result == RTSP_STREAM_MESSAGE_COMPLETE || result == RTSP_STREAM_MESSAGE_TRUNCATED) { + stream->text.length = stream->pos - stream->text.buf; + *stream->pos = '\0'; + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Send RTSP Stream %s [%lu bytes]\n%s", + connection->id, + stream->text.length, + stream->text.buf); + if(apr_socket_send(connection->sock,stream->text.buf,&stream->text.length) == APR_SUCCESS) { + status = TRUE; + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Send RTSP Stream"); + } + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Generate RTSP Stream"); + } + } + while(result == RTSP_STREAM_MESSAGE_TRUNCATED); + + return status; +} + +static apt_bool_t rtsp_server_message_handler(void *obj, rtsp_message_t *message, rtsp_stream_result_e result) +{ + rtsp_server_connection_t *rtsp_connection = obj; + if(result == RTSP_STREAM_MESSAGE_COMPLETE) { + /* message is completely parsed */ + apt_str_t *destination; + rtsp_message_t *message = rtsp_parser_message_get(rtsp_connection->parser); + destination = &message->header.transport.destination; + if(!destination->buf && rtsp_connection->base->client_ip) { + apt_string_assign(destination,rtsp_connection->base->client_ip,rtsp_connection->base->pool); + } + rtsp_server_session_request_process(rtsp_connection->server,rtsp_connection,message); + } + else if(result == RTSP_STREAM_MESSAGE_INVALID) { + /* error case */ + rtsp_message_t *response; + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Parse RTSP Stream"); + if(message) { + response = rtsp_response_create(message,RTSP_STATUS_CODE_BAD_REQUEST, + RTSP_REASON_PHRASE_BAD_REQUEST,message->pool); + if(rtsp_server_message_send(rtsp_connection->server,rtsp_connection->base,response) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Send RTSP Response"); + } + } + } + return TRUE; +} + +/* Receive RTSP message through RTSP connection */ +static apt_bool_t rtsp_server_message_receive(apt_net_server_task_t *task, apt_net_server_connection_t *connection) +{ + rtsp_server_connection_t *rtsp_connection; + apr_status_t status; + apr_size_t offset; + apr_size_t length; + apt_text_stream_t *stream; + + if(!connection || !connection->sock) { + return FALSE; + } + rtsp_connection = connection->obj; + stream = &rtsp_connection->rx_stream; + + /* init length of the stream */ + stream->text.length = sizeof(rtsp_connection->rx_buffer)-1; + /* calculate offset remaining from the previous receive / if any */ + offset = stream->pos - stream->text.buf; + /* calculate available length */ + length = stream->text.length - offset; + status = apr_socket_recv(connection->sock,stream->pos,&length); + if(status == APR_EOF || length == 0) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"TCP Peer Disconnected %s",connection->id); + return apt_net_server_connection_close(task,connection); + } + /* calculate actual length of the stream */ + stream->text.length = offset + length; + stream->pos[length] = '\0'; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Receive RTSP Stream %s [%lu bytes]\n%s", + connection->id, + length, + stream->pos); + + /* reset pos */ + stream->pos = stream->text.buf; + /* walk through the stream parsing RTSP messages */ + return rtsp_stream_walk(rtsp_connection->parser,stream,rtsp_server_message_handler,rtsp_connection); +} + +/* New RTSP connection accepted */ +static apt_bool_t rtsp_server_on_connect(apt_net_server_task_t *task, apt_net_server_connection_t *connection) +{ + rtsp_server_t *server = apt_net_server_task_object_get(task); + rtsp_server_connection_t *rtsp_connection = apr_palloc(connection->pool,sizeof(rtsp_server_connection_t)); + rtsp_connection->session_table = apr_hash_make(connection->pool); + apt_text_stream_init(&rtsp_connection->rx_stream,rtsp_connection->rx_buffer,sizeof(rtsp_connection->rx_buffer)-1); + apt_text_stream_init(&rtsp_connection->tx_stream,rtsp_connection->tx_buffer,sizeof(rtsp_connection->tx_buffer)-1); + rtsp_connection->parser = rtsp_parser_create(connection->pool); + rtsp_connection->generator = rtsp_generator_create(connection->pool); + rtsp_connection->base = connection; + connection->obj = rtsp_connection; + if(!server->connection_list) { + server->connection_list = apt_list_create(server->sub_pool); + } + rtsp_connection->server = server; + rtsp_connection->it = apt_list_push_back(server->connection_list,rtsp_connection,connection->pool); + return TRUE; +} + +/* RTSP connection disconnected */ +static apt_bool_t rtsp_server_on_disconnect(apt_net_server_task_t *task, apt_net_server_connection_t *connection) +{ + apr_size_t remaining_sessions = 0; + rtsp_server_t *server = apt_net_server_task_object_get(task); + rtsp_server_connection_t *rtsp_connection = connection->obj; + apt_list_elem_remove(server->connection_list,rtsp_connection->it); + rtsp_connection->it = NULL; + if(apt_list_is_empty(server->connection_list) == TRUE) { + apr_pool_clear(server->sub_pool); + server->connection_list = NULL; + } + + remaining_sessions = apr_hash_count(rtsp_connection->session_table); + if(remaining_sessions) { + rtsp_server_session_t *session; + void *val; + apr_hash_index_t *it; + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Terminate Remaining RTSP Sessions [%d]",remaining_sessions); + it = apr_hash_first(connection->pool,rtsp_connection->session_table); + for(; it; it = apr_hash_next(it)) { + apr_hash_this(it,NULL,NULL,&val); + session = val; + if(session && session->terminating == FALSE) { + rtsp_server_session_terminate_request(server,session); + } + } + } + else { + apt_net_server_connection_destroy(connection); + } + return TRUE; +} + +/* Process task message */ +static apt_bool_t rtsp_server_task_msg_process(apt_task_t *task, apt_task_msg_t *task_msg) +{ + apt_net_server_task_t *net_task = apt_task_object_get(task); + rtsp_server_t *server = apt_net_server_task_object_get(net_task); + + task_msg_data_t *data = (task_msg_data_t*) task_msg->data; + switch(data->type) { + case TASK_MSG_SEND_MESSAGE: + rtsp_server_session_response_process(server,data->session,data->message); + break; + case TASK_MSG_TERMINATE_SESSION: + rtsp_server_session_do_terminate(server,data->session); + break; + } + + return TRUE; +} diff --git a/libs/unimrcp/libs/uni-rtsp/src/rtsp_start_line.c b/libs/unimrcp/libs/uni-rtsp/src/rtsp_start_line.c new file mode 100644 index 0000000000..b6a3fb3e83 --- /dev/null +++ b/libs/unimrcp/libs/uni-rtsp/src/rtsp_start_line.c @@ -0,0 +1,237 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "rtsp_start_line.h" +#include "apt_string_table.h" +#include "apt_log.h" + +/** Protocol name used in version string */ +#define RTSP_NAME "RTSP" +#define RTSP_NAME_LENGTH (sizeof(RTSP_NAME)-1) + +/** Separators used in RTSP version string parse/generate */ +#define RTSP_NAME_VERSION_SEPARATOR '/' +#define RTSP_VERSION_MAJOR_MINOR_SEPARATOR '.' + +/** String table of RTSP methods (rtsp_method_id) */ +static const apt_str_table_item_t rtsp_method_string_table[] = { + {{"SETUP", 5},0}, + {{"ANNOUNCE", 8},0}, + {{"TEARDOWN", 8},0}, + {{"DESCRIBE", 8},0} +}; + +/** String table of RTSP reason phrases (rtsp_reason_phrase_e) */ +static const apt_str_table_item_t rtsp_reason_string_table[] = { + {{"OK", 2},0}, + {{"Created", 7},0}, + {{"Bad Request", 11},0}, + {{"Unauthorized", 12},0}, + {{"Not Found", 9},4}, + {{"Method Not Allowed", 18},0}, + {{"Not Acceptable", 14},4}, + {{"Session Not Found", 17},0}, + {{"Internal Server Error", 21},0}, + {{"Not Implemented", 15},4} +}; + +/** Parse RTSP URI */ +static apt_bool_t rtsp_resource_uri_parse(const apt_str_t *field, rtsp_request_line_t *request_line, apr_pool_t *pool) +{ + char *str; + apt_str_t *url = &request_line->url; + if(!field->length || !field->buf) { + return FALSE; + } + + apt_string_copy(url,field,pool); + if(url->buf[url->length-1] == '/') { + url->length--; + url->buf[url->length] = '\0'; + } + + str = strrchr(url->buf,'/'); + if(str) { + str++; + } + request_line->resource_name = str; + return TRUE; +} + +/** Parse RTSP version */ +static rtsp_version_e rtsp_version_parse(const apt_str_t *field) +{ + rtsp_version_e version = RTSP_VERSION_UNKNOWN; + const char *pos; + if(field->length <= RTSP_NAME_LENGTH || strncasecmp(field->buf,RTSP_NAME,RTSP_NAME_LENGTH) != 0) { + /* unexpected protocol name */ + return version; + } + + pos = field->buf + RTSP_NAME_LENGTH; + if(*pos == RTSP_NAME_VERSION_SEPARATOR) { + pos++; + switch(*pos) { + case '1': version = RTSP_VERSION_1; break; + default: ; + } + } + return version; +} + +/** Generate RTSP version */ +static apt_bool_t rtsp_version_generate(rtsp_version_e version, apt_text_stream_t *stream) +{ + memcpy(stream->pos,RTSP_NAME,RTSP_NAME_LENGTH); + stream->pos += RTSP_NAME_LENGTH; + *stream->pos++ = RTSP_NAME_VERSION_SEPARATOR; + apt_size_value_generate(version,stream); + *stream->pos++ = RTSP_VERSION_MAJOR_MINOR_SEPARATOR; + *stream->pos++ = '0'; + return TRUE; +} + +/** Parse RTSP status-code */ +static APR_INLINE rtsp_status_code_e rtsp_status_code_parse(const apt_str_t *field) +{ + return apt_size_value_parse(field); +} + +/** Generate RTSP status-code */ +static APR_INLINE apt_bool_t rtsp_status_code_generate(rtsp_status_code_e status_code, apt_text_stream_t *stream) +{ + return apt_size_value_generate(status_code,stream); +} + +/** Generate RTSP request-line */ +static apt_bool_t rtsp_request_line_generate(rtsp_request_line_t *start_line, apt_text_stream_t *stream) +{ + const apt_str_t *method_name = apt_string_table_str_get(rtsp_method_string_table,RTSP_METHOD_COUNT,start_line->method_id); + if(!method_name) { + return FALSE; + } + start_line->method_name = *method_name; + apt_string_value_generate(&start_line->method_name,stream); + apt_text_space_insert(stream); + + apt_string_value_generate(&start_line->url,stream); + apt_text_space_insert(stream); + + rtsp_version_generate(start_line->version,stream); + return TRUE; +} + +/** Generate RTSP status-line */ +static apt_bool_t rtsp_status_line_generate(rtsp_status_line_t *start_line, apt_text_stream_t *stream) +{ + rtsp_version_generate(start_line->version,stream); + apt_text_space_insert(stream); + + rtsp_status_code_generate(start_line->status_code,stream); + apt_text_space_insert(stream); + + apt_string_value_generate(&start_line->reason,stream); + return TRUE; +} + +/** Parse RTSP start-line */ +RTSP_DECLARE(apt_bool_t) rtsp_start_line_parse(rtsp_start_line_t *start_line, apt_text_stream_t *stream, apr_pool_t *pool) +{ + apt_text_stream_t line; + apt_str_t field; + if(apt_text_line_read(stream,&line.text) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot parse RTSP start-line"); + return FALSE; + } + line.pos = line.text.buf; + + if(apt_text_field_read(&line,APT_TOKEN_SP,TRUE,&field) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot read the first field in start-line"); + return FALSE; + } + + if(field.buf == strstr(field.buf,RTSP_NAME)) { + /* parsing RTSP response */ + rtsp_status_line_t *status_line = &start_line->common.status_line; + start_line->message_type = RTSP_MESSAGE_TYPE_RESPONSE; + rtsp_status_line_init(status_line); + + status_line->version = rtsp_version_parse(&field); + + if(apt_text_field_read(&line,APT_TOKEN_SP,TRUE,&field) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot parse status-code in status-line"); + return FALSE; + } + status_line->status_code = rtsp_status_code_parse(&field); + + if(apt_text_field_read(&line,APT_TOKEN_SP,TRUE,&field) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot parse reason phrase in status-line"); + return FALSE; + } + apt_string_copy(&status_line->reason,&field,pool); + } + else { + /* parsing RTSP request */ + rtsp_request_line_t *request_line = &start_line->common.request_line; + start_line->message_type = RTSP_MESSAGE_TYPE_REQUEST; + rtsp_request_line_init(request_line); + + apt_string_copy(&request_line->method_name,&field,pool); + request_line->method_id = apt_string_table_id_find(rtsp_method_string_table,RTSP_METHOD_COUNT,&field); + + if(apt_text_field_read(&line,APT_TOKEN_SP,TRUE,&field) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot parse URL in request-line"); + return FALSE; + } + rtsp_resource_uri_parse(&field,request_line,pool); + + if(apt_text_field_read(&line,APT_TOKEN_SP,TRUE,&field) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot parse version in request-line"); + return FALSE; + } + request_line->version = rtsp_version_parse(&field); + } + + return TRUE; +} + +/** Generate RTSP start-line */ +RTSP_DECLARE(apt_bool_t) rtsp_start_line_generate(rtsp_start_line_t *start_line, apt_text_stream_t *stream) +{ + apt_bool_t status = FALSE; + switch(start_line->message_type) { + case RTSP_MESSAGE_TYPE_REQUEST: + status = rtsp_request_line_generate(&start_line->common.request_line,stream); + break; + case RTSP_MESSAGE_TYPE_RESPONSE: + status = rtsp_status_line_generate(&start_line->common.status_line,stream); + break; + default: + break; + } + + if(status == TRUE) { + apt_text_eol_insert(stream); + } + + return status; +} + +/** Get reason phrase by status code */ +RTSP_DECLARE(const apt_str_t*) rtsp_reason_phrase_get(rtsp_reason_phrase_e reason) +{ + return apt_string_table_str_get(rtsp_reason_string_table,RTSP_REASON_PHRASE_COUNT,reason); +} diff --git a/libs/unimrcp/libs/uni-rtsp/src/rtsp_stream.c b/libs/unimrcp/libs/uni-rtsp/src/rtsp_stream.c new file mode 100644 index 0000000000..51b791009d --- /dev/null +++ b/libs/unimrcp/libs/uni-rtsp/src/rtsp_stream.c @@ -0,0 +1,287 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "rtsp_stream.h" +#include "apt_log.h" + +/** RTSP parser */ +struct rtsp_parser_t { + rtsp_stream_result_e result; + char *pos; + apt_bool_t skip_lf; + rtsp_message_t *message; + apr_pool_t *pool; +}; + +/** RTSP generator */ +struct rtsp_generator_t { + rtsp_stream_result_e result; + char *pos; + rtsp_message_t *message; + apr_pool_t *pool; +}; + +/** Read RTSP message-body */ +static rtsp_stream_result_e rtsp_message_body_read(rtsp_message_t *message, apt_text_stream_t *stream) +{ + rtsp_stream_result_e result = RTSP_STREAM_MESSAGE_COMPLETE; + if(message->body.buf) { + /* stream length available to read */ + apr_size_t stream_length = stream->text.length - (stream->pos - stream->text.buf); + /* required/remaining length to read */ + apr_size_t required_length = message->header.content_length - message->body.length; + if(required_length > stream_length) { + required_length = stream_length; + /* not complete */ + result = RTSP_STREAM_MESSAGE_TRUNCATED; + } + memcpy(message->body.buf+message->body.length,stream->pos,required_length); + message->body.length += required_length; + stream->pos += required_length; + message->body.buf[message->body.length] = '\0'; + } + + return result; +} + +/** Parse RTSP message-body */ +static rtsp_stream_result_e rtsp_message_body_parse(rtsp_message_t *message, apt_text_stream_t *stream, apr_pool_t *pool) +{ + if(rtsp_header_property_check(&message->header.property_set,RTSP_HEADER_FIELD_CONTENT_LENGTH) == TRUE) { + if(message->header.content_length) { + apt_str_t *body = &message->body; + body->buf = apr_palloc(pool,message->header.content_length+1); + body->length = 0; + return rtsp_message_body_read(message,stream); + } + } + return RTSP_STREAM_MESSAGE_COMPLETE; +} + +/** Write RTSP message-body */ +static rtsp_stream_result_e rtsp_message_body_write(rtsp_message_t *message, apt_text_stream_t *stream) +{ + rtsp_stream_result_e result = RTSP_STREAM_MESSAGE_COMPLETE; + if(message->body.length < message->header.content_length) { + /* stream length available to write */ + apr_size_t stream_length = stream->text.length - (stream->pos - stream->text.buf); + /* required/remaining length to write */ + apr_size_t required_length = message->header.content_length - message->body.length; + if(required_length > stream_length) { + required_length = stream_length; + /* not complete */ + result = RTSP_STREAM_MESSAGE_TRUNCATED; + } + + memcpy(stream->pos,message->body.buf+message->body.length,required_length); + message->body.length += required_length; + stream->pos += required_length; + } + + return result; +} + +/** Generate RTSP message-body */ +static rtsp_stream_result_e rtsp_message_body_generate(rtsp_message_t *message, apt_text_stream_t *stream, apr_pool_t *pool) +{ + if(rtsp_header_property_check(&message->header.property_set,RTSP_HEADER_FIELD_CONTENT_LENGTH) == TRUE) { + if(message->header.content_length) { + apt_str_t *body = &message->body; + body->length = 0; + return rtsp_message_body_write(message,stream); + } + } + return RTSP_STREAM_MESSAGE_COMPLETE; +} + +/** Create RTSP parser */ +RTSP_DECLARE(rtsp_parser_t*) rtsp_parser_create(apr_pool_t *pool) +{ + rtsp_parser_t *parser = apr_palloc(pool,sizeof(rtsp_parser_t)); + parser->result = RTSP_STREAM_MESSAGE_INVALID; + parser->pos = NULL; + parser->skip_lf = FALSE; + parser->message = NULL; + parser->pool = pool; + return parser; +} + +static rtsp_stream_result_e rtsp_parser_break(rtsp_parser_t *parser, apt_text_stream_t *stream) +{ + /* failed to parse either start-line or header */ + if(apt_text_is_eos(stream) == TRUE) { + /* end of stream reached, rewind/restore stream */ + stream->pos = parser->pos; + parser->result = RTSP_STREAM_MESSAGE_TRUNCATED; + parser->message = NULL; + } + else { + /* error case */ + parser->result = RTSP_STREAM_MESSAGE_INVALID; + } + return parser->result; +} + +/** Parse RTSP stream */ +RTSP_DECLARE(rtsp_stream_result_e) rtsp_parser_run(rtsp_parser_t *parser, apt_text_stream_t *stream) +{ + rtsp_message_t *message = parser->message; + if(message && parser->result == RTSP_STREAM_MESSAGE_TRUNCATED) { + /* process continuation data */ + parser->result = rtsp_message_body_read(message,stream); + return parser->result; + } + + /* create new RTSP message */ + message = rtsp_message_create(RTSP_MESSAGE_TYPE_UNKNOWN,parser->pool); + parser->message = message; + /* store current position to be able to rewind/restore stream if needed */ + parser->pos = stream->pos; + /* parse start-line */ + if(rtsp_start_line_parse(&message->start_line,stream,message->pool) == FALSE) { + return rtsp_parser_break(parser,stream); + } + /* parse header */ + if(rtsp_header_parse(&message->header,stream,message->pool) == FALSE) { + return rtsp_parser_break(parser,stream); + } + /* parse body */ + parser->result = rtsp_message_body_parse(message,stream,message->pool); + + /* in the worst case message segmentation may occur between and + of the final empty header */ + if(!message->body.length && *(stream->pos-1)== APT_TOKEN_CR) { + /* if this is the case be prepared to skip */ + parser->skip_lf = TRUE; + } + return parser->result; +} + +/** Get parsed RTSP message */ +RTSP_DECLARE(rtsp_message_t*) rtsp_parser_message_get(const rtsp_parser_t *parser) +{ + return parser->message; +} + + +/** Create RTSP stream generator */ +RTSP_DECLARE(rtsp_generator_t*) rtsp_generator_create(apr_pool_t *pool) +{ + rtsp_generator_t *generator = apr_palloc(pool,sizeof(rtsp_generator_t)); + generator->result = RTSP_STREAM_MESSAGE_INVALID; + generator->pos = NULL; + generator->message = NULL; + generator->pool = pool; + return generator; +} + +/** Set RTSP message to generate */ +RTSP_DECLARE(apt_bool_t) rtsp_generator_message_set(rtsp_generator_t *generator, rtsp_message_t *message) +{ + if(!message) { + return FALSE; + } + generator->message = message; + return TRUE; +} + +static rtsp_stream_result_e rtsp_generator_break(rtsp_generator_t *generator, apt_text_stream_t *stream) +{ + /* failed to generate either start-line or header */ + if(apt_text_is_eos(stream) == TRUE) { + /* end of stream reached, rewind/restore stream */ + stream->pos = generator->pos; + generator->result = RTSP_STREAM_MESSAGE_TRUNCATED; + } + else { + /* error case */ + generator->result = RTSP_STREAM_MESSAGE_INVALID; + } + return generator->result; +} + +/** Generate RTSP stream */ +RTSP_DECLARE(rtsp_stream_result_e) rtsp_generator_run(rtsp_generator_t *generator, apt_text_stream_t *stream) +{ + rtsp_message_t *message = generator->message; + if(!message) { + return RTSP_STREAM_MESSAGE_INVALID; + } + + if(message && generator->result == RTSP_STREAM_MESSAGE_TRUNCATED) { + /* process continuation data */ + generator->result = rtsp_message_body_write(message,stream); + return generator->result; + } + + /* generate start-line */ + if(rtsp_start_line_generate(&message->start_line,stream) == FALSE) { + return rtsp_generator_break(generator,stream); + } + + /* generate header */ + if(rtsp_header_generate(&message->header,stream) == FALSE) { + return rtsp_generator_break(generator,stream); + } + + /* generate body */ + generator->result = rtsp_message_body_generate(message,stream,message->pool); + return generator->result; +} + + +/** Walk through RTSP stream and invoke message handler for each parsed message */ +RTSP_DECLARE(apt_bool_t) rtsp_stream_walk(rtsp_parser_t *parser, apt_text_stream_t *stream, rtsp_message_handler_f handler, void *obj) +{ + rtsp_stream_result_e result; + if(parser->skip_lf == TRUE) { + /* skip occurred as a result of message segmentation between and */ + apt_text_char_skip(stream,APT_TOKEN_LF); + parser->skip_lf = FALSE; + } + do { + result = rtsp_parser_run(parser,stream); + if(result == RTSP_STREAM_MESSAGE_COMPLETE) { + /* message is completely parsed */ + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Parsed RTSP Message [%lu]", stream->pos - stream->text.buf); + /* invoke message handler */ + handler(obj,parser->message,result); + } + else if(result == RTSP_STREAM_MESSAGE_TRUNCATED) { + /* message is partially parsed, to be continued */ + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Truncated RTSP Message [%lu]", stream->pos - stream->text.buf); + /* prepare stream for further processing */ + if(apt_text_stream_scroll(stream) == TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Scroll RTSP Stream", stream->text.buf); + } + return TRUE; + } + else if(result == RTSP_STREAM_MESSAGE_INVALID){ + /* error case */ + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Parse RTSP Message"); + /* invoke message handler */ + handler(obj,parser->message,result); + /* reset stream pos */ + stream->pos = stream->text.buf; + return FALSE; + } + } + while(apt_text_is_eos(stream) == FALSE); + + /* reset stream pos */ + stream->pos = stream->text.buf; + return TRUE; +} diff --git a/libs/unimrcp/libs/uni-rtsp/unirtsp.vcproj b/libs/unimrcp/libs/uni-rtsp/unirtsp.vcproj new file mode 100644 index 0000000000..070bd0e2c1 --- /dev/null +++ b/libs/unimrcp/libs/uni-rtsp/unirtsp.vcproj @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/modules/Makefile.am b/libs/unimrcp/modules/Makefile.am new file mode 100644 index 0000000000..7c5ba9768e --- /dev/null +++ b/libs/unimrcp/modules/Makefile.am @@ -0,0 +1,3 @@ +MAINTAINERCLEANFILES = Makefile.in + +SUBDIRS = mrcp-sofiasip mrcp-unirtsp diff --git a/libs/unimrcp/modules/mrcp-sofiasip/Makefile.am b/libs/unimrcp/modules/mrcp-sofiasip/Makefile.am new file mode 100644 index 0000000000..9bae7e3300 --- /dev/null +++ b/libs/unimrcp/modules/mrcp-sofiasip/Makefile.am @@ -0,0 +1,21 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -Iinclude \ + -I$(top_srcdir)/libs/mrcpv2-transport/include \ + -I$(top_srcdir)/libs/mrcp-signaling/include \ + -I$(top_srcdir)/libs/mrcp/include \ + -I$(top_srcdir)/libs/mrcp/message/include \ + -I$(top_srcdir)/libs/mrcp/control/include \ + -I$(top_srcdir)/libs/mrcp/resources/include \ + -I$(top_srcdir)/libs/mpf/include \ + -I$(top_srcdir)/libs/apr-toolkit/include \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) $(UNIMRCP_SOFIA_INCLUDES) + +noinst_LTLIBRARIES = libmrcpsofiasip.la + +include_HEADERS = include/mrcp_sdp.h \ + include/mrcp_sofiasip_server_agent.h \ + include/mrcp_sofiasip_client_agent.h +libmrcpsofiasip_la_SOURCES = src/mrcp_sdp.c \ + src/mrcp_sofiasip_server_agent.c \ + src/mrcp_sofiasip_client_agent.c diff --git a/libs/unimrcp/modules/mrcp-sofiasip/include/mrcp_sdp.h b/libs/unimrcp/modules/mrcp-sofiasip/include/mrcp_sdp.h new file mode 100644 index 0000000000..a7938c841a --- /dev/null +++ b/libs/unimrcp/modules/mrcp-sofiasip/include/mrcp_sdp.h @@ -0,0 +1,40 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_SDP_H__ +#define __MRCP_SDP_H__ + +/** + * @file mrcp_sdp.h + * @brief MRCP SDP Transformations + */ + +#include "mrcp_sig_types.h" + +APT_BEGIN_EXTERN_C + +/** Generate SDP string by MRCP descriptor */ +MRCP_DECLARE(apr_size_t) sdp_string_generate_by_mrcp_descriptor(char *buffer, apr_size_t size, const mrcp_session_descriptor_t *descriptor, apt_bool_t offer); + +/** Generate MRCP descriptor by SDP session */ +MRCP_DECLARE(mrcp_session_descriptor_t*) mrcp_descriptor_generate_by_sdp_session(const sdp_session_t *sdp, const char *force_destination_ip, apr_pool_t *pool); + +/** Generate SDP resource discovery string */ +MRCP_DECLARE(apr_size_t) sdp_resource_discovery_string_generate(const char *ip, const char *origin, char *buffer, apr_size_t size); + +APT_END_EXTERN_C + +#endif /*__MRCP_SDP_H__*/ diff --git a/libs/unimrcp/modules/mrcp-sofiasip/include/mrcp_sofiasip_client_agent.h b/libs/unimrcp/modules/mrcp-sofiasip/include/mrcp_sofiasip_client_agent.h new file mode 100644 index 0000000000..6505c7695e --- /dev/null +++ b/libs/unimrcp/modules/mrcp-sofiasip/include/mrcp_sofiasip_client_agent.h @@ -0,0 +1,75 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_SOFIASIP_CLIENT_AGENT_H__ +#define __MRCP_SOFIASIP_CLIENT_AGENT_H__ + +/** + * @file mrcp_sofiasip_client_agent.h + * @brief Implementation of MRCP Signaling Interface using Sofia-SIP + */ + +#include +#include "mrcp_sig_agent.h" + +APT_BEGIN_EXTERN_C + +/** Sofia-SIP config declaration */ +typedef struct mrcp_sofia_client_config_t mrcp_sofia_client_config_t; + +/** Sofia-SIP config */ +struct mrcp_sofia_client_config_t { + /** Local IP address */ + char *local_ip; + /** External (NAT) IP address */ + char *ext_ip; + /** Local SIP port */ + apr_port_t local_port; + /** Local SIP user name */ + char *local_user_name; + + /** Remote IP address */ + char *remote_ip; + /** Remote SIP port */ + apr_port_t remote_port; + /** Remote SIP user name */ + char *remote_user_name; + + /** Force destination ip address. Should be used only in case + SDP contains incorrect connection address (local IP address behind NAT) */ + apt_bool_t force_destination; + + /** User agent name */ + char *user_agent_name; + /** SDP origin */ + char *origin; + /** SIP transport */ + char *transport; +}; + +/** + * Create Sofia-SIP signaling agent. + */ +MRCP_DECLARE(mrcp_sig_agent_t*) mrcp_sofiasip_client_agent_create(mrcp_sofia_client_config_t *config, apr_pool_t *pool); + +/** + * Allocate Sofia-SIP config. + */ +MRCP_DECLARE(mrcp_sofia_client_config_t*) mrcp_sofiasip_client_config_alloc(apr_pool_t *pool); + +APT_END_EXTERN_C + +#endif /*__MRCP_SOFIASIP_CLIENT_AGENT_H__*/ diff --git a/libs/unimrcp/modules/mrcp-sofiasip/include/mrcp_sofiasip_server_agent.h b/libs/unimrcp/modules/mrcp-sofiasip/include/mrcp_sofiasip_server_agent.h new file mode 100644 index 0000000000..85bdea45f4 --- /dev/null +++ b/libs/unimrcp/modules/mrcp-sofiasip/include/mrcp_sofiasip_server_agent.h @@ -0,0 +1,68 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_SOFIASIP_SERVER_AGENT_H__ +#define __MRCP_SOFIASIP_SERVER_AGENT_H__ + +/** + * @file mrcp_sofiasip_server_agent.h + * @brief Implementation of MRCP Signaling Interface using Sofia-SIP + */ + +#include +#include "mrcp_sig_agent.h" + +APT_BEGIN_EXTERN_C + +/** Sofia-SIP config declaration */ +typedef struct mrcp_sofia_server_config_t mrcp_sofia_server_config_t; + +/** Sofia-SIP config */ +struct mrcp_sofia_server_config_t { + /** Local IP address to bind to */ + char *local_ip; + /** External (NAT) IP address */ + char *ext_ip; + /** Local port to bind to */ + apr_port_t local_port; + + /** SIP user name */ + char *user_name; + /** User agent name */ + char *user_agent_name; + /** SDP origin */ + char *origin; + /** SIP transport */ + char *transport; + + /** Force destination ip address. Should be used only in case + SDP contains incorrect connection address (local IP address behind NAT) */ + apt_bool_t force_destination; +}; + +/** + * Create Sofia-SIP signaling agent. + */ +MRCP_DECLARE(mrcp_sig_agent_t*) mrcp_sofiasip_server_agent_create(mrcp_sofia_server_config_t *config, apr_pool_t *pool); + +/** + * Allocate Sofia-SIP config. + */ +MRCP_DECLARE(mrcp_sofia_server_config_t*) mrcp_sofiasip_server_config_alloc(apr_pool_t *pool); + +APT_END_EXTERN_C + +#endif /*__MRCP_SOFIASIP_SERVER_AGENT_H__*/ diff --git a/libs/unimrcp/modules/mrcp-sofiasip/mrcpsofiasip.vcproj b/libs/unimrcp/modules/mrcp-sofiasip/mrcpsofiasip.vcproj new file mode 100644 index 0000000000..46c247f0cc --- /dev/null +++ b/libs/unimrcp/modules/mrcp-sofiasip/mrcpsofiasip.vcproj @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/modules/mrcp-sofiasip/src/mrcp_sdp.c b/libs/unimrcp/modules/mrcp-sofiasip/src/mrcp_sdp.c new file mode 100644 index 0000000000..1cfed93e01 --- /dev/null +++ b/libs/unimrcp/modules/mrcp-sofiasip/src/mrcp_sdp.c @@ -0,0 +1,395 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include +#include "mrcp_sdp.h" +#include "mrcp_session_descriptor.h" +#include "mrcp_control_descriptor.h" +#include "mpf_rtp_attribs.h" +#include "apt_text_stream.h" +#include "apt_log.h" + +static apr_size_t sdp_rtp_media_generate(char *buffer, apr_size_t size, const mrcp_session_descriptor_t *descriptor, const mpf_rtp_media_descriptor_t *audio_descriptor); +static apr_size_t sdp_control_media_generate(char *buffer, apr_size_t size, const mrcp_session_descriptor_t *descriptor, const mrcp_control_descriptor_t *control_media, apt_bool_t offer); + +static apt_bool_t mpf_rtp_media_generate(mpf_rtp_media_descriptor_t *rtp_media, const sdp_media_t *sdp_media, const apt_str_t *ip, apr_pool_t *pool); +static apt_bool_t mrcp_control_media_generate(mrcp_control_descriptor_t *mrcp_media, const sdp_media_t *sdp_media, const apt_str_t *ip, apr_pool_t *pool); + +/** Generate SDP string by MRCP descriptor */ +MRCP_DECLARE(apr_size_t) sdp_string_generate_by_mrcp_descriptor(char *buffer, apr_size_t size, const mrcp_session_descriptor_t *descriptor, apt_bool_t offer) +{ + apr_size_t i; + apr_size_t count; + apr_size_t audio_index = 0; + mpf_rtp_media_descriptor_t *audio_media; + apr_size_t video_index = 0; + mpf_rtp_media_descriptor_t *video_media; + apr_size_t control_index = 0; + mrcp_control_descriptor_t *control_media; + apr_size_t offset = 0; + const char *ip = descriptor->ext_ip.buf ? descriptor->ext_ip.buf : (descriptor->ip.buf ? descriptor->ip.buf : "0.0.0.0"); + buffer[0] = '\0'; + offset += snprintf(buffer+offset,size-offset, + "v=0\r\n" + "o=%s 0 0 IN IP4 %s\r\n" + "s=-\r\n" + "c=IN IP4 %s\r\n" + "t=0 0\r\n", + descriptor->origin.buf ? descriptor->origin.buf : "-", + ip, + ip); + count = mrcp_session_media_count_get(descriptor); + for(i=0; ibase.id == i) { + /* generate audio media */ + audio_index++; + offset += sdp_rtp_media_generate(buffer+offset,size-offset,descriptor,audio_media); + continue; + } + video_media = mrcp_session_video_media_get(descriptor,video_index); + if(video_media && video_media->base.id == i) { + /* generate video media */ + video_index++; + offset += sdp_rtp_media_generate(buffer+offset,size-offset,descriptor,video_media); + continue; + } + control_media = mrcp_session_control_media_get(descriptor,control_index); + if(control_media && control_media->id == i) { + /** generate mrcp control media */ + control_index++; + offset += sdp_control_media_generate(buffer+offset,size-offset,descriptor,control_media,offer); + continue; + } + } + return offset; +} + +/** Generate MRCP descriptor by SDP session */ +MRCP_DECLARE(mrcp_session_descriptor_t*) mrcp_descriptor_generate_by_sdp_session(const sdp_session_t *sdp, const char *force_destination_ip, apr_pool_t *pool) +{ + sdp_media_t *sdp_media; + mrcp_session_descriptor_t *descriptor; + + if(!sdp) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Invalid SDP Message"); + return NULL; + } + + descriptor = mrcp_session_descriptor_create(pool); + + if(force_destination_ip) { + apt_string_assign(&descriptor->ip,force_destination_ip,pool); + } + else if(sdp->sdp_connection) { + apt_string_assign(&descriptor->ip,sdp->sdp_connection->c_address,pool); + } + + for(sdp_media=sdp->sdp_media; sdp_media; sdp_media=sdp_media->m_next) { + switch(sdp_media->m_type) { + case sdp_media_audio: + { + mpf_rtp_media_descriptor_t *media = apr_palloc(pool,sizeof(mpf_rtp_media_descriptor_t)); + mpf_rtp_media_descriptor_init(media); + media->base.id = mrcp_session_audio_media_add(descriptor,media); + mpf_rtp_media_generate(media,sdp_media,&descriptor->ip,pool); + break; + } + case sdp_media_video: + { + mpf_rtp_media_descriptor_t *media = apr_palloc(pool,sizeof(mpf_rtp_media_descriptor_t)); + mpf_rtp_media_descriptor_init(media); + media->base.id = mrcp_session_video_media_add(descriptor,media); + mpf_rtp_media_generate(media,sdp_media,&descriptor->ip,pool); + break; + } + case sdp_media_application: + { + mrcp_control_descriptor_t *control_media = apr_palloc(pool,sizeof(mrcp_control_descriptor_t)); + mrcp_control_descriptor_init(control_media); + control_media->id = mrcp_session_control_media_add(descriptor,control_media); + mrcp_control_media_generate(control_media,sdp_media,&descriptor->ip,pool); + break; + } + default: + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Not Supported SDP Media [%s]", sdp_media->m_type_name); + break; + } + } + return descriptor; +} + + +/** Generate SDP media by RTP media descriptor */ +static apr_size_t sdp_rtp_media_generate(char *buffer, apr_size_t size, const mrcp_session_descriptor_t *descriptor, const mpf_rtp_media_descriptor_t *audio_media) +{ + apr_size_t offset = 0; + int i; + mpf_codec_descriptor_t *codec_descriptor; + apr_array_header_t *descriptor_arr = audio_media->codec_list.descriptor_arr; + if(!descriptor_arr) { + return 0; + } + offset += snprintf(buffer+offset,size-offset, + "m=audio %d RTP/AVP", + audio_media->base.state == MPF_MEDIA_ENABLED ? audio_media->base.port : 0); + for(i=0; inelts; i++) { + codec_descriptor = (mpf_codec_descriptor_t*)descriptor_arr->elts + i; + if(codec_descriptor->enabled == TRUE) { + offset += snprintf(buffer+offset,size-offset," %d", codec_descriptor->payload_type); + } + } + offset += snprintf(buffer+offset,size-offset,"\r\n"); + if(descriptor->ip.length && audio_media->base.ip.length && + apt_string_compare(&descriptor->ip,&audio_media->base.ip) != TRUE) { + const char *media_ip = audio_media->base.ext_ip.buf ? audio_media->base.ext_ip.buf : audio_media->base.ip.buf; + offset += sprintf(buffer+offset,"c=IN IP4 %s\r\n",media_ip); + } + if(audio_media->base.state == MPF_MEDIA_ENABLED) { + const apt_str_t *mode_str = mpf_stream_mode_str_get(audio_media->mode); + for(i=0; inelts; i++) { + codec_descriptor = (mpf_codec_descriptor_t*)descriptor_arr->elts + i; + if(codec_descriptor->enabled == TRUE && codec_descriptor->name.buf) { + offset += snprintf(buffer+offset,size-offset,"a=rtpmap:%d %s/%d\r\n", + codec_descriptor->payload_type, + codec_descriptor->name.buf, + codec_descriptor->sampling_rate); + } + } + offset += snprintf(buffer+offset,size-offset,"a=%s\r\n",mode_str ? mode_str->buf : ""); + + if(audio_media->ptime) { + offset += snprintf(buffer+offset,size-offset,"a=ptime:%hu\r\n",audio_media->ptime); + } + } + offset += snprintf(buffer+offset,size-offset,"a=mid:%d\r\n",audio_media->mid); + return offset; +} + +/** Generate SDP media by MRCP control media descriptor */ +static apr_size_t sdp_control_media_generate(char *buffer, apr_size_t size, const mrcp_session_descriptor_t *descriptor, const mrcp_control_descriptor_t *control_media, apt_bool_t offer) +{ + apr_size_t offset = 0; + const apt_str_t *proto; + const apt_str_t *setup_type; + const apt_str_t *connection_type; + proto = mrcp_proto_get(control_media->proto); + setup_type = mrcp_setup_type_get(control_media->setup_type); + connection_type = mrcp_connection_type_get(control_media->connection_type); + if(offer == TRUE) { /* offer */ + if(control_media->port) { + offset += snprintf(buffer+offset,size-offset, + "m=application %d %s 1\r\n" + "a=setup:%s\r\n" + "a=connection:%s\r\n" + "a=resource:%s\r\n" + "a=cmid:%d\r\n", + control_media->port, + proto ? proto->buf : "", + setup_type ? setup_type->buf : "", + connection_type ? connection_type->buf : "", + control_media->resource_name.buf, + control_media->cmid); + } + else { + offset += snprintf(buffer+offset,size-offset, + "m=application %d %s 1\r\n" + "a=resource:%s\r\n" + "a=cmid:%d\r\n", + control_media->port, + proto ? proto->buf : "", + control_media->resource_name.buf, + control_media->cmid); + } + } + else { /* answer */ + if(control_media->port) { + offset += sprintf(buffer+offset, + "m=application %d %s 1\r\n" + "a=setup:%s\r\n" + "a=connection:%s\r\n" + "a=channel:%s@%s\r\n" + "a=cmid:%d\r\n", + control_media->port, + proto ? proto->buf : "", + setup_type ? setup_type->buf : "", + connection_type ? connection_type->buf : "", + control_media->session_id.buf, + control_media->resource_name.buf, + control_media->cmid); + } + else { + offset += sprintf(buffer+offset, + "m=application %d %s 1\r\n" + "a=channel:%s@%s\r\n" + "a=cmid:%d\r\n", + control_media->port, + proto ? proto->buf : "", + control_media->session_id.buf, + control_media->resource_name.buf, + control_media->cmid); + } + } + + return offset; +} + +/** Generate RTP media descriptor by SDP media */ +static apt_bool_t mpf_rtp_media_generate(mpf_rtp_media_descriptor_t *rtp_media, const sdp_media_t *sdp_media, const apt_str_t *ip, apr_pool_t *pool) +{ + mpf_rtp_attrib_e id; + apt_str_t name; + sdp_attribute_t *attrib = NULL; + sdp_rtpmap_t *map; + mpf_codec_descriptor_t *codec; + for(attrib = sdp_media->m_attributes; attrib; attrib=attrib->a_next) { + apt_string_set(&name,attrib->a_name); + id = mpf_rtp_attrib_id_find(&name); + switch(id) { + case RTP_ATTRIB_MID: + rtp_media->mid = atoi(attrib->a_value); + break; + case RTP_ATTRIB_PTIME: + rtp_media->ptime = (apr_uint16_t)atoi(attrib->a_value); + break; + default: + break; + } + } + + mpf_codec_list_init(&rtp_media->codec_list,5,pool); + for(map = sdp_media->m_rtpmaps; map; map = map->rm_next) { + codec = mpf_codec_list_add(&rtp_media->codec_list); + if(codec) { + codec->payload_type = (apr_byte_t)map->rm_pt; + apt_string_assign(&codec->name,map->rm_encoding,pool); + codec->sampling_rate = (apr_uint16_t)map->rm_rate; + codec->channel_count = 1; + } + } + + switch(sdp_media->m_mode) { + case sdp_inactive: + rtp_media->mode = STREAM_MODE_NONE; + break; + case sdp_sendonly: + rtp_media->mode = STREAM_MODE_SEND; + break; + case sdp_recvonly: + rtp_media->mode = STREAM_MODE_RECEIVE; + break; + case sdp_sendrecv: + rtp_media->mode = STREAM_MODE_SEND_RECEIVE; + break; + } + + if(sdp_media->m_connections) { + apt_string_assign(&rtp_media->base.ip,sdp_media->m_connections->c_address,pool); + } + else { + rtp_media->base.ip = *ip; + } + if(sdp_media->m_port) { + rtp_media->base.port = (apr_port_t)sdp_media->m_port; + rtp_media->base.state = MPF_MEDIA_ENABLED; + } + else { + rtp_media->base.state = MPF_MEDIA_DISABLED; + } + return TRUE; +} + +/** Generate MRCP control media by SDP media */ +static apt_bool_t mrcp_control_media_generate(mrcp_control_descriptor_t *control_media, const sdp_media_t *sdp_media, const apt_str_t *ip, apr_pool_t *pool) +{ + mrcp_attrib_e id; + apt_str_t name; + apt_str_t value; + sdp_attribute_t *attrib = NULL; + apt_string_set(&name,sdp_media->m_proto_name); + control_media->proto = mrcp_proto_find(&name); + if(control_media->proto != MRCP_PROTO_TCP) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Not supported SDP Proto [%s], expected [%s]",sdp_media->m_proto_name,mrcp_proto_get(MRCP_PROTO_TCP)); + return FALSE; + } + + for(attrib = sdp_media->m_attributes; attrib; attrib=attrib->a_next) { + apt_string_set(&name,attrib->a_name); + id = mrcp_attrib_id_find(&name); + switch(id) { + case MRCP_ATTRIB_SETUP: + apt_string_set(&value,attrib->a_value); + control_media->setup_type = mrcp_setup_type_find(&value); + break; + case MRCP_ATTRIB_CONNECTION: + apt_string_set(&value,attrib->a_value); + control_media->connection_type = mrcp_connection_type_find(&value); + break; + case MRCP_ATTRIB_RESOURCE: + apt_string_assign(&control_media->resource_name,attrib->a_value,pool); + break; + case MRCP_ATTRIB_CHANNEL: + apt_string_set(&value,attrib->a_value); + apt_id_resource_parse(&value,'@',&control_media->session_id,&control_media->resource_name,pool); + break; + case MRCP_ATTRIB_CMID: + control_media->cmid = atoi(attrib->a_value); + break; + default: + break; + } + } + + if(sdp_media->m_connections) { + apt_string_assign(&control_media->ip,sdp_media->m_connections->c_address,pool); + } + else { + control_media->ip = *ip; + } + control_media->port = (apr_port_t)sdp_media->m_port; + return TRUE; +} + +/** Generate SDP resource discovery string */ +MRCP_DECLARE(apr_size_t) sdp_resource_discovery_string_generate(const char *ip, const char *origin, char *buffer, apr_size_t size) +{ + apr_size_t offset = 0; + if(!ip) { + ip = "0.0.0.0"; + } + if(!origin) { + origin = "-"; + } + buffer[0] = '\0'; + offset += snprintf(buffer+offset,size-offset, + "v=0\r\n" + "o=%s 0 0 IN IP4 %s\r\n" + "s=-\r\n" + "c=IN IP4 %s\r\n" + "t=0 0\r\n" + "m=application 0 TCP/MRCPv2 1\r\n" + "a=resource:speechsynth\r\n" + "a=resource:speechrecog\r\n" + "m=audio 0 RTP/AVP 0 8\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n", + origin, + ip, + ip); + return offset; +} diff --git a/libs/unimrcp/modules/mrcp-sofiasip/src/mrcp_sofiasip_client_agent.c b/libs/unimrcp/modules/mrcp-sofiasip/src/mrcp_sofiasip_client_agent.c new file mode 100644 index 0000000000..f193b0dd97 --- /dev/null +++ b/libs/unimrcp/modules/mrcp-sofiasip/src/mrcp_sofiasip_client_agent.c @@ -0,0 +1,528 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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. + */ + +typedef struct mrcp_sofia_agent_t mrcp_sofia_agent_t; +#define NUA_MAGIC_T mrcp_sofia_agent_t + +typedef struct mrcp_sofia_session_t mrcp_sofia_session_t; +#define NUA_HMAGIC_T mrcp_sofia_session_t + +#include +#include +#include +#include +#include +#include + +#include "mrcp_sofiasip_client_agent.h" +#include "mrcp_session.h" +#include "mrcp_session_descriptor.h" +#include "mrcp_sdp.h" +#include "apt_log.h" + +#define SOFIA_TASK_NAME "SofiaSIP Agent" + +struct mrcp_sofia_agent_t { + mrcp_sig_agent_t *sig_agent; + + mrcp_sofia_client_config_t *config; + char *sip_contact_str; + char *sip_to_str; + char *sip_from_str; + char *sip_bind_str; + + su_root_t *root; + nua_t *nua; +}; + +struct mrcp_sofia_session_t { + mrcp_session_t *session; + su_home_t *home; + nua_handle_t *nh; + + apt_bool_t terminate_requested; + apr_thread_mutex_t *mutex; +}; + +/* Task Interface */ +static void mrcp_sofia_task_initialize(apt_task_t *task); +static apt_bool_t mrcp_sofia_task_run(apt_task_t *task); +static apt_bool_t mrcp_sofia_task_terminate(apt_task_t *task); + +/* MRCP Signaling Interface */ +static apt_bool_t mrcp_sofia_session_offer(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor); +static apt_bool_t mrcp_sofia_session_terminate_request(mrcp_session_t *session); +static apt_bool_t mrcp_sofia_session_discover_request(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor); + +static const mrcp_session_request_vtable_t session_request_vtable = { + mrcp_sofia_session_offer, + mrcp_sofia_session_terminate_request, + NULL, + mrcp_sofia_session_discover_request +}; + +static apt_bool_t mrcp_sofia_config_validate(mrcp_sofia_agent_t *sofia_agent, mrcp_sofia_client_config_t *config, apr_pool_t *pool); +static apt_bool_t mrcp_sofia_session_create(mrcp_session_t *session); + +static void mrcp_sofia_event_callback( nua_event_t nua_event, + int status, + char const *phrase, + nua_t *nua, + mrcp_sofia_agent_t *sofia_agent, + nua_handle_t *nh, + mrcp_sofia_session_t *sofia_session, + sip_t const *sip, + tagi_t tags[]); + + +/** Create Sofia-SIP Signaling Agent */ +MRCP_DECLARE(mrcp_sig_agent_t*) mrcp_sofiasip_client_agent_create(mrcp_sofia_client_config_t *config, apr_pool_t *pool) +{ + apt_task_t *task; + apt_task_vtable_t *vtable; + mrcp_sofia_agent_t *sofia_agent; + sofia_agent = apr_palloc(pool,sizeof(mrcp_sofia_agent_t)); + sofia_agent->sig_agent = mrcp_signaling_agent_create(sofia_agent,MRCP_VERSION_2,pool); + sofia_agent->sig_agent->create_client_session = mrcp_sofia_session_create; + sofia_agent->root = NULL; + sofia_agent->nua = NULL; + + if(mrcp_sofia_config_validate(sofia_agent,config,pool) == FALSE) { + return NULL; + } + + task = apt_task_create(sofia_agent,NULL,pool); + if(!task) { + return NULL; + } + apt_task_name_set(task,SOFIA_TASK_NAME); + vtable = apt_task_vtable_get(task); + if(vtable) { + vtable->on_pre_run = mrcp_sofia_task_initialize; + vtable->run = mrcp_sofia_task_run; + vtable->terminate = mrcp_sofia_task_terminate; + } + sofia_agent->sig_agent->task = task; + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create "SOFIA_TASK_NAME" ["SOFIA_SIP_VERSION"] %s:%hu -> %s:%hu %s", + config->local_ip,config->local_port, + config->remote_ip,config->remote_port, + config->transport ? config->transport : ""); + return sofia_agent->sig_agent; +} + +/** Allocate Sofia-SIP config */ +MRCP_DECLARE(mrcp_sofia_client_config_t*) mrcp_sofiasip_client_config_alloc(apr_pool_t *pool) +{ + mrcp_sofia_client_config_t *config = apr_palloc(pool,sizeof(mrcp_sofia_client_config_t)); + config->local_ip = NULL; + config->ext_ip = NULL; + config->local_port = 0; + config->local_user_name = NULL; + config->remote_ip = NULL; + config->remote_port = 0; + config->remote_user_name = NULL; + + config->force_destination = FALSE; + + config->user_agent_name = NULL; + config->origin = NULL; + config->transport = NULL; + return config; +} + +static apt_bool_t mrcp_sofia_config_validate(mrcp_sofia_agent_t *sofia_agent, mrcp_sofia_client_config_t *config, apr_pool_t *pool) +{ + const char *local_ip = config->ext_ip ? config->ext_ip : config->local_ip; + if(!config->local_ip || !config->remote_ip) { + return FALSE; + } + + sofia_agent->config = config; + sofia_agent->sip_contact_str = apr_psprintf(pool,"sip:%s:%d", local_ip, config->local_port); + sofia_agent->sip_from_str = apr_psprintf(pool,"sip:%s:%d", local_ip, config->local_port); + + if(config->remote_user_name && config->remote_user_name != '\0') { + sofia_agent->sip_to_str = apr_psprintf(pool,"sip:%s@%s:%d", + config->remote_user_name, + config->remote_ip, + config->remote_port); + } + else { + sofia_agent->sip_to_str = apr_psprintf(pool,"sip:%s:%d", + config->remote_ip, + config->remote_port); + } + if(config->transport) { + sofia_agent->sip_bind_str = apr_psprintf(pool,"sip:%s:%d;transport=%s", + config->local_ip, + config->local_port, + config->transport); + } + else { + sofia_agent->sip_bind_str = apr_psprintf(pool,"sip:%s:%d", + config->local_ip, + config->local_port); + } + return TRUE; +} + +static void mrcp_sofia_task_initialize(apt_task_t *task) +{ + mrcp_sofia_agent_t *sofia_agent = apt_task_object_get(task); + + /* Initialize Sofia-SIP library and create event loop */ + su_init(); + sofia_agent->root = su_root_create(NULL); + + /* Create a user agent instance. The stack will call the 'event_callback()' + * callback when events such as succesful registration to network, + * an incoming call, etc, occur. + */ + sofia_agent->nua = nua_create( + sofia_agent->root, /* Event loop */ + mrcp_sofia_event_callback, /* Callback for processing events */ + sofia_agent, /* Additional data to pass to callback */ + NUTAG_URL(sofia_agent->sip_bind_str), /* Address to bind to */ + TAG_END()); /* Last tag should always finish the sequence */ + if(sofia_agent->nua) { + nua_set_params( + sofia_agent->nua, + NUTAG_AUTOANSWER(0), + NUTAG_APPL_METHOD("OPTIONS"), + SIPTAG_USER_AGENT_STR(sofia_agent->config->user_agent_name), + TAG_END()); + } +} + +static apt_bool_t mrcp_sofia_task_run(apt_task_t *task) +{ + mrcp_sofia_agent_t *sofia_agent = apt_task_object_get(task); + + if(sofia_agent->nua) { + /* Run event loop */ + su_root_run(sofia_agent->root); + + /* Destroy allocated resources */ + nua_destroy(sofia_agent->nua); + sofia_agent->nua = NULL; + } + su_root_destroy(sofia_agent->root); + sofia_agent->root = NULL; + su_deinit(); + + apt_task_child_terminate(task); + return TRUE; +} + +static apt_bool_t mrcp_sofia_task_terminate(apt_task_t *task) +{ + mrcp_sofia_agent_t *sofia_agent = apt_task_object_get(task); + if(sofia_agent->nua) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Send Shutdown Signal to NUA"); + nua_shutdown(sofia_agent->nua); + } + return TRUE; +} + +static APR_INLINE mrcp_sofia_agent_t* mrcp_sofia_agent_get(mrcp_session_t *session) +{ + return session->signaling_agent->obj; +} + +static apt_bool_t mrcp_sofia_session_create(mrcp_session_t *session) +{ + mrcp_sofia_agent_t *sofia_agent = mrcp_sofia_agent_get(session); + mrcp_sofia_session_t *sofia_session; + session->request_vtable = &session_request_vtable; + + sofia_session = apr_palloc(session->pool,sizeof(mrcp_sofia_session_t)); + sofia_session->mutex = NULL; + sofia_session->home = su_home_new(sizeof(*sofia_session->home)); + sofia_session->session = session; + sofia_session->terminate_requested = FALSE; + session->obj = sofia_session; + + sofia_session->nh = nua_handle( + sofia_agent->nua, + sofia_session, + SIPTAG_TO_STR(sofia_agent->sip_to_str), + SIPTAG_FROM_STR(sofia_agent->sip_from_str), + SIPTAG_CONTACT_STR(sofia_agent->sip_contact_str), + TAG_END()); + + apr_thread_mutex_create(&sofia_session->mutex,APR_THREAD_MUTEX_DEFAULT,session->pool); + return TRUE; +} + +static apt_bool_t mrcp_sofia_session_destroy(mrcp_sofia_session_t *sofia_session) +{ + if(sofia_session->mutex) { + apr_thread_mutex_destroy(sofia_session->mutex); + sofia_session->mutex = NULL; + } + if(sofia_session->home) { + su_home_unref(sofia_session->home); + sofia_session->home = NULL; + } + return TRUE; +} + +static apt_bool_t mrcp_sofia_session_unref(mrcp_sofia_session_t *sofia_session) +{ + if(sofia_session->nh) { + nua_handle_bind(sofia_session->nh, NULL); + nua_handle_destroy(sofia_session->nh); + sofia_session->nh = NULL; + } + sofia_session->session = NULL; + return TRUE; +} + +static apt_bool_t mrcp_sofia_session_offer(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor) +{ + char sdp_str[2048]; + char *local_sdp_str = NULL; + apt_bool_t res = FALSE; + mrcp_sofia_session_t *sofia_session = session->obj; + if(!sofia_session) { + return FALSE; + } + + if(session->signaling_agent) { + mrcp_sofia_agent_t *sofia_agent = mrcp_sofia_agent_get(session); + if(sofia_agent) { + if(sofia_agent->config->origin) { + apt_string_set(&descriptor->origin,sofia_agent->config->origin); + } + } + } + if(sdp_string_generate_by_mrcp_descriptor(sdp_str,sizeof(sdp_str),descriptor,TRUE) > 0) { + local_sdp_str = sdp_str; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Local SDP "APT_PTRSID_FMT"\n%s", + MRCP_SESSION_PTRSID(session), + local_sdp_str); + } + + apr_thread_mutex_lock(sofia_session->mutex); + + if(sofia_session->nh) { + res = TRUE; + nua_invite(sofia_session->nh, + TAG_IF(local_sdp_str,SOATAG_USER_SDP_STR(local_sdp_str)), + TAG_END()); + } + + apr_thread_mutex_unlock(sofia_session->mutex); + return res; +} + +static apt_bool_t mrcp_sofia_session_terminate_request(mrcp_session_t *session) +{ + mrcp_sofia_session_t *sofia_session = session->obj; + if(!sofia_session) { + return FALSE; + } + + sofia_session->terminate_requested = FALSE; + apr_thread_mutex_lock(sofia_session->mutex); + if(sofia_session->nh) { + sofia_session->terminate_requested = TRUE; + nua_bye(sofia_session->nh,TAG_END()); + } + apr_thread_mutex_unlock(sofia_session->mutex); + + if(sofia_session->terminate_requested == FALSE) { + mrcp_sofia_session_destroy(sofia_session); + mrcp_session_terminate_response(session); + } + return TRUE; +} + +static apt_bool_t mrcp_sofia_session_discover_request(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor) +{ + apt_bool_t res = FALSE; + mrcp_sofia_session_t *sofia_session = session->obj; + if(!sofia_session) { + return FALSE; + } + + apr_thread_mutex_lock(sofia_session->mutex); + if(sofia_session->nh) { + res = TRUE; + nua_options(sofia_session->nh,TAG_END()); + } + apr_thread_mutex_unlock(sofia_session->mutex); + return res; +} + +static void mrcp_sofia_on_session_ready( + int status, + mrcp_sofia_agent_t *sofia_agent, + nua_handle_t *nh, + mrcp_sofia_session_t *sofia_session, + sip_t const *sip, + tagi_t tags[]) +{ + mrcp_session_t *session = sofia_session->session; + if(session) { + const char *local_sdp_str = NULL, *remote_sdp_str = NULL; + mrcp_session_descriptor_t *descriptor = NULL; + + tl_gets(tags, + SOATAG_LOCAL_SDP_STR_REF(local_sdp_str), + SOATAG_REMOTE_SDP_STR_REF(remote_sdp_str), + TAG_END()); + + if(remote_sdp_str) { + sdp_parser_t *parser = NULL; + sdp_session_t *sdp = NULL; + const char *force_destination_ip = NULL; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Remote SDP "APT_PTRSID_FMT"\n%s", + MRCP_SESSION_PTRSID(session), + remote_sdp_str); + + parser = sdp_parse(sofia_session->home,remote_sdp_str,(int)strlen(remote_sdp_str),0); + sdp = sdp_session(parser); + if(sofia_agent && sofia_agent->config->force_destination == TRUE) { + force_destination_ip = sofia_agent->config->remote_ip; + } + descriptor = mrcp_descriptor_generate_by_sdp_session(sdp,force_destination_ip,session->pool); + sdp_parser_free(parser); + } + + mrcp_session_answer(session,descriptor); + } +} + +static void mrcp_sofia_on_session_terminate( + int status, + mrcp_sofia_agent_t *sofia_agent, + nua_handle_t *nh, + mrcp_sofia_session_t *sofia_session, + sip_t const *sip, + tagi_t tags[]) +{ + mrcp_session_t *session = sofia_session->session; + if(session) { + apt_bool_t terminate_requested; + + apr_thread_mutex_lock(sofia_session->mutex); + terminate_requested = sofia_session->terminate_requested; + session = sofia_session->session; + mrcp_sofia_session_unref(sofia_session); + apr_thread_mutex_unlock(sofia_session->mutex); + + if(terminate_requested == TRUE) { + mrcp_sofia_session_destroy(sofia_session); + mrcp_session_terminate_response(session); + } + else { + mrcp_session_terminate_event(session); + } + } +} + +static void mrcp_sofia_on_state_change( + int status, + mrcp_sofia_agent_t *sofia_agent, + nua_handle_t *nh, + mrcp_sofia_session_t *sofia_session, + sip_t const *sip, + tagi_t tags[]) +{ + int ss_state = nua_callstate_init; + tl_gets(tags, + NUTAG_CALLSTATE_REF(ss_state), + TAG_END()); + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"SIP Call State "APT_PTR_FMT" [%s]", + sofia_session ? MRCP_SESSION_PTR(sofia_session->session) : NULL, + nua_callstate_name(ss_state)); + + switch(ss_state) { + case nua_callstate_ready: + mrcp_sofia_on_session_ready(status,sofia_agent,nh,sofia_session,sip,tags); + break; + case nua_callstate_terminated: + mrcp_sofia_on_session_terminate(status,sofia_agent,nh,sofia_session,sip,tags); + break; + } +} + +static void mrcp_sofia_on_resource_discover( + int status, + mrcp_sofia_agent_t *sofia_agent, + nua_handle_t *nh, + mrcp_sofia_session_t *sofia_session, + sip_t const *sip, + tagi_t tags[]) +{ + mrcp_session_t *session = sofia_session->session; + if(session) { + const char *remote_sdp_str = NULL; + mrcp_session_descriptor_t *descriptor = NULL; + + tl_gets(tags, + SOATAG_REMOTE_SDP_STR_REF(remote_sdp_str), + TAG_END()); + + if(remote_sdp_str) { + sdp_parser_t *parser = NULL; + sdp_session_t *sdp = NULL; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Resource Discovery SDP "APT_PTR_FMT"\n%s", + MRCP_SESSION_PTR(session), + remote_sdp_str); + + parser = sdp_parse(sofia_session->home,remote_sdp_str,(int)strlen(remote_sdp_str),0); + sdp = sdp_session(parser); + descriptor = mrcp_descriptor_generate_by_sdp_session(sdp,NULL,session->pool); + sdp_parser_free(parser); + } + + mrcp_session_discover_response(session,descriptor); + } +} + +/** This callback will be called by SIP stack to process incoming events */ +static void mrcp_sofia_event_callback( + nua_event_t nua_event, + int status, + char const *phrase, + nua_t *nua, + mrcp_sofia_agent_t *sofia_agent, + nua_handle_t *nh, + mrcp_sofia_session_t *sofia_session, + sip_t const *sip, + tagi_t tags[]) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Receive SIP Event [%s] Status %d %s", + nua_event_name(nua_event),status,phrase); + + switch(nua_event) { + case nua_i_state: + mrcp_sofia_on_state_change(status,sofia_agent,nh,sofia_session,sip,tags); + break; + case nua_r_options: + mrcp_sofia_on_resource_discover(status,sofia_agent,nh,sofia_session,sip,tags); + break; + case nua_r_shutdown: + /* break main loop of sofia thread */ + su_root_break(sofia_agent->root); + break; + default: + break; + } +} diff --git a/libs/unimrcp/modules/mrcp-sofiasip/src/mrcp_sofiasip_server_agent.c b/libs/unimrcp/modules/mrcp-sofiasip/src/mrcp_sofiasip_server_agent.c new file mode 100644 index 0000000000..0d0c16a102 --- /dev/null +++ b/libs/unimrcp/modules/mrcp-sofiasip/src/mrcp_sofiasip_server_agent.c @@ -0,0 +1,434 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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. + */ + +typedef struct mrcp_sofia_agent_t mrcp_sofia_agent_t; +#define NUA_MAGIC_T mrcp_sofia_agent_t + +typedef struct mrcp_sofia_session_t mrcp_sofia_session_t; +#define NUA_HMAGIC_T mrcp_sofia_session_t + +#include +#include +#include +#include +#include +#include + +#include "mrcp_sofiasip_server_agent.h" +#include "mrcp_session.h" +#include "mrcp_session_descriptor.h" +#include "mrcp_sdp.h" +#include "apt_log.h" + +#define SOFIA_TASK_NAME "SofiaSIP Agent" + +struct mrcp_sofia_agent_t { + mrcp_sig_agent_t *sig_agent; + + mrcp_sofia_server_config_t *config; + char *sip_contact_str; + char *sip_bind_str; + + su_root_t *root; + nua_t *nua; +}; + +struct mrcp_sofia_session_t { + mrcp_session_t *session; + su_home_t *home; + nua_handle_t *nh; +}; + +/* Task Interface */ +static void mrcp_sofia_task_initialize(apt_task_t *task); +static apt_bool_t mrcp_sofia_task_run(apt_task_t *task); +static apt_bool_t mrcp_sofia_task_terminate(apt_task_t *task); + +/* MRCP Signaling Interface */ +static apt_bool_t mrcp_sofia_on_session_answer(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor); +static apt_bool_t mrcp_sofia_on_session_terminate(mrcp_session_t *session); + +static const mrcp_session_response_vtable_t session_response_vtable = { + mrcp_sofia_on_session_answer, + mrcp_sofia_on_session_terminate +}; + +static apt_bool_t mrcp_sofia_config_validate(mrcp_sofia_agent_t *sofia_agent, mrcp_sofia_server_config_t *config, apr_pool_t *pool); + +static void mrcp_sofia_event_callback( nua_event_t nua_event, + int status, + char const *phrase, + nua_t *nua, + mrcp_sofia_agent_t *sofia_agent, + nua_handle_t *nh, + mrcp_sofia_session_t *sofia_session, + sip_t const *sip, + tagi_t tags[]); + + +/** Create Sofia-SIP Signaling Agent */ +MRCP_DECLARE(mrcp_sig_agent_t*) mrcp_sofiasip_server_agent_create(mrcp_sofia_server_config_t *config, apr_pool_t *pool) +{ + apt_task_t *task; + apt_task_vtable_t *vtable; + mrcp_sofia_agent_t *sofia_agent; + sofia_agent = apr_palloc(pool,sizeof(mrcp_sofia_agent_t)); + sofia_agent->sig_agent = mrcp_signaling_agent_create(sofia_agent,MRCP_VERSION_2,pool); + sofia_agent->config = config; + sofia_agent->root = NULL; + sofia_agent->nua = NULL; + + if(mrcp_sofia_config_validate(sofia_agent,config,pool) == FALSE) { + return NULL; + } + + task = apt_task_create(sofia_agent,NULL,pool); + if(!task) { + return NULL; + } + apt_task_name_set(task,SOFIA_TASK_NAME); + vtable = apt_task_vtable_get(task); + if(vtable) { + vtable->on_pre_run = mrcp_sofia_task_initialize; + vtable->run = mrcp_sofia_task_run; + vtable->terminate = mrcp_sofia_task_terminate; + } + sofia_agent->sig_agent->task = task; + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create "SOFIA_TASK_NAME" ["SOFIA_SIP_VERSION"] %s:%hu %s", + config->local_ip, + config->local_port, + config->transport ? config->transport : ""); + return sofia_agent->sig_agent; +} + +/** Allocate Sofia-SIP config */ +MRCP_DECLARE(mrcp_sofia_server_config_t*) mrcp_sofiasip_server_config_alloc(apr_pool_t *pool) +{ + mrcp_sofia_server_config_t *config = apr_palloc(pool,sizeof(mrcp_sofia_server_config_t)); + config->local_ip = NULL; + config->ext_ip = NULL; + config->local_port = 0; + config->user_name = NULL; + config->user_agent_name = NULL; + config->origin = NULL; + config->transport = NULL; + config->force_destination = FALSE; + return config; +} + +static apt_bool_t mrcp_sofia_config_validate(mrcp_sofia_agent_t *sofia_agent, mrcp_sofia_server_config_t *config, apr_pool_t *pool) +{ + const char *local_ip = config->ext_ip ? config->ext_ip : config->local_ip; + sofia_agent->config = config; + sofia_agent->sip_contact_str = apr_psprintf(pool,"sip:%s:%d",local_ip,config->local_port); + if(config->transport) { + sofia_agent->sip_bind_str = apr_psprintf(pool,"sip:%s:%d;transport=%s", + config->local_ip, + config->local_port, + config->transport); + } + else { + sofia_agent->sip_bind_str = apr_psprintf(pool,"sip:%s:%d", + config->local_ip, + config->local_port); + } + return TRUE; +} + +static void mrcp_sofia_task_initialize(apt_task_t *task) +{ + mrcp_sofia_agent_t *sofia_agent = apt_task_object_get(task); + + /* Initialize Sofia-SIP library and create event loop */ + su_init(); + sofia_agent->root = su_root_create(NULL); + + /* Create a user agent instance. The stack will call the 'event_callback()' + * callback when events such as succesful registration to network, + * an incoming call, etc, occur. + */ + sofia_agent->nua = nua_create( + sofia_agent->root, /* Event loop */ + mrcp_sofia_event_callback, /* Callback for processing events */ + sofia_agent, /* Additional data to pass to callback */ + NUTAG_URL(sofia_agent->sip_bind_str), /* Address to bind to */ + TAG_END()); /* Last tag should always finish the sequence */ + if(sofia_agent->nua) { + nua_set_params( + sofia_agent->nua, + NUTAG_AUTOANSWER(0), + NUTAG_APPL_METHOD("OPTIONS"), + SIPTAG_USER_AGENT_STR(sofia_agent->config->user_agent_name), + TAG_END()); + } +} + +static apt_bool_t mrcp_sofia_task_run(apt_task_t *task) +{ + mrcp_sofia_agent_t *sofia_agent = apt_task_object_get(task); + + if(sofia_agent->nua) { + /* Run event loop */ + su_root_run(sofia_agent->root); + + /* Destroy allocated resources */ + nua_destroy(sofia_agent->nua); + sofia_agent->nua = NULL; + } + su_root_destroy(sofia_agent->root); + sofia_agent->root = NULL; + su_deinit(); + + apt_task_child_terminate(task); + return TRUE; +} + +static apt_bool_t mrcp_sofia_task_terminate(apt_task_t *task) +{ + mrcp_sofia_agent_t *sofia_agent = apt_task_object_get(task); + if(sofia_agent->nua) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Send Shutdown Signal to NUA"); + nua_shutdown(sofia_agent->nua); + } + return TRUE; +} + +static mrcp_sofia_session_t* mrcp_sofia_session_create(mrcp_sofia_agent_t *sofia_agent, nua_handle_t *nh) +{ + mrcp_sofia_session_t *sofia_session; + mrcp_session_t* session = sofia_agent->sig_agent->create_server_session(sofia_agent->sig_agent); + if(!session) { + return NULL; + } + session->response_vtable = &session_response_vtable; + session->event_vtable = NULL; + + sofia_session = apr_palloc(session->pool,sizeof(mrcp_sofia_session_t)); + sofia_session->home = su_home_new(sizeof(*sofia_session->home)); + sofia_session->session = session; + session->obj = sofia_session; + + nua_handle_bind(nh, sofia_session); + sofia_session->nh = nh; + return sofia_session; +} + +static int sip_status_get(mrcp_session_status_e status) +{ + switch (status) { + case MRCP_SESSION_STATUS_OK: + return 200; + case MRCP_SESSION_STATUS_NO_SUCH_RESOURCE: + return 404; + case MRCP_SESSION_STATUS_UNACCEPTABLE_RESOURCE: + return 406; + case MRCP_SESSION_STATUS_UNAVAILABLE_RESOURCE: + return 480; + case MRCP_SESSION_STATUS_ERROR: + return 500; + } + return 200; +} + +static apt_bool_t mrcp_sofia_on_session_answer(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor) +{ + mrcp_sofia_session_t *sofia_session = session->obj; + mrcp_sofia_agent_t *sofia_agent = session->signaling_agent->obj; + const char *local_sdp_str = NULL; + char sdp_str[2048]; + + if(!sofia_agent || !sofia_session || !sofia_session->nh) { + return FALSE; + } + + if(descriptor->status != MRCP_SESSION_STATUS_OK) { + int status = sip_status_get(descriptor->status); + nua_respond(sofia_session->nh, status, sip_status_phrase(status), + SIPTAG_CONTACT_STR(sofia_agent->sip_contact_str), + TAG_END()); + return TRUE; + } + + if(sofia_agent->config->origin) { + apt_string_set(&descriptor->origin,sofia_agent->config->origin); + } + + if(sdp_string_generate_by_mrcp_descriptor(sdp_str,sizeof(sdp_str),descriptor,FALSE) > 0) { + local_sdp_str = sdp_str; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Local SDP\n%s", local_sdp_str); + } + + nua_respond(sofia_session->nh, SIP_200_OK, + SIPTAG_CONTACT_STR(sofia_agent->sip_contact_str), + TAG_IF(local_sdp_str,SOATAG_USER_SDP_STR(local_sdp_str)), + NUTAG_AUTOANSWER(0), + TAG_END()); + + return TRUE; +} + +static apt_bool_t mrcp_sofia_on_session_terminate(mrcp_session_t *session) +{ + mrcp_sofia_session_t *sofia_session = session->obj; + if(sofia_session) { + if(sofia_session->nh) { + nua_handle_bind(sofia_session->nh, NULL); + nua_handle_destroy(sofia_session->nh); + } + if(sofia_session->home) { + su_home_unref(sofia_session->home); + sofia_session->home = NULL; + } + sofia_session->session = NULL; + } + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Destroy Session "APT_SID_FMT, MRCP_SESSION_SID(session)); + mrcp_session_destroy(session); + return TRUE; +} + +static void mrcp_sofia_on_call_receive(mrcp_sofia_agent_t *sofia_agent, + nua_handle_t *nh, + mrcp_sofia_session_t *sofia_session, + sip_t const *sip, + tagi_t tags[]) +{ + int offer_recv = 0, answer_recv = 0, offer_sent = 0, answer_sent = 0; + const char *local_sdp_str = NULL, *remote_sdp_str = NULL; + mrcp_session_descriptor_t *descriptor = NULL; + + tl_gets(tags, + NUTAG_OFFER_RECV_REF(offer_recv), + NUTAG_ANSWER_RECV_REF(answer_recv), + NUTAG_OFFER_SENT_REF(offer_sent), + NUTAG_ANSWER_SENT_REF(answer_sent), + SOATAG_LOCAL_SDP_STR_REF(local_sdp_str), + SOATAG_REMOTE_SDP_STR_REF(remote_sdp_str), + TAG_END()); + + if(!sofia_session) { + sofia_session = mrcp_sofia_session_create(sofia_agent,nh); + if(!sofia_session) { + nua_respond(nh, SIP_488_NOT_ACCEPTABLE, TAG_END()); + return; + } + } + + if(remote_sdp_str) { + sdp_parser_t *parser = NULL; + sdp_session_t *sdp = NULL; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Remote SDP\n%s", remote_sdp_str); + + parser = sdp_parse(sofia_session->home,remote_sdp_str,(int)strlen(remote_sdp_str),0); + sdp = sdp_session(parser); + descriptor = mrcp_descriptor_generate_by_sdp_session(sdp,NULL,sofia_session->session->pool); + sdp_parser_free(parser); + } + + if(!descriptor) { + nua_respond(nh, SIP_400_BAD_REQUEST, TAG_END()); + return; + } + + mrcp_session_offer(sofia_session->session,descriptor); +} + +static void mrcp_sofia_on_call_terminate(mrcp_sofia_agent_t *sofia_agent, + nua_handle_t *nh, + mrcp_sofia_session_t *sofia_session, + sip_t const *sip, + tagi_t tags[]) +{ + if(sofia_session) { + mrcp_session_terminate_request(sofia_session->session); + } +} + +static void mrcp_sofia_on_state_change(mrcp_sofia_agent_t *sofia_agent, + nua_handle_t *nh, + mrcp_sofia_session_t *sofia_session, + sip_t const *sip, + tagi_t tags[]) +{ + int ss_state = nua_callstate_init; + tl_gets(tags, + NUTAG_CALLSTATE_REF(ss_state), + TAG_END()); + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"SIP Call State [%s]", nua_callstate_name(ss_state)); + + switch(ss_state) { + case nua_callstate_received: + mrcp_sofia_on_call_receive(sofia_agent,nh,sofia_session,sip,tags); + break; + case nua_callstate_terminated: + mrcp_sofia_on_call_terminate(sofia_agent,nh,sofia_session,sip,tags); + break; + } +} + +static void mrcp_sofia_on_resource_discover(mrcp_sofia_agent_t *sofia_agent, + nua_handle_t *nh, + mrcp_sofia_session_t *sofia_session, + sip_t const *sip, + tagi_t tags[]) +{ + char sdp_str[2048]; + char *local_sdp_str = NULL; + + const char *ip = sofia_agent->config->ext_ip ? + sofia_agent->config->ext_ip : sofia_agent->config->local_ip; + + if(sdp_resource_discovery_string_generate(ip,sofia_agent->config->origin,sdp_str,sizeof(sdp_str)) > 0) { + local_sdp_str = sdp_str; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Resource Discovery SDP\n[%s]\n", local_sdp_str); + } + + nua_respond(nh, SIP_200_OK, + NUTAG_WITH_CURRENT(sofia_agent->nua), + SIPTAG_CONTACT_STR(sofia_agent->sip_contact_str), + TAG_IF(local_sdp_str,SOATAG_USER_SDP_STR(local_sdp_str)), + TAG_END()); +} + +/** This callback will be called by SIP stack to process incoming events */ +static void mrcp_sofia_event_callback( nua_event_t nua_event, + int status, + char const *phrase, + nua_t *nua, + mrcp_sofia_agent_t *sofia_agent, + nua_handle_t *nh, + mrcp_sofia_session_t *sofia_session, + sip_t const *sip, + tagi_t tags[]) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Receive SIP Event [%s] Status %d %s",nua_event_name(nua_event),status,phrase); + + switch(nua_event) { + case nua_i_state: + mrcp_sofia_on_state_change(sofia_agent,nh,sofia_session,sip,tags); + break; + case nua_i_options: + mrcp_sofia_on_resource_discover(sofia_agent,nh,sofia_session,sip,tags); + break; + case nua_r_shutdown: + /* break main loop of sofia thread */ + su_root_break(sofia_agent->root); + break; + default: + break; + } +} diff --git a/libs/unimrcp/modules/mrcp-unirtsp/Makefile.am b/libs/unimrcp/modules/mrcp-unirtsp/Makefile.am new file mode 100644 index 0000000000..ad7d54afe1 --- /dev/null +++ b/libs/unimrcp/modules/mrcp-unirtsp/Makefile.am @@ -0,0 +1,22 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -Iinclude \ + -I$(top_srcdir)/libs/mrcp-signaling/include \ + -I$(top_srcdir)/libs/mrcp/include \ + -I$(top_srcdir)/libs/mrcp/message/include \ + -I$(top_srcdir)/libs/mrcp/control/include \ + -I$(top_srcdir)/libs/mrcp/resources/include \ + -I$(top_srcdir)/libs/mpf/include \ + -I$(top_srcdir)/libs/uni-rtsp/include \ + -I$(top_srcdir)/libs/apr-toolkit/include \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) $(UNIMRCP_SOFIA_INCLUDES) + +noinst_LTLIBRARIES = libmrcpunirtsp.la + +include_HEADERS = include/mrcp_unirtsp_sdp.h \ + include/mrcp_unirtsp_server_agent.h \ + include/mrcp_unirtsp_client_agent.h +libmrcpunirtsp_la_SOURCES = src/mrcp_unirtsp_sdp.c \ + src/mrcp_unirtsp_server_agent.c \ + src/mrcp_unirtsp_client_agent.c +libmrcpunirtsp_la_LIBADD = $(top_builddir)/libs/uni-rtsp/libunirtsp.la diff --git a/libs/unimrcp/modules/mrcp-unirtsp/include/mrcp_unirtsp_client_agent.h b/libs/unimrcp/modules/mrcp-unirtsp/include/mrcp_unirtsp_client_agent.h new file mode 100644 index 0000000000..fc26929cbe --- /dev/null +++ b/libs/unimrcp/modules/mrcp-unirtsp/include/mrcp_unirtsp_client_agent.h @@ -0,0 +1,68 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_UNIRTSP_CLIENT_AGENT_H__ +#define __MRCP_UNIRTSP_CLIENT_AGENT_H__ + +/** + * @file mrcp_unirtsp_client_agent.h + * @brief Implementation of MRCP Signaling Interface using UniRTSP + */ + +#include +#include "apt_string.h" +#include "mrcp_sig_agent.h" + +APT_BEGIN_EXTERN_C + +/** UniRTSP config declaration */ +typedef struct rtsp_client_config_t rtsp_client_config_t; + +/** UniRTSP config */ +struct rtsp_client_config_t { + /** Server IP address */ + char *server_ip; + /** Server port */ + apr_port_t server_port; + /** Resource location */ + char *resource_location; + /** SDP origin */ + char *origin; + + /** Map of the MRCP resource names */ + apr_table_t *resource_map; + + /** Number of max RTSP connections */ + apr_size_t max_connection_count; + + /** Force destination ip address. Should be used only in case + SDP contains incorrect connection address (local IP address behind NAT) */ + apt_bool_t force_destination; +}; + +/** + * Create UniRTSP signaling agent. + */ +MRCP_DECLARE(mrcp_sig_agent_t*) mrcp_unirtsp_client_agent_create(rtsp_client_config_t *config, apr_pool_t *pool); + +/** + * Allocate UniRTSP config. + */ +MRCP_DECLARE(rtsp_client_config_t*) mrcp_unirtsp_client_config_alloc(apr_pool_t *pool); + +APT_END_EXTERN_C + +#endif /*__MRCP_UNIRTSP_CLIENT_AGENT_H__*/ diff --git a/libs/unimrcp/modules/mrcp-unirtsp/include/mrcp_unirtsp_sdp.h b/libs/unimrcp/modules/mrcp-unirtsp/include/mrcp_unirtsp_sdp.h new file mode 100644 index 0000000000..59fbf64e86 --- /dev/null +++ b/libs/unimrcp/modules/mrcp-unirtsp/include/mrcp_unirtsp_sdp.h @@ -0,0 +1,87 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_UNIRTSP_SDP_H__ +#define __MRCP_UNIRTSP_SDP_H__ + +/** + * @file mrcp_unirtsp_sdp.h + * @brief MRCP RTSP SDP Transformations + */ + +#include "mrcp_session_descriptor.h" + +APT_BEGIN_EXTERN_C + +/** Generate MRCP descriptor by RTSP request */ +MRCP_DECLARE(mrcp_session_descriptor_t*) mrcp_descriptor_generate_by_rtsp_request( + const rtsp_message_t *request, + const char *force_destination_ip, + const apr_table_t *resource_map, + apr_pool_t *pool, + su_home_t *home); + +/** Generate MRCP descriptor by RTSP response */ +MRCP_DECLARE(mrcp_session_descriptor_t*) mrcp_descriptor_generate_by_rtsp_response( + const rtsp_message_t *request, + const rtsp_message_t *response, + const char *force_destination_ip, + const apr_table_t *resource_map, + apr_pool_t *pool, + su_home_t *home); + +/** Generate RTSP request by MRCP descriptor */ +MRCP_DECLARE(rtsp_message_t*) rtsp_request_generate_by_mrcp_descriptor( + const mrcp_session_descriptor_t *descriptor, + const apr_table_t *resource_map, + apr_pool_t *pool); +/** Generate RTSP response by MRCP descriptor */ +MRCP_DECLARE(rtsp_message_t*) rtsp_response_generate_by_mrcp_descriptor( + const rtsp_message_t *request, + const mrcp_session_descriptor_t *descriptor, + const apr_table_t *resource_map, + apr_pool_t *pool); + +/** Generate RTSP resource discovery request */ +MRCP_DECLARE(rtsp_message_t*) rtsp_resource_discovery_request_generate( + const char *resource_name, + const apr_table_t *resource_map, + apr_pool_t *pool); + +/** Generate resource descovery descriptor by RTSP response */ +MRCP_DECLARE(mrcp_session_descriptor_t*) mrcp_resource_discovery_response_generate( + const rtsp_message_t *request, + const rtsp_message_t *response, + const apr_table_t *resource_map, + apr_pool_t *pool, + su_home_t *home); + +/** Generate RTSP resource discovery response */ +MRCP_DECLARE(rtsp_message_t*) rtsp_resource_discovery_response_generate( + const rtsp_message_t *request, + const char *ip, + const char *origin, + apr_pool_t *pool); + + +/** Get MRCP resource name by RTSP resource name */ +MRCP_DECLARE(const char*) mrcp_name_get_by_rtsp_name(const apr_table_t *resource_map, const char *rtsp_name); +/** Get RTSP resource name by MRCP resource name */ +MRCP_DECLARE(const char*) rtsp_name_get_by_mrcp_name(const apr_table_t *resource_map, const char *mrcp_name); + +APT_END_EXTERN_C + +#endif /*__MRCP_UNIRTSP_SDP_H__*/ diff --git a/libs/unimrcp/modules/mrcp-unirtsp/include/mrcp_unirtsp_server_agent.h b/libs/unimrcp/modules/mrcp-unirtsp/include/mrcp_unirtsp_server_agent.h new file mode 100644 index 0000000000..e117f2a672 --- /dev/null +++ b/libs/unimrcp/modules/mrcp-unirtsp/include/mrcp_unirtsp_server_agent.h @@ -0,0 +1,68 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __MRCP_UNIRTSP_SERVER_AGENT_H__ +#define __MRCP_UNIRTSP_SERVER_AGENT_H__ + +/** + * @file mrcp_unirtsp_server_agent.h + * @brief Implementation of MRCP Signaling Interface using UniRTSP + */ + +#include +#include "mrcp_sig_agent.h" + +APT_BEGIN_EXTERN_C + +/** UniRTSP config declaration */ +typedef struct rtsp_server_config_t rtsp_server_config_t; + +/** UniRTSP config */ +struct rtsp_server_config_t { + /** Local IP address to bind to */ + char *local_ip; + /** Local port to bind to */ + apr_port_t local_port; + + /** Resource location */ + char *resource_location; + /** SDP origin */ + char *origin; + + /** Map of the MRCP resource names */ + apr_table_t *resource_map; + + /** Number of max RTSP connections */ + apr_size_t max_connection_count; + + /** Force destination ip address. Should be used only in case + SDP contains incorrect connection address (local IP address behind NAT) */ + apt_bool_t force_destination; +}; + +/** + * Create UniRTSP signaling agent. + */ +MRCP_DECLARE(mrcp_sig_agent_t*) mrcp_unirtsp_server_agent_create(rtsp_server_config_t *config, apr_pool_t *pool); + +/** + * Allocate UniRTSP config. + */ +MRCP_DECLARE(rtsp_server_config_t*) mrcp_unirtsp_server_config_alloc(apr_pool_t *pool); + +APT_END_EXTERN_C + +#endif /*__MRCP_UNIRTSP_SERVER_AGENT_H__*/ diff --git a/libs/unimrcp/modules/mrcp-unirtsp/mrcpunirtsp.vcproj b/libs/unimrcp/modules/mrcp-unirtsp/mrcpunirtsp.vcproj new file mode 100644 index 0000000000..1d25d5a20e --- /dev/null +++ b/libs/unimrcp/modules/mrcp-unirtsp/mrcpunirtsp.vcproj @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/modules/mrcp-unirtsp/src/mrcp_unirtsp_client_agent.c b/libs/unimrcp/modules/mrcp-unirtsp/src/mrcp_unirtsp_client_agent.c new file mode 100644 index 0000000000..85876f7c47 --- /dev/null +++ b/libs/unimrcp/modules/mrcp-unirtsp/src/mrcp_unirtsp_client_agent.c @@ -0,0 +1,437 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include + +#include "mrcp_unirtsp_client_agent.h" +#include "mrcp_session.h" +#include "mrcp_session_descriptor.h" +#include "mrcp_message.h" +#include "mrcp_stream.h" +#include "mrcp_resource_factory.h" +#include "rtsp_client.h" +#include "mrcp_unirtsp_sdp.h" +#include "apt_consumer_task.h" +#include "apt_log.h" + +#define UNIRTSP_TASK_NAME "UniRTSP Agent" + +typedef struct mrcp_unirtsp_agent_t mrcp_unirtsp_agent_t; +typedef struct mrcp_unirtsp_session_t mrcp_unirtsp_session_t; + +struct mrcp_unirtsp_agent_t { + mrcp_sig_agent_t *sig_agent; + rtsp_client_t *rtsp_client; + + rtsp_client_config_t *config; +}; + +struct mrcp_unirtsp_session_t { + mrcp_message_t *mrcp_message; + mrcp_session_t *mrcp_session; + rtsp_client_session_t *rtsp_session; + su_home_t *home; +}; + + +static apt_bool_t mrcp_unirtsp_session_offer(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor); +static apt_bool_t mrcp_unirtsp_session_terminate(mrcp_session_t *session); +static apt_bool_t mrcp_unirtsp_session_control(mrcp_session_t *session, mrcp_message_t *message); +static apt_bool_t mrcp_unirtsp_resource_discover(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor); + +static const mrcp_session_request_vtable_t session_request_vtable = { + mrcp_unirtsp_session_offer, + mrcp_unirtsp_session_terminate, + mrcp_unirtsp_session_control, + mrcp_unirtsp_resource_discover +}; + +static apt_bool_t mrcp_unirtsp_on_session_terminate_response(rtsp_client_t *client, rtsp_client_session_t *session); +static apt_bool_t mrcp_unirtsp_on_session_terminate_event(rtsp_client_t *client, rtsp_client_session_t *session); +static apt_bool_t mrcp_unirtsp_on_session_response(rtsp_client_t *client, rtsp_client_session_t *session, rtsp_message_t *request, rtsp_message_t *response); +static apt_bool_t mrcp_unirtsp_on_session_event(rtsp_client_t *client, rtsp_client_session_t *session, rtsp_message_t *message); + +static const rtsp_client_vtable_t session_response_vtable = { + mrcp_unirtsp_on_session_terminate_response, + mrcp_unirtsp_on_session_terminate_event, + mrcp_unirtsp_on_session_response, + mrcp_unirtsp_on_session_event +}; + +static apt_bool_t mrcp_unirtsp_session_create(mrcp_session_t *session); +static apt_bool_t rtsp_config_validate(mrcp_unirtsp_agent_t *agent, rtsp_client_config_t *config, apr_pool_t *pool); +static apt_bool_t mrcp_unirtsp_on_resource_discover(mrcp_unirtsp_agent_t *agent, mrcp_unirtsp_session_t *session, rtsp_message_t *request, rtsp_message_t *response); + + +/** Create UniRTSP Signaling Agent */ +MRCP_DECLARE(mrcp_sig_agent_t*) mrcp_unirtsp_client_agent_create(rtsp_client_config_t *config, apr_pool_t *pool) +{ + apt_task_t *task; + mrcp_unirtsp_agent_t *agent; + agent = apr_palloc(pool,sizeof(mrcp_unirtsp_agent_t)); + agent->sig_agent = mrcp_signaling_agent_create(agent,MRCP_VERSION_1,pool); + agent->sig_agent->create_client_session = mrcp_unirtsp_session_create; + agent->config = config; + + if(rtsp_config_validate(agent,config,pool) == FALSE) { + return NULL; + } + + agent->rtsp_client = rtsp_client_create(config->max_connection_count, + agent,&session_response_vtable,pool); + if(!agent->rtsp_client) { + return NULL; + } + + task = rtsp_client_task_get(agent->rtsp_client); + apt_task_name_set(task,UNIRTSP_TASK_NAME); + agent->sig_agent->task = task; + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create "UNIRTSP_TASK_NAME" %s:%hu [%d]", + config->server_ip, + config->server_port, + config->max_connection_count); + return agent->sig_agent; +} + +/** Allocate UniRTSP config */ +MRCP_DECLARE(rtsp_client_config_t*) mrcp_unirtsp_client_config_alloc(apr_pool_t *pool) +{ + rtsp_client_config_t *config = apr_palloc(pool,sizeof(rtsp_client_config_t)); + config->server_ip = NULL; + config->server_port = 0; + config->resource_location = NULL; + config->origin = NULL; + config->resource_map = apr_table_make(pool,2); + config->max_connection_count = 100; + config->force_destination = FALSE; + return config; +} + + +static apt_bool_t rtsp_config_validate(mrcp_unirtsp_agent_t *agent, rtsp_client_config_t *config, apr_pool_t *pool) +{ + if(!config->server_ip) { + return FALSE; + } + agent->config = config; + return TRUE; +} + +static APR_INLINE mrcp_unirtsp_agent_t* client_agent_get(apt_task_t *task) +{ + apt_consumer_task_t *consumer_task = apt_task_object_get(task); + mrcp_unirtsp_agent_t *agent = apt_consumer_task_object_get(consumer_task); + return agent; +} + +static apt_bool_t mrcp_unirtsp_session_create(mrcp_session_t *mrcp_session) +{ + mrcp_unirtsp_agent_t *agent = mrcp_session->signaling_agent->obj; + mrcp_unirtsp_session_t *session; + mrcp_session->request_vtable = &session_request_vtable; + + session = apr_palloc(mrcp_session->pool,sizeof(mrcp_unirtsp_session_t)); + session->home = su_home_new(sizeof(*session->home)); + session->mrcp_message = NULL; + session->mrcp_session = mrcp_session; + mrcp_session->obj = session; + + session->rtsp_session = rtsp_client_session_create( + agent->rtsp_client, + agent->config->server_ip, + agent->config->server_port, + agent->config->resource_location); + if(!session->rtsp_session) { + su_home_unref(session->home); + return FALSE; + } + rtsp_client_session_object_set(session->rtsp_session,session); + return TRUE; +} + +static void mrcp_unirtsp_session_destroy(mrcp_unirtsp_session_t *session) +{ + if(session->home) { + su_home_unref(session->home); + session->home = NULL; + } + rtsp_client_session_object_set(session->rtsp_session,NULL); + rtsp_client_session_destroy(session->rtsp_session); +} + +static apt_bool_t mrcp_unirtsp_on_session_terminate_response(rtsp_client_t *rtsp_client, rtsp_client_session_t *rtsp_session) +{ + mrcp_unirtsp_session_t *session = rtsp_client_session_object_get(rtsp_session); + + mrcp_unirtsp_session_destroy(session); + mrcp_session_terminate_response(session->mrcp_session); + return TRUE; +} + +static apt_bool_t mrcp_unirtsp_on_session_terminate_event(rtsp_client_t *rtsp_client, rtsp_client_session_t *rtsp_session) +{ + mrcp_unirtsp_session_t *session = rtsp_client_session_object_get(rtsp_session); + mrcp_session_terminate_event(session->mrcp_session); + return TRUE; +} + +static apt_bool_t mrcp_unirtsp_on_announce_response(mrcp_unirtsp_agent_t *agent, mrcp_unirtsp_session_t *session, rtsp_message_t *message, const char *resource_name) +{ + mrcp_message_t *mrcp_message = NULL; + + if(!session || !resource_name) { + return FALSE; + } + + if(rtsp_header_property_check(&message->header.property_set,RTSP_HEADER_FIELD_CONTENT_TYPE) == TRUE && + message->header.content_type == RTSP_CONTENT_TYPE_MRCP && + rtsp_header_property_check(&message->header.property_set,RTSP_HEADER_FIELD_CONTENT_LENGTH) == TRUE && + message->header.content_length > 0) { + + apt_text_stream_t text_stream; + mrcp_parser_t *parser; + apt_str_t resource_name_str; + + text_stream.text = message->body; + text_stream.pos = text_stream.text.buf; + apt_string_set(&resource_name_str,resource_name); + + parser = mrcp_parser_create(agent->sig_agent->resource_factory,session->mrcp_session->pool); + mrcp_parser_resource_name_set(parser,&resource_name_str); + if(mrcp_parser_run(parser,&text_stream) == MRCP_STREAM_MESSAGE_COMPLETE) { + mrcp_message = mrcp_parser_message_get(parser); + mrcp_message->channel_id.session_id = message->header.session_id; + } + else { + /* error case */ + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Parse MRCPv1 Message"); + } + } + else { + /* error case */ + } + + if(!mrcp_message) { + if(!session->mrcp_message) { + return FALSE; + } + mrcp_message = mrcp_response_create(session->mrcp_message,session->mrcp_session->pool); + mrcp_message->start_line.status_code = MRCP_STATUS_CODE_METHOD_FAILED; + } + + session->mrcp_message = NULL; + mrcp_session_control_response(session->mrcp_session,mrcp_message); + return TRUE; +} + +static apt_bool_t mrcp_unirtsp_on_session_response(rtsp_client_t *rtsp_client, rtsp_client_session_t *rtsp_session, rtsp_message_t *request, rtsp_message_t *response) +{ + apt_bool_t status = FALSE; + mrcp_unirtsp_agent_t *agent = rtsp_client_object_get(rtsp_client); + mrcp_unirtsp_session_t *session = rtsp_client_session_object_get(rtsp_session); + if(!agent || !session) { + return FALSE; + } + + switch(request->start_line.common.request_line.method_id) { + case RTSP_METHOD_SETUP: + { + const apt_str_t *session_id; + const char *force_destination_ip = NULL; + mrcp_session_descriptor_t *descriptor; + + if(agent->config->force_destination == TRUE) { + force_destination_ip = agent->config->server_ip; + } + + descriptor = mrcp_descriptor_generate_by_rtsp_response( + request, + response, + force_destination_ip, + agent->config->resource_map, + session->mrcp_session->pool, + session->home); + if(!descriptor) { + return FALSE; + } + session_id = rtsp_client_session_id_get(session->rtsp_session); + if(session_id) { + apt_string_copy( + &session->mrcp_session->id, + session_id, + session->mrcp_session->pool); + } + status = mrcp_session_answer(session->mrcp_session,descriptor); + break; + } + case RTSP_METHOD_TEARDOWN: + { + mrcp_session_descriptor_t *descriptor; + descriptor = mrcp_descriptor_generate_by_rtsp_response( + request, + response, + NULL, + agent->config->resource_map, + session->mrcp_session->pool, + session->home); + if(!descriptor) { + return FALSE; + } + status = mrcp_session_answer(session->mrcp_session,descriptor); + break; + } + case RTSP_METHOD_ANNOUNCE: + { + mrcp_unirtsp_agent_t *agent = rtsp_client_object_get(rtsp_client); + const char *resource_name = mrcp_name_get_by_rtsp_name( + agent->config->resource_map, + request->start_line.common.request_line.resource_name); + mrcp_unirtsp_on_announce_response(agent,session,response,resource_name); + break; + } + case RTSP_METHOD_DESCRIBE: + { + mrcp_unirtsp_agent_t *agent = rtsp_client_object_get(rtsp_client); + mrcp_unirtsp_on_resource_discover(agent,session,request,response); + break; + } + default: + break; + } + + return status; +} + +static apt_bool_t mrcp_unirtsp_on_session_event(rtsp_client_t *rtsp_client, rtsp_client_session_t *rtsp_session, rtsp_message_t *message) +{ + mrcp_unirtsp_agent_t *agent = rtsp_client_object_get(rtsp_client); + mrcp_unirtsp_session_t *session = rtsp_client_session_object_get(rtsp_session); + const char *resource_name = mrcp_name_get_by_rtsp_name( + agent->config->resource_map, + message->start_line.common.request_line.resource_name); + if(!session || !resource_name) { + return FALSE; + } + + mrcp_unirtsp_on_announce_response(agent,session,message,resource_name); + return TRUE; +} + +static apt_bool_t mrcp_unirtsp_session_offer(mrcp_session_t *mrcp_session, mrcp_session_descriptor_t *descriptor) +{ + mrcp_unirtsp_session_t *session = mrcp_session->obj; + mrcp_unirtsp_agent_t *agent = mrcp_session->signaling_agent->obj; + rtsp_message_t *request; + + if(agent->config->origin) { + apt_string_set(&descriptor->origin,agent->config->origin); + } + + request = rtsp_request_generate_by_mrcp_descriptor(descriptor,agent->config->resource_map,mrcp_session->pool); + return rtsp_client_session_request(agent->rtsp_client,session->rtsp_session,request); +} + +static apt_bool_t mrcp_unirtsp_session_terminate(mrcp_session_t *mrcp_session) +{ + mrcp_unirtsp_session_t *session = mrcp_session->obj; + mrcp_unirtsp_agent_t *agent = mrcp_session->signaling_agent->obj; + + return rtsp_client_session_terminate(agent->rtsp_client,session->rtsp_session); +} + +static apt_bool_t mrcp_unirtsp_session_control(mrcp_session_t *mrcp_session, mrcp_message_t *mrcp_message) +{ + mrcp_unirtsp_session_t *session = mrcp_session->obj; + mrcp_unirtsp_agent_t *agent = mrcp_session->signaling_agent->obj; + + char buffer[500]; + apt_text_stream_t stream; + rtsp_message_t *rtsp_message = NULL; + apt_str_t *body; + + apt_text_stream_init(&stream,buffer,sizeof(buffer)); + + mrcp_message->start_line.version = MRCP_VERSION_1; + if(mrcp_message_generate(agent->sig_agent->resource_factory,mrcp_message,&stream) != TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Generate MRCPv1 Message"); + return FALSE; + } + stream.text.length = stream.pos - stream.text.buf; + + rtsp_message = rtsp_request_create(mrcp_session->pool); + rtsp_message->start_line.common.request_line.resource_name = rtsp_name_get_by_mrcp_name( + agent->config->resource_map, + mrcp_message->channel_id.resource_name.buf); + rtsp_message->start_line.common.request_line.method_id = RTSP_METHOD_ANNOUNCE; + + body = &rtsp_message->body; + body->length = mrcp_message->start_line.length; + body->buf = apr_palloc(rtsp_message->pool,body->length+1); + memcpy(body->buf,stream.text.buf,stream.text.length); + if(mrcp_message->body.length) { + memcpy(body->buf+stream.text.length,mrcp_message->body.buf,mrcp_message->body.length); + } + body->buf[body->length] = '\0'; + + rtsp_message->header.content_type = RTSP_CONTENT_TYPE_MRCP; + rtsp_header_property_add(&rtsp_message->header.property_set,RTSP_HEADER_FIELD_CONTENT_TYPE); + rtsp_message->header.content_length = body->length; + rtsp_header_property_add(&rtsp_message->header.property_set,RTSP_HEADER_FIELD_CONTENT_LENGTH); + + session->mrcp_message = mrcp_message; + rtsp_client_session_request(agent->rtsp_client,session->rtsp_session,rtsp_message); + return TRUE; +} + +static apt_bool_t mrcp_unirtsp_resource_discover(mrcp_session_t *mrcp_session, mrcp_session_descriptor_t *descriptor) +{ + mrcp_unirtsp_session_t *session = mrcp_session->obj; + mrcp_unirtsp_agent_t *agent = mrcp_session->signaling_agent->obj; + rtsp_message_t *request; + + if(!descriptor) { + return FALSE; + } + request = rtsp_resource_discovery_request_generate( + descriptor->resource_name.buf, + agent->config->resource_map, + mrcp_session->pool); + if(request) { + rtsp_client_session_request(agent->rtsp_client,session->rtsp_session,request); + } + return TRUE; +} + +static apt_bool_t mrcp_unirtsp_on_resource_discover(mrcp_unirtsp_agent_t *agent, mrcp_unirtsp_session_t *session, rtsp_message_t *request, rtsp_message_t *response) +{ + mrcp_session_descriptor_t *descriptor; + if(!session) { + return FALSE; + } + + descriptor = mrcp_resource_discovery_response_generate( + request, + response, + agent->config->resource_map, + session->mrcp_session->pool, + session->home); + if(descriptor) { + mrcp_session_discover_response(session->mrcp_session,descriptor); + } + return TRUE; +} diff --git a/libs/unimrcp/modules/mrcp-unirtsp/src/mrcp_unirtsp_sdp.c b/libs/unimrcp/modules/mrcp-unirtsp/src/mrcp_unirtsp_sdp.c new file mode 100644 index 0000000000..fc33c9477d --- /dev/null +++ b/libs/unimrcp/modules/mrcp-unirtsp/src/mrcp_unirtsp_sdp.c @@ -0,0 +1,576 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include +#include "rtsp_message.h" +#include "mrcp_unirtsp_sdp.h" +#include "mpf_rtp_attribs.h" +#include "apt_text_stream.h" +#include "apt_log.h" + + +/** Generate SDP media by RTP media descriptor */ +static apr_size_t sdp_rtp_media_generate(char *buffer, apr_size_t size, const mrcp_session_descriptor_t *descriptor, const mpf_rtp_media_descriptor_t *audio_media) +{ + apr_size_t offset = 0; + int i; + mpf_codec_descriptor_t *codec_descriptor; + apr_array_header_t *descriptor_arr = audio_media->codec_list.descriptor_arr; + if(!descriptor_arr) { + return 0; + } + offset += snprintf(buffer+offset,size-offset, + "m=audio %d RTP/AVP", + audio_media->base.state == MPF_MEDIA_ENABLED ? audio_media->base.port : 0); + for(i=0; inelts; i++) { + codec_descriptor = (mpf_codec_descriptor_t*)descriptor_arr->elts + i; + if(codec_descriptor->enabled == TRUE) { + offset += snprintf(buffer+offset,size-offset," %d", codec_descriptor->payload_type); + } + } + offset += snprintf(buffer+offset,size-offset,"\r\n"); + if(audio_media->base.state == MPF_MEDIA_ENABLED) { + const apt_str_t *mode_str = mpf_stream_mode_str_get(audio_media->mode); + for(i=0; inelts; i++) { + codec_descriptor = (mpf_codec_descriptor_t*)descriptor_arr->elts + i; + if(codec_descriptor->enabled == TRUE && codec_descriptor->name.buf) { + offset += snprintf(buffer+offset,size-offset,"a=rtpmap:%d %s/%d\r\n", + codec_descriptor->payload_type, + codec_descriptor->name.buf, + codec_descriptor->sampling_rate); + } + } + offset += snprintf(buffer+offset,size-offset,"a=%s\r\n",mode_str ? mode_str->buf : ""); + + if(audio_media->ptime) { + offset += snprintf(buffer+offset,size-offset,"a=ptime:%hu\r\n", + audio_media->ptime); + } + } + return offset; +} + +/** Generate RTP media descriptor by SDP media */ +static apt_bool_t mpf_rtp_media_generate(mpf_rtp_media_descriptor_t *rtp_media, const sdp_media_t *sdp_media, const apt_str_t *ip, apr_pool_t *pool) +{ + mpf_rtp_attrib_e id; + apt_str_t name; + sdp_attribute_t *attrib = NULL; + sdp_rtpmap_t *map; + mpf_codec_descriptor_t *codec; + for(attrib = sdp_media->m_attributes; attrib; attrib=attrib->a_next) { + apt_string_set(&name,attrib->a_name); + id = mpf_rtp_attrib_id_find(&name); + switch(id) { + case RTP_ATTRIB_PTIME: + rtp_media->ptime = (apr_uint16_t)atoi(attrib->a_value); + break; + default: + break; + } + } + + mpf_codec_list_init(&rtp_media->codec_list,5,pool); + for(map = sdp_media->m_rtpmaps; map; map = map->rm_next) { + codec = mpf_codec_list_add(&rtp_media->codec_list); + if(codec) { + codec->payload_type = (apr_byte_t)map->rm_pt; + apt_string_assign(&codec->name,map->rm_encoding,pool); + codec->sampling_rate = (apr_uint16_t)map->rm_rate; + codec->channel_count = 1; + } + } + + switch(sdp_media->m_mode) { + case sdp_inactive: + rtp_media->mode = STREAM_MODE_NONE; + break; + case sdp_sendonly: + rtp_media->mode = STREAM_MODE_SEND; + break; + case sdp_recvonly: + rtp_media->mode = STREAM_MODE_RECEIVE; + break; + case sdp_sendrecv: + rtp_media->mode = STREAM_MODE_SEND_RECEIVE; + break; + } + + if(sdp_media->m_connections) { + apt_string_assign(&rtp_media->base.ip,sdp_media->m_connections->c_address,pool); + } + else { + rtp_media->base.ip = *ip; + } + if(sdp_media->m_port) { + rtp_media->base.port = (apr_port_t)sdp_media->m_port; + rtp_media->base.state = MPF_MEDIA_ENABLED; + } + else { + rtp_media->base.state = MPF_MEDIA_DISABLED; + } + return TRUE; +} + +/** Generate MRCP descriptor by SDP session */ +static mrcp_session_descriptor_t* mrcp_descriptor_generate_by_sdp_session(mrcp_session_descriptor_t *descriptor, const sdp_session_t *sdp, const char *force_destination_ip, apr_pool_t *pool) +{ + sdp_media_t *sdp_media; + + if(force_destination_ip) { + apt_string_assign(&descriptor->ip,force_destination_ip,pool); + } + else if(sdp->sdp_connection) { + apt_string_assign(&descriptor->ip,sdp->sdp_connection->c_address,pool); + } + + for(sdp_media=sdp->sdp_media; sdp_media; sdp_media=sdp_media->m_next) { + switch(sdp_media->m_type) { + case sdp_media_audio: + { + mpf_rtp_media_descriptor_t *media = apr_palloc(pool,sizeof(mpf_rtp_media_descriptor_t)); + mpf_rtp_media_descriptor_init(media); + media->base.id = mrcp_session_audio_media_add(descriptor,media); + mpf_rtp_media_generate(media,sdp_media,&descriptor->ip,pool); + break; + } + case sdp_media_video: + { + mpf_rtp_media_descriptor_t *media = apr_palloc(pool,sizeof(mpf_rtp_media_descriptor_t)); + mpf_rtp_media_descriptor_init(media); + media->base.id = mrcp_session_video_media_add(descriptor,media); + mpf_rtp_media_generate(media,sdp_media,&descriptor->ip,pool); + break; + } + default: + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Not Supported SDP Media [%s]", sdp_media->m_type_name); + break; + } + } + return descriptor; +} + + +/** Generate MRCP descriptor by RTSP request */ +MRCP_DECLARE(mrcp_session_descriptor_t*) mrcp_descriptor_generate_by_rtsp_request( + const rtsp_message_t *request, + const char *force_destination_ip, + const apr_table_t *resource_map, + apr_pool_t *pool, + su_home_t *home) +{ + mrcp_session_descriptor_t *descriptor = NULL; + const char *resource_name = mrcp_name_get_by_rtsp_name( + resource_map, + request->start_line.common.request_line.resource_name); + if(!resource_name) { + return NULL; + } + + if(request->start_line.common.request_line.method_id == RTSP_METHOD_SETUP) { + if(rtsp_header_property_check(&request->header.property_set,RTSP_HEADER_FIELD_CONTENT_TYPE) == TRUE && + rtsp_header_property_check(&request->header.property_set,RTSP_HEADER_FIELD_CONTENT_LENGTH) == TRUE && + request->body.buf) { + + sdp_parser_t *parser; + sdp_session_t *sdp; + + parser = sdp_parse(home,request->body.buf,request->body.length,0); + sdp = sdp_session(parser); + if(sdp) { + descriptor = mrcp_session_descriptor_create(pool); + mrcp_descriptor_generate_by_sdp_session(descriptor,sdp,force_destination_ip,pool); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Parse SDP Message"); + } + sdp_parser_free(parser); + } + else { + /* create default descriptor in case RTSP SETUP contains no SDP */ + mpf_rtp_media_descriptor_t *media; + descriptor = mrcp_session_descriptor_create(pool); + media = apr_palloc(pool,sizeof(mpf_rtp_media_descriptor_t)); + mpf_rtp_media_descriptor_init(media); + media->base.state = MPF_MEDIA_ENABLED; + media->base.id = mrcp_session_audio_media_add(descriptor,media); + if(rtsp_header_property_check(&request->header.property_set,RTSP_HEADER_FIELD_TRANSPORT) == TRUE) { + media->base.port = request->header.transport.client_port_range.min; + media->base.ip = request->header.transport.destination; + } + } + + if(descriptor) { + apt_string_assign(&descriptor->resource_name,resource_name,pool); + descriptor->resource_state = TRUE; + } + } + else if(request->start_line.common.request_line.method_id == RTSP_METHOD_TEARDOWN) { + descriptor = mrcp_session_descriptor_create(pool); + apt_string_assign(&descriptor->resource_name,resource_name,pool); + descriptor->resource_state = FALSE; + } + return descriptor; +} + +/** Generate MRCP descriptor by RTSP response */ +MRCP_DECLARE(mrcp_session_descriptor_t*) mrcp_descriptor_generate_by_rtsp_response( + const rtsp_message_t *request, + const rtsp_message_t *response, + const char *force_destination_ip, + const apr_table_t *resource_map, + apr_pool_t *pool, + su_home_t *home) +{ + mrcp_session_descriptor_t *descriptor = NULL; + const char *resource_name = mrcp_name_get_by_rtsp_name( + resource_map, + request->start_line.common.request_line.resource_name); + if(!resource_name) { + return NULL; + } + + if(request->start_line.common.request_line.method_id == RTSP_METHOD_SETUP) { + if(rtsp_header_property_check(&response->header.property_set,RTSP_HEADER_FIELD_CONTENT_TYPE) == TRUE && + rtsp_header_property_check(&response->header.property_set,RTSP_HEADER_FIELD_CONTENT_LENGTH) == TRUE && + response->body.buf) { + + sdp_parser_t *parser; + sdp_session_t *sdp; + + parser = sdp_parse(home,response->body.buf,response->body.length,0); + sdp = sdp_session(parser); + if(sdp) { + descriptor = mrcp_session_descriptor_create(pool); + mrcp_descriptor_generate_by_sdp_session(descriptor,sdp,force_destination_ip,pool); + + apt_string_assign(&descriptor->resource_name,resource_name,pool); + descriptor->resource_state = TRUE; + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Parse SDP Message"); + } + + sdp_parser_free(parser); + } + else { + descriptor = mrcp_session_descriptor_create(pool); + apt_string_assign(&descriptor->resource_name,resource_name,pool); + descriptor->resource_state = FALSE; + } + } + else if(request->start_line.common.request_line.method_id == RTSP_METHOD_TEARDOWN) { + descriptor = mrcp_session_descriptor_create(pool); + apt_string_assign(&descriptor->resource_name,resource_name,pool); + descriptor->resource_state = FALSE; + } + return descriptor; +} + +/** Generate RTSP request by MRCP descriptor */ +MRCP_DECLARE(rtsp_message_t*) rtsp_request_generate_by_mrcp_descriptor(const mrcp_session_descriptor_t *descriptor, const apr_table_t *resource_map, apr_pool_t *pool) +{ + apr_size_t i; + apr_size_t count; + apr_size_t audio_index = 0; + mpf_rtp_media_descriptor_t *audio_media; + apr_size_t video_index = 0; + mpf_rtp_media_descriptor_t *video_media; + apr_size_t offset = 0; + char buffer[2048]; + apr_size_t size = sizeof(buffer); + rtsp_message_t *request; + const char *ip = descriptor->ext_ip.buf ? descriptor->ext_ip.buf : (descriptor->ip.buf ? descriptor->ip.buf : "0.0.0.0"); + + request = rtsp_request_create(pool); + request->start_line.common.request_line.resource_name = rtsp_name_get_by_mrcp_name( + resource_map, + descriptor->resource_name.buf); + if(descriptor->resource_state != TRUE) { + request->start_line.common.request_line.method_id = RTSP_METHOD_TEARDOWN; + return request; + } + + request->start_line.common.request_line.method_id = RTSP_METHOD_SETUP; + + buffer[0] = '\0'; + offset += snprintf(buffer+offset,size-offset, + "v=0\r\n" + "o=%s 0 0 IN IP4 %s\r\n" + "s=-\r\n" + "c=IN IP4 %s\r\n" + "t=0 0\r\n", + descriptor->origin.buf ? descriptor->origin.buf : "-", + ip, + ip); + count = mrcp_session_media_count_get(descriptor); + for(i=0; ibase.id == i) { + /* generate audio media */ + audio_index++; + offset += sdp_rtp_media_generate(buffer+offset,size-offset,descriptor,audio_media); + request->header.transport.client_port_range.min = audio_media->base.port; + request->header.transport.client_port_range.max = audio_media->base.port+1; + continue; + } + video_media = mrcp_session_video_media_get(descriptor,video_index); + if(video_media && video_media->base.id == i) { + /* generate video media */ + video_index++; + offset += sdp_rtp_media_generate(buffer+offset,size-offset,descriptor,video_media); + continue; + } + } + + request->header.transport.protocol = RTSP_TRANSPORT_RTP; + request->header.transport.profile = RTSP_PROFILE_AVP; + request->header.transport.delivery = RTSP_DELIVERY_UNICAST; + rtsp_header_property_add(&request->header.property_set,RTSP_HEADER_FIELD_TRANSPORT); + + if(offset) { + apt_string_assign_n(&request->body,buffer,offset,pool); + request->header.content_type = RTSP_CONTENT_TYPE_SDP; + rtsp_header_property_add(&request->header.property_set,RTSP_HEADER_FIELD_CONTENT_TYPE); + request->header.content_length = offset; + rtsp_header_property_add(&request->header.property_set,RTSP_HEADER_FIELD_CONTENT_LENGTH); + } + return request; +} + +/** Generate RTSP response by MRCP descriptor */ +MRCP_DECLARE(rtsp_message_t*) rtsp_response_generate_by_mrcp_descriptor(const rtsp_message_t *request, const mrcp_session_descriptor_t *descriptor, const apr_table_t *resource_map, apr_pool_t *pool) +{ + rtsp_message_t *response = NULL; + + switch(descriptor->status) { + case MRCP_SESSION_STATUS_OK: + response = rtsp_response_create(request,RTSP_STATUS_CODE_OK,RTSP_REASON_PHRASE_OK,pool); + break; + case MRCP_SESSION_STATUS_NO_SUCH_RESOURCE: + response = rtsp_response_create(request,RTSP_STATUS_CODE_NOT_FOUND,RTSP_REASON_PHRASE_NOT_FOUND,pool); + break; + case MRCP_SESSION_STATUS_UNACCEPTABLE_RESOURCE: + case MRCP_SESSION_STATUS_UNAVAILABLE_RESOURCE: + response = rtsp_response_create(request,RTSP_STATUS_CODE_NOT_ACCEPTABLE,RTSP_REASON_PHRASE_NOT_ACCEPTABLE,pool); + break; + case MRCP_SESSION_STATUS_ERROR: + response = rtsp_response_create(request,RTSP_STATUS_CODE_INTERNAL_SERVER_ERROR,RTSP_REASON_PHRASE_INTERNAL_SERVER_ERROR,pool); + break; + } + + if(!response) { + return NULL; + } + + if(descriptor->status == MRCP_SESSION_STATUS_OK) { + apr_size_t i; + apr_size_t count; + apr_size_t audio_index = 0; + mpf_rtp_media_descriptor_t *audio_media; + apr_size_t video_index = 0; + mpf_rtp_media_descriptor_t *video_media; + apr_size_t offset = 0; + char buffer[2048]; + apr_size_t size = sizeof(buffer); + const char *ip = descriptor->ext_ip.buf ? descriptor->ext_ip.buf : (descriptor->ip.buf ? descriptor->ip.buf : "0.0.0.0"); + + buffer[0] = '\0'; + offset += snprintf(buffer+offset,size-offset, + "v=0\r\n" + "o=%s 0 0 IN IP4 %s\r\n" + "s=-\r\n" + "c=IN IP4 %s\r\n" + "t=0 0\r\n", + descriptor->origin.buf ? descriptor->origin.buf : "-", + ip, + ip); + count = mrcp_session_media_count_get(descriptor); + for(i=0; ibase.id == i) { + /* generate audio media */ + audio_index++; + offset += sdp_rtp_media_generate(buffer+offset,size-offset,descriptor,audio_media); + response->header.transport.server_port_range.min = audio_media->base.port; + response->header.transport.server_port_range.max = audio_media->base.port+1; + response->header.transport.client_port_range = request->header.transport.client_port_range; + continue; + } + video_media = mrcp_session_video_media_get(descriptor,video_index); + if(video_media && video_media->base.id == i) { + /* generate video media */ + video_index++; + offset += sdp_rtp_media_generate(buffer+offset,size-offset,descriptor,video_media); + continue; + } + } + + /* ok */ + response->header.transport.protocol = RTSP_TRANSPORT_RTP; + response->header.transport.profile = RTSP_PROFILE_AVP; + response->header.transport.delivery = RTSP_DELIVERY_UNICAST; + rtsp_header_property_add(&response->header.property_set,RTSP_HEADER_FIELD_TRANSPORT); + + if(offset) { + apt_string_assign_n(&response->body,buffer,offset,pool); + response->header.content_type = RTSP_CONTENT_TYPE_SDP; + rtsp_header_property_add(&response->header.property_set,RTSP_HEADER_FIELD_CONTENT_TYPE); + response->header.content_length = offset; + rtsp_header_property_add(&response->header.property_set,RTSP_HEADER_FIELD_CONTENT_LENGTH); + } + } + return response; +} + +/** Generate RTSP resource discovery request */ +MRCP_DECLARE(rtsp_message_t*) rtsp_resource_discovery_request_generate( + const char *resource_name, + const apr_table_t *resource_map, + apr_pool_t *pool) +{ + rtsp_message_t *request = rtsp_request_create(pool); + request->start_line.common.request_line.resource_name = rtsp_name_get_by_mrcp_name( + resource_map, + resource_name); + + request->start_line.common.request_line.method_id = RTSP_METHOD_DESCRIBE; + return request; +} + +/** Generate resource descovery descriptor by RTSP response */ +MRCP_DECLARE(mrcp_session_descriptor_t*) mrcp_resource_discovery_response_generate( + const rtsp_message_t *request, + const rtsp_message_t *response, + const apr_table_t *resource_map, + apr_pool_t *pool, + su_home_t *home) +{ + mrcp_session_descriptor_t *descriptor = NULL; + const char *resource_name = mrcp_name_get_by_rtsp_name( + resource_map, + request->start_line.common.request_line.resource_name); + if(!resource_name) { + return NULL; + } + + descriptor = mrcp_session_descriptor_create(pool); + apt_string_assign(&descriptor->resource_name,resource_name,pool); + + if(rtsp_header_property_check(&response->header.property_set,RTSP_HEADER_FIELD_CONTENT_TYPE) == TRUE && + rtsp_header_property_check(&response->header.property_set,RTSP_HEADER_FIELD_CONTENT_LENGTH) == TRUE && + response->body.buf) { + + sdp_parser_t *parser; + sdp_session_t *sdp; + + parser = sdp_parse(home,response->body.buf,response->body.length,0); + sdp = sdp_session(parser); + if(sdp) { + mrcp_descriptor_generate_by_sdp_session(descriptor,sdp,0,pool); + descriptor->resource_state = TRUE; + } + else { + apt_string_assign(&descriptor->resource_name,resource_name,pool); + descriptor->resource_state = TRUE; + } + + sdp_parser_free(parser); + } + else { + descriptor->resource_state = FALSE; + } + return descriptor; +} + +/** Generate RTSP resource discovery response */ +MRCP_DECLARE(rtsp_message_t*) rtsp_resource_discovery_response_generate( + const rtsp_message_t *request, + const char *ip, + const char *origin, + apr_pool_t *pool) +{ + rtsp_message_t *response = rtsp_response_create(request,RTSP_STATUS_CODE_OK,RTSP_REASON_PHRASE_OK,pool); + if(response) { + apr_size_t offset = 0; + char buffer[2048]; + apr_size_t size = sizeof(buffer); + + if(!ip) { + ip = "0.0.0.0"; + } + if(!origin) { + origin = "-"; + } + + buffer[0] = '\0'; + offset += snprintf(buffer+offset,size-offset, + "v=0\r\n" + "o=%s 0 0 IN IP4 %s\r\n" + "s=-\r\n" + "c=IN IP4 %s\r\n" + "t=0 0\r\n" + "m=audio 0 RTP/AVP 0 8\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n", + origin, + ip, + ip); + + response->header.transport.protocol = RTSP_TRANSPORT_RTP; + response->header.transport.profile = RTSP_PROFILE_AVP; + response->header.transport.delivery = RTSP_DELIVERY_UNICAST; + rtsp_header_property_add(&response->header.property_set,RTSP_HEADER_FIELD_TRANSPORT); + + if(offset) { + apt_string_assign_n(&response->body,buffer,offset,pool); + response->header.content_type = RTSP_CONTENT_TYPE_SDP; + rtsp_header_property_add(&response->header.property_set,RTSP_HEADER_FIELD_CONTENT_TYPE); + response->header.content_length = offset; + rtsp_header_property_add(&response->header.property_set,RTSP_HEADER_FIELD_CONTENT_LENGTH); + } + } + + return response; +} + +/** Get MRCP resource name by RTSP resource name */ +MRCP_DECLARE(const char*) mrcp_name_get_by_rtsp_name(const apr_table_t *resource_map, const char *rtsp_name) +{ + const apr_array_header_t *header = apr_table_elts(resource_map); + apr_table_entry_t *entry = (apr_table_entry_t *)header->elts; + int i; + + for(i=0; inelts; i++) { + if(entry[i].val && rtsp_name) { + if(apr_strnatcasecmp(entry[i].val,rtsp_name) == 0) { + return entry[i].key; + } + } + } + return rtsp_name; +} + +/** Get RTSP resource name by MRCP resource name */ +MRCP_DECLARE(const char*) rtsp_name_get_by_mrcp_name(const apr_table_t *resource_map, const char *mrcp_name) +{ + const char *rtsp_name = apr_table_get(resource_map,mrcp_name); + if(rtsp_name) { + return rtsp_name; + } + return mrcp_name; +} diff --git a/libs/unimrcp/modules/mrcp-unirtsp/src/mrcp_unirtsp_server_agent.c b/libs/unimrcp/modules/mrcp-unirtsp/src/mrcp_unirtsp_server_agent.c new file mode 100644 index 0000000000..12de96a741 --- /dev/null +++ b/libs/unimrcp/modules/mrcp-unirtsp/src/mrcp_unirtsp_server_agent.c @@ -0,0 +1,381 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include + +#include "mrcp_unirtsp_server_agent.h" +#include "mrcp_session.h" +#include "mrcp_session_descriptor.h" +#include "mrcp_message.h" +#include "mrcp_resource_factory.h" +#include "mrcp_stream.h" +#include "rtsp_server.h" +#include "mrcp_unirtsp_sdp.h" +#include "apt_consumer_task.h" +#include "apt_log.h" + +#define UNIRTSP_TASK_NAME "UniRTSP Agent" + +typedef struct mrcp_unirtsp_agent_t mrcp_unirtsp_agent_t; +typedef struct mrcp_unirtsp_session_t mrcp_unirtsp_session_t; + +struct mrcp_unirtsp_agent_t { + mrcp_sig_agent_t *sig_agent; + rtsp_server_t *rtsp_server; + + rtsp_server_config_t *config; +}; + +struct mrcp_unirtsp_session_t { + mrcp_session_t *mrcp_session; + rtsp_server_session_t *rtsp_session; + su_home_t *home; +}; + + +static apt_bool_t mrcp_unirtsp_on_session_answer(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor); +static apt_bool_t mrcp_unirtsp_on_session_terminate(mrcp_session_t *session); +static apt_bool_t mrcp_unirtsp_on_session_control(mrcp_session_t *session, mrcp_message_t *message); + +static const mrcp_session_response_vtable_t session_response_vtable = { + mrcp_unirtsp_on_session_answer, + mrcp_unirtsp_on_session_terminate, + mrcp_unirtsp_on_session_control +}; + +static apt_bool_t mrcp_unirtsp_session_create(rtsp_server_t *server, rtsp_server_session_t *session); +static apt_bool_t mrcp_unirtsp_session_terminate(rtsp_server_t *server, rtsp_server_session_t *session); +static apt_bool_t mrcp_unirtsp_message_handle(rtsp_server_t *server, rtsp_server_session_t *session, rtsp_message_t *message); + +static const rtsp_server_vtable_t session_request_vtable = { + mrcp_unirtsp_session_create, + mrcp_unirtsp_session_terminate, + mrcp_unirtsp_message_handle +}; + + +static apt_bool_t rtsp_config_validate(mrcp_unirtsp_agent_t *agent, rtsp_server_config_t *config, apr_pool_t *pool); + + +/** Create UniRTSP Signaling Agent */ +MRCP_DECLARE(mrcp_sig_agent_t*) mrcp_unirtsp_server_agent_create(rtsp_server_config_t *config, apr_pool_t *pool) +{ + apt_task_t *task; + mrcp_unirtsp_agent_t *agent; + agent = apr_palloc(pool,sizeof(mrcp_unirtsp_agent_t)); + agent->sig_agent = mrcp_signaling_agent_create(agent,MRCP_VERSION_1,pool); + agent->config = config; + + if(rtsp_config_validate(agent,config,pool) == FALSE) { + return NULL; + } + + agent->rtsp_server = rtsp_server_create( + config->local_ip, + config->local_port, + config->max_connection_count, + agent, + &session_request_vtable, + pool); + if(!agent->rtsp_server) { + return NULL; + } + + task = rtsp_server_task_get(agent->rtsp_server); + apt_task_name_set(task,UNIRTSP_TASK_NAME); + agent->sig_agent->task = task; + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create "UNIRTSP_TASK_NAME" %s:%hu [%d]", + config->local_ip, + config->local_port, + config->max_connection_count); + return agent->sig_agent; +} + +/** Allocate UniRTSP config */ +MRCP_DECLARE(rtsp_server_config_t*) mrcp_unirtsp_server_config_alloc(apr_pool_t *pool) +{ + rtsp_server_config_t *config = apr_palloc(pool,sizeof(rtsp_server_config_t)); + config->local_ip = NULL; + config->local_port = 0; + config->origin = NULL; + config->resource_location = NULL; + config->resource_map = apr_table_make(pool,2); + config->max_connection_count = 100; + config->force_destination = FALSE; + return config; +} + + +static apt_bool_t rtsp_config_validate(mrcp_unirtsp_agent_t *agent, rtsp_server_config_t *config, apr_pool_t *pool) +{ + if(!config->local_ip) { + return FALSE; + } + agent->config = config; + return TRUE; +} + +static APR_INLINE mrcp_unirtsp_agent_t* server_agent_get(apt_task_t *task) +{ + apt_consumer_task_t *consumer_task = apt_task_object_get(task); + mrcp_unirtsp_agent_t *agent = apt_consumer_task_object_get(consumer_task); + return agent; +} + +static apt_bool_t mrcp_unirtsp_session_create(rtsp_server_t *rtsp_server, rtsp_server_session_t *rtsp_session) +{ + mrcp_unirtsp_agent_t *agent = rtsp_server_object_get(rtsp_server); + const apt_str_t *session_id; + mrcp_unirtsp_session_t *session; + mrcp_session_t* mrcp_session = agent->sig_agent->create_server_session(agent->sig_agent); + if(!mrcp_session) { + return FALSE; + } + session_id = rtsp_server_session_id_get(rtsp_session); + if(session_id) { + mrcp_session->id = *session_id; + } + mrcp_session->response_vtable = &session_response_vtable; + mrcp_session->event_vtable = NULL; + + session = apr_palloc(mrcp_session->pool,sizeof(mrcp_unirtsp_session_t)); + session->mrcp_session = mrcp_session; + mrcp_session->obj = session; + + session->home = su_home_new(sizeof(*session->home)); + + rtsp_server_session_object_set(rtsp_session,session); + session->rtsp_session = rtsp_session; + return TRUE; +} + +static apt_bool_t mrcp_unirtsp_session_terminate(rtsp_server_t *rtsp_server, rtsp_server_session_t *rtsp_session) +{ + mrcp_unirtsp_session_t *session = rtsp_server_session_object_get(rtsp_session); + if(!session) { + return FALSE; + } + return mrcp_session_terminate_request(session->mrcp_session); +} + +static void mrcp_unirtsp_session_destroy(mrcp_unirtsp_session_t *session) +{ + if(session->home) { + su_home_unref(session->home); + session->home = NULL; + } + rtsp_server_session_object_set(session->rtsp_session,NULL); + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Destroy Session "APT_SID_FMT,MRCP_SESSION_SID(session->mrcp_session)); + mrcp_session_destroy(session->mrcp_session); +} + +static apt_bool_t mrcp_unirtsp_session_announce(mrcp_unirtsp_agent_t *agent, mrcp_unirtsp_session_t *session, rtsp_message_t *message) +{ + const char *resource_name = mrcp_name_get_by_rtsp_name( + agent->config->resource_map, + message->start_line.common.request_line.resource_name); + apt_bool_t status = TRUE; + + if(session && resource_name && + rtsp_header_property_check(&message->header.property_set,RTSP_HEADER_FIELD_CONTENT_TYPE) == TRUE && + message->header.content_type == RTSP_CONTENT_TYPE_MRCP && + rtsp_header_property_check(&message->header.property_set,RTSP_HEADER_FIELD_CONTENT_LENGTH) == TRUE && + message->header.content_length > 0) { + + apt_text_stream_t text_stream; + mrcp_parser_t *parser; + apt_str_t resource_name_str; + + text_stream.text = message->body; + text_stream.pos = text_stream.text.buf; + apt_string_set(&resource_name_str,resource_name); + + parser = mrcp_parser_create(agent->sig_agent->resource_factory,session->mrcp_session->pool); + mrcp_parser_resource_name_set(parser,&resource_name_str); + if(mrcp_parser_run(parser,&text_stream) == MRCP_STREAM_MESSAGE_COMPLETE) { + mrcp_message_t *mrcp_message = mrcp_parser_message_get(parser); + mrcp_message->channel_id.session_id = message->header.session_id; + status = mrcp_session_control_request(session->mrcp_session,mrcp_message); + } + else { + /* error response */ + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Parse MRCPv1 Message"); + status = FALSE; + } + } + else { + /* error response */ + status = FALSE; + } + return status; +} + +static apt_bool_t mrcp_unirtsp_message_handle(rtsp_server_t *rtsp_server, rtsp_server_session_t *rtsp_session, rtsp_message_t *rtsp_message) +{ + apt_bool_t status = FALSE; + mrcp_unirtsp_agent_t *agent = rtsp_server_object_get(rtsp_server); + mrcp_unirtsp_session_t *session = rtsp_server_session_object_get(rtsp_session); + if(!session) { + return FALSE; + } + + switch(rtsp_message->start_line.common.request_line.method_id) { + case RTSP_METHOD_SETUP: + case RTSP_METHOD_TEARDOWN: + { + const char *force_destination_ip = NULL; + mrcp_session_descriptor_t *descriptor; + + if(agent->config->force_destination == TRUE) { + force_destination_ip = rtsp_server_session_destination_get(rtsp_session); + } + descriptor = mrcp_descriptor_generate_by_rtsp_request( + rtsp_message, + force_destination_ip, + agent->config->resource_map, + session->mrcp_session->pool, + session->home); + if(!descriptor) { + rtsp_message_t *response = rtsp_response_create(rtsp_message, + RTSP_STATUS_CODE_BAD_REQUEST, + RTSP_REASON_PHRASE_BAD_REQUEST, + rtsp_message->pool); + status = rtsp_server_session_respond(rtsp_server,session->rtsp_session,response); + break; + } + status = mrcp_session_offer(session->mrcp_session,descriptor); + break; + } + case RTSP_METHOD_ANNOUNCE: + { + status = mrcp_unirtsp_session_announce(agent,session,rtsp_message); + break; + } + case RTSP_METHOD_DESCRIBE: + { + rtsp_message_t *response = rtsp_resource_discovery_response_generate( + rtsp_message, + agent->config->local_ip, + agent->config->origin, + session->mrcp_session->pool); + status = rtsp_server_session_respond(rtsp_server,session->rtsp_session,response); + break; + } + default: + break; + } + + return status; +} + +static apt_bool_t mrcp_unirtsp_on_session_answer(mrcp_session_t *mrcp_session, mrcp_session_descriptor_t *descriptor) +{ + mrcp_unirtsp_session_t *session = mrcp_session->obj; + mrcp_unirtsp_agent_t *agent = mrcp_session->signaling_agent->obj; + rtsp_message_t *response = NULL; + const rtsp_message_t *request = rtsp_server_session_request_get(session->rtsp_session); + if(!request) { + return FALSE; + } + + if(request->start_line.common.request_line.method_id == RTSP_METHOD_SETUP) { + if(agent->config->origin) { + apt_string_set(&descriptor->origin,agent->config->origin); + } + + response = rtsp_response_generate_by_mrcp_descriptor( + request, + descriptor, + agent->config->resource_map, + mrcp_session->pool); + } + else if(request->start_line.common.request_line.method_id == RTSP_METHOD_TEARDOWN) { + response = rtsp_response_create(request,RTSP_STATUS_CODE_OK,RTSP_REASON_PHRASE_OK,mrcp_session->pool); + } + + if(!response) { + return FALSE; + } + rtsp_server_session_respond(agent->rtsp_server,session->rtsp_session,response); + return TRUE; +} + +static apt_bool_t mrcp_unirtsp_on_session_terminate(mrcp_session_t *mrcp_session) +{ + mrcp_unirtsp_session_t *session = mrcp_session->obj; + rtsp_server_session_t *rtsp_session = session->rtsp_session; + mrcp_unirtsp_agent_t *agent = mrcp_session->signaling_agent->obj; + + mrcp_unirtsp_session_destroy(session); + rtsp_server_session_terminate(agent->rtsp_server,rtsp_session); + return TRUE; +} + +static apt_bool_t mrcp_unirtsp_on_session_control(mrcp_session_t *mrcp_session, mrcp_message_t *mrcp_message) +{ + mrcp_unirtsp_session_t *session = mrcp_session->obj; + mrcp_unirtsp_agent_t *agent = mrcp_session->signaling_agent->obj; + + char buffer[500]; + apt_text_stream_t stream; + rtsp_message_t *rtsp_message = NULL; + apt_str_t *body; + + apt_text_stream_init(&stream,buffer,sizeof(buffer)); + + mrcp_message->start_line.version = MRCP_VERSION_1; + if(mrcp_message_generate(agent->sig_agent->resource_factory,mrcp_message,&stream) != TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Generate MRCPv1 Message"); + return FALSE; + } + stream.text.length = stream.pos - stream.text.buf; + + if(mrcp_message->start_line.message_type == MRCP_MESSAGE_TYPE_RESPONSE) { + /* send RTSP response (OK) */ + const rtsp_message_t *request = rtsp_server_session_request_get(session->rtsp_session); + if(request) { + rtsp_message = rtsp_response_create(request,RTSP_STATUS_CODE_OK,RTSP_REASON_PHRASE_OK,mrcp_session->pool); + } + } + else if(mrcp_message->start_line.message_type == MRCP_MESSAGE_TYPE_EVENT) { + /* send RTSP announce */ + rtsp_message = rtsp_request_create(mrcp_session->pool); + rtsp_message->start_line.common.request_line.method_id = RTSP_METHOD_ANNOUNCE; + } + + if(!rtsp_message) { + return FALSE; + } + + body = &rtsp_message->body; + body->length = mrcp_message->start_line.length; + body->buf = apr_palloc(rtsp_message->pool,body->length+1); + memcpy(body->buf,stream.text.buf,stream.text.length); + if(mrcp_message->body.length) { + memcpy(body->buf+stream.text.length,mrcp_message->body.buf,mrcp_message->body.length); + } + body->buf[body->length] = '\0'; + + rtsp_message->header.content_type = RTSP_CONTENT_TYPE_MRCP; + rtsp_header_property_add(&rtsp_message->header.property_set,RTSP_HEADER_FIELD_CONTENT_TYPE); + rtsp_message->header.content_length = body->length; + rtsp_header_property_add(&rtsp_message->header.property_set,RTSP_HEADER_FIELD_CONTENT_LENGTH); + + rtsp_server_session_respond(agent->rtsp_server,session->rtsp_session,rtsp_message); + return TRUE; +} diff --git a/libs/unimrcp/packages/inno-setup/setup.iss b/libs/unimrcp/packages/inno-setup/setup.iss new file mode 100644 index 0000000000..a14c5001d3 --- /dev/null +++ b/libs/unimrcp/packages/inno-setup/setup.iss @@ -0,0 +1,14 @@ +#define uni_version "0.6.0" + +AppName=UniMRCP +AppVerName=UniMRCP-{#= uni_version} +AppPublisher=UniMRCP +AppPublisherURL=http://www.unimrcp.org/ +AppSupportURL=http://groups.google.com/group/unimrcp +AppUpdatesURL=http://code.google.com/p/unimrcp/downloads/list +DefaultDirName={pf}\UniMRCP +DefaultGroupName=UniMRCP +Compression=lzma +InternalCompressLevel=max +SolidCompression=true + diff --git a/libs/unimrcp/packages/inno-setup/unimrcp-sdk.iss b/libs/unimrcp/packages/inno-setup/unimrcp-sdk.iss new file mode 100644 index 0000000000..cf876f736a --- /dev/null +++ b/libs/unimrcp/packages/inno-setup/unimrcp-sdk.iss @@ -0,0 +1,56 @@ +[Setup] +#include "setup.iss" +OutputBaseFilename=unimrcp-sdk-{#= uni_version} + +[Types] +Name: "full"; Description: "Full installation" +Name: "sdk"; Description: "SDK installation" +Name: "docs"; Description: "Documentation installation" +Name: "custom"; Description: "Custom installation"; Flags: iscustom + +[Components] +Name: "sdk"; Description: "UniMRCP SDK (client, server and plugin development)"; Types: full sdk +Name: "docs"; Description: "UniMRCP documentation"; Types: full docs +Name: "docs\design"; Description: "Design concepts"; Types: full docs +Name: "docs\api"; Description: "API"; Types: full docs + +[Files] +Source: "..\..\libs\apr\include\*.h"; DestDir: "{app}\include"; Components: sdk +Source: "..\..\libs\apr-toolkit\include\*.h"; DestDir: "{app}\include"; Components: sdk +Source: "..\..\libs\mpf\include\*.h"; DestDir: "{app}\include"; Components: sdk +Source: "..\..\libs\mrcp\include\*.h"; DestDir: "{app}\include"; Components: sdk +Source: "..\..\libs\mrcp\message\include\*.h"; DestDir: "{app}\include"; Components: sdk +Source: "..\..\libs\mrcp\control\include\*.h"; DestDir: "{app}\include"; Components: sdk +Source: "..\..\libs\mrcp\resources\include\*.h"; DestDir: "{app}\include"; Components: sdk +Source: "..\..\libs\mrcp-engine\include\*.h"; DestDir: "{app}\include"; Components: sdk +Source: "..\..\libs\mrcp-signaling\include\*.h"; DestDir: "{app}\include"; Components: sdk +Source: "..\..\libs\mrcpv2-transport\include\*.h"; DestDir: "{app}\include"; Components: sdk +Source: "..\..\libs\mrcp-client\include\*.h"; DestDir: "{app}\include"; Components: sdk +Source: "..\..\libs\mrcp-server\include\*.h"; DestDir: "{app}\include"; Components: sdk +Source: "..\..\platforms\libunimrcp-client\include\*.h"; DestDir: "{app}\include"; Components: sdk +Source: "..\..\platforms\libunimrcp-server\include\*.h"; DestDir: "{app}\include"; Components: sdk +Source: "..\..\Release\bin\*.lib"; DestDir: "{app}\lib"; Components: sdk +Source: "..\..\libs\apr\Release\*.lib"; DestDir: "{app}\lib"; Components: sdk +Source: "..\..\libs\apr-util\Release\*.lib"; DestDir: "{app}\lib"; Components: sdk +Source: "..\..\libs\sofia-sip\win32\libsofia-sip-ua\Release\*.lib"; DestDir: "{app}\lib"; Components: sdk +Source: "..\..\build\vsprops\sdk\*.vsprops"; DestDir: "{app}\vsprops"; Components: sdk; AfterInstall: SetProjectPath() +Source: "..\..\docs\ea\*"; DestDir: "{app}\doc\ea"; Components: docs/design; Flags: recursesubdirs +Source: "..\..\docs\dox\*"; DestDir: "{app}\doc\dox"; Components: docs/api; Flags: recursesubdirs + +[Icons] +Name: "{group}\UniMRCP Docs\Design concepts"; Filename: "{app}\doc\ea\index.htm"; Components: docs\design +Name: "{group}\UniMRCP Docs\API"; Filename: "{app}\doc\dox\html\index.html"; Components: docs\api +Name: "{group}\Uninstall"; Filename: "{uninstallexe}" + +[Code] +procedure SetProjectPath(); +var + VspropsFile: String; + Content: String; +begin + VspropsFile := ExpandConstant('{app}\vsprops\unimrcpsdk.vsprops'); + LoadStringFromFile (VspropsFile, Content); + StringChange (Content, 'Value="C:\Program Files\UniMRCP"', ExpandConstant('Value="{app}"')); + SaveStringToFile (VspropsFile, Content, False); +end; + diff --git a/libs/unimrcp/packages/inno-setup/unimrcp.iss b/libs/unimrcp/packages/inno-setup/unimrcp.iss new file mode 100644 index 0000000000..b582652ab9 --- /dev/null +++ b/libs/unimrcp/packages/inno-setup/unimrcp.iss @@ -0,0 +1,85 @@ +[Setup] +#include "setup.iss" +OutputBaseFilename=unimrcp-{#= uni_version} + +[Types] +Name: "full"; Description: "Full installation" +Name: "server"; Description: "Server installation" +Name: "client"; Description: "Client installation" +Name: "custom"; Description: "Custom installation"; Flags: iscustom + +[Components] +Name: "server"; Description: "UniMRCP server"; Types: full server +Name: "server\cepstral"; Description: "Cepstral synthesizer plugin"; Types: full server +Name: "server\demosynth"; Description: "Demo synthesizer plugin"; Types: full server +Name: "server\demorecog"; Description: "Demo recognizer plugin"; Types: full server +Name: "client"; Description: "UniMRCP client (demo application)"; Types: full client + +[Dirs] +Name: "{app}\data"; Permissions: everyone-full; +Name: "{app}\log"; Permissions: everyone-full; + +[Files] +#define uni_root "..\..\Release" +Source: "{#= uni_root}\bin\unimrcpserver.exe"; DestDir: "{app}\bin"; Components: server +Source: "{#= uni_root}\bin\unimrcpservice.exe"; DestDir: "{app}\bin"; Components: server +Source: "{#= uni_root}\bin\unimrcpclient.exe"; DestDir: "{app}\bin"; Components: client +Source: "{#= uni_root}\bin\*.dll"; DestDir: "{app}\bin"; Components: server client +Source: "{#= uni_root}\plugin\mrcpcepstral.dll"; DestDir: "{app}\plugin"; Components: server/cepstral +Source: "{#= uni_root}\plugin\demosynth.dll"; DestDir: "{app}\plugin"; Components: server/demosynth +Source: "{#= uni_root}\plugin\demorecog.dll"; DestDir: "{app}\plugin"; Components: server/demorecog +Source: "{#= uni_root}\conf\unimrcpserver.xml"; DestDir: "{app}\conf"; Components: server +Source: "{#= uni_root}\conf\unimrcpclient.xml"; DestDir: "{app}\conf"; Components: client +Source: "{#= uni_root}\data\*.pcm"; DestDir: "{app}\data"; Components: server client +Source: "{#= uni_root}\data\*.xml"; DestDir: "{app}\data"; Components: server client + +[Icons] +Name: "{group}\UniMRCP Server Console"; Filename: "{app}\bin\unimrcpserver.exe"; Parameters: "--root-dir ""{app}"""; Components: server +Name: "{group}\UniMRCP Client Console"; Filename: "{app}\bin\unimrcpclient.exe"; Parameters: "--root-dir ""{app}"""; Components: client +Name: "{group}\UniMRCP Service\Start Server"; Filename: "{app}\bin\unimrcpservice.exe"; Parameters: "--start"; Components: server +Name: "{group}\UniMRCP Service\Stop Server"; Filename: "{app}\bin\unimrcpservice.exe"; Parameters: "--stop"; Components: server +Name: "{group}\Uninstall"; Filename: "{uninstallexe}" + +[Run] +Filename: "{app}\bin\unimrcpservice.exe"; Description: "Register service"; Parameters: "--register ""{app}"""; Components: server + +[UninstallRun] +Filename: "{app}\bin\unimrcpservice.exe"; Parameters: "--unregister"; Components: server + +[Code] +var + Content: String; + +procedure ModifyPluginConf(PluginName: String; Enable: Boolean); +var + TextFrom: String; + TextTo: String; +begin + if Enable = True then + begin + TextFrom := 'class="' + PluginName + '" enable="0"'; + TextTo := 'class="' + PluginName + '" enable="1"'; + end + else + begin + TextFrom := 'class="' + PluginName + '" enable="1"'; + TextTo := 'class="' + PluginName + '" enable="0"'; + end + StringChange (Content, TextFrom, TextTo); +end; + +procedure CurStepChanged(CurStep: TSetupStep); +var + CfgFile: String; +begin + if CurStep = ssPostInstall then + begin + CfgFile := ExpandConstant('{app}\conf\unimrcpserver.xml'); + LoadStringFromFile (CfgFile, Content); + ModifyPluginConf ('mrcpcepstral', IsComponentSelected('server\cepstral')); + ModifyPluginConf ('demosynth', IsComponentSelected('server\demosynth')); + ModifyPluginConf ('demorecog', IsComponentSelected('server\demorecog')); + SaveStringToFile (CfgFile, Content, False); + end +end; + diff --git a/libs/unimrcp/platforms/Makefile.am b/libs/unimrcp/platforms/Makefile.am new file mode 100644 index 0000000000..d6ce93d131 --- /dev/null +++ b/libs/unimrcp/platforms/Makefile.am @@ -0,0 +1,4 @@ +MAINTAINERCLEANFILES = Makefile.in + +SUBDIRS = libunimrcp-server unimrcp-server \ + libunimrcp-client unimrcp-client diff --git a/libs/unimrcp/platforms/libunimrcp-client/Makefile.am b/libs/unimrcp/platforms/libunimrcp-client/Makefile.am new file mode 100644 index 0000000000..a21f667de3 --- /dev/null +++ b/libs/unimrcp/platforms/libunimrcp-client/Makefile.am @@ -0,0 +1,34 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_srcdir)/platforms/libunimrcp-client/include \ + -I$(top_srcdir)/modules/mrcp-sofiasip/include \ + -I$(top_srcdir)/modules/mrcp-unirtsp/include \ + -I$(top_srcdir)/libs/mrcp-client/include \ + -I$(top_srcdir)/libs/mrcp-signaling/include \ + -I$(top_srcdir)/libs/mrcpv2-transport/include \ + -I$(top_srcdir)/libs/mrcp/include \ + -I$(top_srcdir)/libs/mrcp/message/include \ + -I$(top_srcdir)/libs/mrcp/control/include \ + -I$(top_srcdir)/libs/mrcp/resources/include \ + -I$(top_srcdir)/libs/mpf/include \ + -I$(top_srcdir)/libs/apr-toolkit/include \ + -I$(top_srcdir)/build \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) + +lib_LTLIBRARIES = libunimrcpclient.la + +include_HEADERS = include/unimrcp_client.h + +libunimrcpclient_la_SOURCES = src/unimrcp_client.c + +libunimrcpclient_la_LIBADD = $(top_builddir)/modules/mrcp-sofiasip/libmrcpsofiasip.la \ + $(top_builddir)/modules/mrcp-unirtsp/libmrcpunirtsp.la \ + $(top_builddir)/libs/mrcpv2-transport/libmrcpv2transport.la \ + $(top_builddir)/libs/mrcp-client/libmrcpclient.la \ + $(top_builddir)/libs/mrcp-signaling/libmrcpsignaling.la \ + $(top_builddir)/libs/mrcp/libmrcp.la \ + $(top_builddir)/libs/mpf/libmpf.la \ + $(top_builddir)/libs/apr-toolkit/libaprtoolkit.la \ + $(UNIMRCP_APR_LIBS) $(UNIMRCP_APU_LIBS) $(UNIMRCP_SOFIA_LIBS) + +libunimrcpclient_la_LDFLAGS = $(UNI_LT_VERSION) diff --git a/libs/unimrcp/platforms/libunimrcp-client/include/unimrcp_client.h b/libs/unimrcp/platforms/libunimrcp-client/include/unimrcp_client.h new file mode 100644 index 0000000000..152f856374 --- /dev/null +++ b/libs/unimrcp/platforms/libunimrcp-client/include/unimrcp_client.h @@ -0,0 +1,38 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __UNIMRCP_CLIENT_H__ +#define __UNIMRCP_CLIENT_H__ + +/** + * @file unimrcp_client.h + * @brief UniMRCP Client + */ + +#include "mrcp_client.h" + +APT_BEGIN_EXTERN_C + +/** + * Create UniMRCP client. + * @param dir_layout the dir layout structure + */ +MRCP_DECLARE(mrcp_client_t*) unimrcp_client_create(apt_dir_layout_t *dir_layout); + + +APT_END_EXTERN_C + +#endif /*__UNIMRCP_CLIENT_H__*/ diff --git a/libs/unimrcp/platforms/libunimrcp-client/libunimrcpclient.vcproj b/libs/unimrcp/platforms/libunimrcp-client/libunimrcpclient.vcproj new file mode 100644 index 0000000000..d0c7e7aafe --- /dev/null +++ b/libs/unimrcp/platforms/libunimrcp-client/libunimrcpclient.vcproj @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/platforms/libunimrcp-client/src/unimrcp_client.c b/libs/unimrcp/platforms/libunimrcp-client/src/unimrcp_client.c new file mode 100644 index 0000000000..5844f5c55c --- /dev/null +++ b/libs/unimrcp/platforms/libunimrcp-client/src/unimrcp_client.c @@ -0,0 +1,596 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include "unimrcp_client.h" +#include "uni_version.h" +#include "mrcp_default_factory.h" +#include "mpf_engine.h" +#include "mpf_codec_manager.h" +#include "mpf_rtp_termination_factory.h" +#include "mrcp_sofiasip_client_agent.h" +#include "mrcp_unirtsp_client_agent.h" +#include "mrcp_client_connection.h" +#include "apt_net.h" +#include "apt_log.h" + +#define CONF_FILE_NAME "unimrcpclient.xml" +#define DEFAULT_CONF_DIR_PATH "../conf" + +#define DEFAULT_LOCAL_IP_ADDRESS "127.0.0.1" +#define DEFAULT_REMOTE_IP_ADDRESS "127.0.0.1" +#define DEFAULT_SIP_LOCAL_PORT 8062 +#define DEFAULT_SIP_REMOTE_PORT 8060 +#define DEFAULT_RTP_PORT_MIN 4000 +#define DEFAULT_RTP_PORT_MAX 5000 + +#define DEFAULT_SOFIASIP_UA_NAME "UniMRCP SofiaSIP" +#define DEFAULT_SDP_ORIGIN "UniMRCPClient" +#define DEFAULT_RESOURCE_LOCATION "media" + +#define XML_FILE_BUFFER_LENGTH 2000 + +static apr_xml_doc* unimrcp_client_config_parse(const char *path, apr_pool_t *pool); +static apt_bool_t unimrcp_client_config_load(mrcp_client_t *client, const apr_xml_doc *doc, apr_pool_t *pool); + +/** Start UniMRCP client */ +MRCP_DECLARE(mrcp_client_t*) unimrcp_client_create(apt_dir_layout_t *dir_layout) +{ + apr_pool_t *pool; + apr_xml_doc *doc; + mrcp_resource_factory_t *resource_factory; + mpf_codec_manager_t *codec_manager; + mrcp_client_t *client; + + if(!dir_layout) { + return NULL; + } + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"UniMRCP Client ["UNI_VERSION_STRING"]"); + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"APR ["APR_VERSION_STRING"]"); + client = mrcp_client_create(dir_layout); + if(!client) { + return NULL; + } + pool = mrcp_client_memory_pool_get(client); + if(!pool) { + return NULL; + } + + resource_factory = mrcp_default_factory_create(pool); + if(resource_factory) { + mrcp_client_resource_factory_register(client,resource_factory); + } + + codec_manager = mpf_engine_codec_manager_create(pool); + if(codec_manager) { + mrcp_client_codec_manager_register(client,codec_manager); + } + + doc = unimrcp_client_config_parse(dir_layout->conf_dir_path,pool); + if(doc) { + unimrcp_client_config_load(client,doc,pool); + } + + return client; +} + +/** Parse config file */ +static apr_xml_doc* unimrcp_client_config_parse(const char *dir_path, apr_pool_t *pool) +{ + apr_xml_parser *parser = NULL; + apr_xml_doc *doc = NULL; + apr_file_t *fd = NULL; + apr_status_t rv; + const char *file_path; + + if(!dir_path) { + dir_path = DEFAULT_CONF_DIR_PATH; + } + if(*dir_path == '\0') { + file_path = CONF_FILE_NAME; + } + else { + file_path = apr_psprintf(pool,"%s/%s",dir_path,CONF_FILE_NAME); + } + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Open Config File [%s]",file_path); + rv = apr_file_open(&fd,file_path,APR_READ|APR_BINARY,0,pool); + if(rv != APR_SUCCESS) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Open Config File [%s]",file_path); + return NULL; + } + + rv = apr_xml_parse_file(pool,&parser,&doc,fd,XML_FILE_BUFFER_LENGTH); + if(rv != APR_SUCCESS) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Parse Config File [%s]",file_path); + return NULL; + } + + apr_file_close(fd); + return doc; +} + +static apt_bool_t param_name_value_get(const apr_xml_elem *elem, const apr_xml_attr **name, const apr_xml_attr **value) +{ + const apr_xml_attr *attr; + if(!name || !value) { + return FALSE; + } + + *name = NULL; + *value = NULL; + for(attr = elem->attr; attr; attr = attr->next) { + if(strcasecmp(attr->name,"name") == 0) { + *name = attr; + } + else if(strcasecmp(attr->name,"value") == 0) { + *value = attr; + } + } + return (*name && *value) ? TRUE : FALSE; +} + +static char* ip_addr_get(const char *value, apr_pool_t *pool) +{ + if(!value || strcasecmp(value,"auto") == 0) { + char *addr = DEFAULT_LOCAL_IP_ADDRESS; + apt_ip_get(&addr,pool); + return addr; + } + return apr_pstrdup(pool,value); +} + +/** Load map of MRCP resource names */ +static apt_bool_t resource_map_load(apr_table_t *resource_map, const apr_xml_elem *root, apr_pool_t *pool) +{ + const apr_xml_attr *attr_name; + const apr_xml_attr *attr_value; + const apr_xml_elem *elem; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Resource Map"); + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"param") == 0) { + if(param_name_value_get(elem,&attr_name,&attr_value) == TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Param %s:%s",attr_name->value,attr_value->value); + apr_table_set(resource_map,attr_name->value,attr_value->value); + } + } + } + return TRUE; +} + +/** Load SofiaSIP signaling agent */ +static mrcp_sig_agent_t* unimrcp_client_sofiasip_agent_load(mrcp_client_t *client, const apr_xml_elem *root, apr_pool_t *pool) +{ + const apr_xml_elem *elem; + mrcp_sofia_client_config_t *config = mrcp_sofiasip_client_config_alloc(pool); + config->local_ip = DEFAULT_LOCAL_IP_ADDRESS; + config->local_port = DEFAULT_SIP_LOCAL_PORT; + config->remote_ip = DEFAULT_REMOTE_IP_ADDRESS; + config->remote_port = DEFAULT_SIP_REMOTE_PORT; + config->ext_ip = NULL; + config->user_agent_name = DEFAULT_SOFIASIP_UA_NAME; + config->origin = DEFAULT_SDP_ORIGIN; + + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading SofiaSIP Agent"); + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"param") == 0) { + const apr_xml_attr *attr_name; + const apr_xml_attr *attr_value; + if(param_name_value_get(elem,&attr_name,&attr_value) == TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Param %s:%s",attr_name->value,attr_value->value); + if(strcasecmp(attr_name->value,"client-ip") == 0) { + config->local_ip = ip_addr_get(attr_value->value,pool); + } + else if(strcasecmp(attr_name->value,"client-ext-ip") == 0) { + config->ext_ip = ip_addr_get(attr_value->value,pool); + } + else if(strcasecmp(attr_name->value,"client-port") == 0) { + config->local_port = (apr_port_t)atol(attr_value->value); + } + else if(strcasecmp(attr_name->value,"server-ip") == 0) { + config->remote_ip = ip_addr_get(attr_value->value,pool); + } + else if(strcasecmp(attr_name->value,"server-port") == 0) { + config->remote_port = (apr_port_t)atol(attr_value->value); + } + else if(strcasecmp(attr_name->value,"server-username") == 0) { + config->remote_user_name = apr_pstrdup(pool,attr_value->value); + } + else if(strcasecmp(attr_name->value,"force-destination") == 0) { + config->force_destination = atoi(attr_value->value); + } + else if(strcasecmp(attr_name->value,"sip-transport") == 0) { + config->transport = apr_pstrdup(pool,attr_value->value); + } + else if(strcasecmp(attr_name->value,"ua-name") == 0) { + config->user_agent_name = apr_pstrdup(pool,attr_value->value); + } + else if(strcasecmp(attr_name->value,"sdp-origin") == 0) { + config->origin = apr_pstrdup(pool,attr_value->value); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr_name->value); + } + } + } + } + return mrcp_sofiasip_client_agent_create(config,pool); +} + +/** Load UniRTSP signaling agent */ +static mrcp_sig_agent_t* unimrcp_client_rtsp_agent_load(mrcp_client_t *client, const apr_xml_elem *root, apr_pool_t *pool) +{ + const apr_xml_elem *elem; + rtsp_client_config_t *config = mrcp_unirtsp_client_config_alloc(pool); + config->origin = DEFAULT_SDP_ORIGIN; + config->resource_location = DEFAULT_RESOURCE_LOCATION; + + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading UniRTSP Agent"); + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"param") == 0) { + const apr_xml_attr *attr_name; + const apr_xml_attr *attr_value; + if(param_name_value_get(elem,&attr_name,&attr_value) == TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Param %s:%s",attr_name->value,attr_value->value); + if(strcasecmp(attr_name->value,"server-ip") == 0) { + config->server_ip = ip_addr_get(attr_value->value,pool); + } + else if(strcasecmp(attr_name->value,"server-port") == 0) { + config->server_port = (apr_port_t)atol(attr_value->value); + } + else if(strcasecmp(attr_name->value,"resource-location") == 0) { + config->resource_location = apr_pstrdup(pool,attr_value->value); + } + else if(strcasecmp(attr_name->value,"sdp-origin") == 0) { + config->origin = apr_pstrdup(pool,attr_value->value); + } + else if(strcasecmp(attr_name->value,"max-connection-count") == 0) { + config->max_connection_count = atol(attr_value->value); + } + else if(strcasecmp(attr_name->value,"force-destination") == 0) { + config->force_destination = atoi(attr_value->value); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr_name->value); + } + } + } + else if(strcasecmp(elem->name,"resourcemap") == 0) { + resource_map_load(config->resource_map,elem,pool); + } + } + return mrcp_unirtsp_client_agent_create(config,pool); +} + +/** Load signaling agents */ +static apt_bool_t unimrcp_client_signaling_agents_load(mrcp_client_t *client, const apr_xml_elem *root, apr_pool_t *pool) +{ + const apr_xml_elem *elem; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Signaling Agents"); + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"agent") == 0) { + mrcp_sig_agent_t *sig_agent = NULL; + const char *name = NULL; + const apr_xml_attr *attr; + for(attr = elem->attr; attr; attr = attr->next) { + if(strcasecmp(attr->name,"name") == 0) { + name = apr_pstrdup(pool,attr->value); + } + else if(strcasecmp(attr->name,"class") == 0) { + if(strcasecmp(attr->value,"SofiaSIP") == 0) { + sig_agent = unimrcp_client_sofiasip_agent_load(client,elem,pool); + } + else if(strcasecmp(attr->value,"UniRTSP") == 0) { + sig_agent = unimrcp_client_rtsp_agent_load(client,elem,pool); + } + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr->name); + } + } + if(sig_agent) { + mrcp_client_signaling_agent_register(client,sig_agent,name); + } + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Element <%s>",elem->name); + } + } + return TRUE; +} + +/** Load MRCPv2 connection agent */ +static mrcp_connection_agent_t* unimrcp_client_connection_agent_load(mrcp_client_t *client, const apr_xml_elem *root, apr_pool_t *pool) +{ + const apr_xml_elem *elem; + apr_size_t max_connection_count = 100; + apt_bool_t offer_new_connection = FALSE; + + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading MRCPv2 Agent"); + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"param") == 0) { + const apr_xml_attr *attr_name; + const apr_xml_attr *attr_value; + if(param_name_value_get(elem,&attr_name,&attr_value) == TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Param %s:%s",attr_name->value,attr_value->value); + if(strcasecmp(attr_name->value,"max-connection-count") == 0) { + max_connection_count = atol(attr_value->value); + } + else if(strcasecmp(attr_name->value,"offer-new-connection") == 0) { + offer_new_connection = atoi(attr_value->value); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr_name->value); + } + } + } + } + return mrcp_client_connection_agent_create(max_connection_count,offer_new_connection,pool); +} + +/** Load MRCPv2 conection agents */ +static apt_bool_t unimrcp_client_connection_agents_load(mrcp_client_t *client, const apr_xml_elem *root, apr_pool_t *pool) +{ + const apr_xml_elem *elem; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Connection Agents"); + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"agent") == 0) { + mrcp_connection_agent_t *connection_agent; + const char *name = NULL; + const apr_xml_attr *attr; + for(attr = elem->attr; attr; attr = attr->next) { + if(strcasecmp(attr->name,"name") == 0) { + name = apr_pstrdup(pool,attr->value); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr->name); + } + } + connection_agent = unimrcp_client_connection_agent_load(client,elem,pool); + if(connection_agent) { + mrcp_client_connection_agent_register(client,connection_agent,name); + } + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Element <%s>",elem->name); + } + } + return TRUE; +} + +/** Load RTP termination factory */ +static mpf_termination_factory_t* unimrcp_client_rtp_factory_load(mrcp_client_t *client, const apr_xml_elem *root, apr_pool_t *pool) +{ + const apr_xml_elem *elem; + char *rtp_ip = DEFAULT_LOCAL_IP_ADDRESS; + char *rtp_ext_ip = NULL; + mpf_rtp_config_t *rtp_config = mpf_rtp_config_create(pool); + rtp_config->rtp_port_min = DEFAULT_RTP_PORT_MIN; + rtp_config->rtp_port_max = DEFAULT_RTP_PORT_MAX; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading RTP Termination Factory"); + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"param") == 0) { + const apr_xml_attr *attr_name; + const apr_xml_attr *attr_value; + if(param_name_value_get(elem,&attr_name,&attr_value) == TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Param %s:%s",attr_name->value,attr_value->value); + if(strcasecmp(attr_name->value,"rtp-ip") == 0) { + rtp_ip = ip_addr_get(attr_value->value,pool); + } + else if(strcasecmp(attr_name->value,"rtp-ext-ip") == 0) { + rtp_ext_ip = ip_addr_get(attr_value->value,pool); + } + else if(strcasecmp(attr_name->value,"rtp-port-min") == 0) { + rtp_config->rtp_port_min = (apr_port_t)atol(attr_value->value); + } + else if(strcasecmp(attr_name->value,"rtp-port-max") == 0) { + rtp_config->rtp_port_max = (apr_port_t)atol(attr_value->value); + } + else if(strcasecmp(attr_name->value,"playout-delay") == 0) { + rtp_config->jb_config.initial_playout_delay = atol(attr_value->value); + } + else if(strcasecmp(attr_name->value,"min-playout-delay") == 0) { + rtp_config->jb_config.min_playout_delay = atol(attr_value->value); + } + else if(strcasecmp(attr_name->value,"max-playout-delay") == 0) { + rtp_config->jb_config.max_playout_delay = atol(attr_value->value); + } + else if(strcasecmp(attr_name->value,"codecs") == 0) { + const mpf_codec_manager_t *codec_manager = mrcp_client_codec_manager_get(client); + if(codec_manager) { + mpf_codec_manager_codec_list_load(codec_manager,&rtp_config->codec_list,attr_value->value,pool); + } + } + else if(strcasecmp(attr_name->value,"ptime") == 0) { + rtp_config->ptime = (apr_uint16_t)atol(attr_value->value); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr_name->value); + } + } + } + } + apt_string_set(&rtp_config->ip,rtp_ip); + if(rtp_ext_ip) { + apt_string_set(&rtp_config->ext_ip,rtp_ext_ip); + } + return mpf_rtp_termination_factory_create(rtp_config,pool); +} + +/** Load media engines */ +static apt_bool_t unimrcp_client_media_engines_load(mrcp_client_t *client, const apr_xml_elem *root, apr_pool_t *pool) +{ + const apr_xml_elem *elem; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Media Engines"); + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"engine") == 0) { + mpf_engine_t *media_engine; + const char *name = NULL; + const apr_xml_attr *attr; + for(attr = elem->attr; attr; attr = attr->next) { + if(strcasecmp(attr->name,"name") == 0) { + name = apr_pstrdup(pool,attr->value); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr->name); + } + } + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Media Engine"); + media_engine = mpf_engine_create(pool); + if(media_engine) { + mrcp_client_media_engine_register(client,media_engine,name); + } + } + else if(strcasecmp(elem->name,"rtp") == 0) { + mpf_termination_factory_t *rtp_factory; + const char *name = NULL; + const apr_xml_attr *attr; + for(attr = elem->attr; attr; attr = attr->next) { + if(strcasecmp(attr->name,"name") == 0) { + name = apr_pstrdup(pool,attr->value); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr->name); + } + } + rtp_factory = unimrcp_client_rtp_factory_load(client,elem,pool); + if(rtp_factory) { + mrcp_client_rtp_factory_register(client,rtp_factory,name); + } + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Element <%s>",elem->name); + } + } + return TRUE; +} + +/** Load settings */ +static apt_bool_t unimrcp_client_settings_load(mrcp_client_t *client, const apr_xml_elem *root, apr_pool_t *pool) +{ + const apr_xml_elem *elem; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Settings"); + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"signaling") == 0) { + unimrcp_client_signaling_agents_load(client,elem,pool); + } + else if(strcasecmp(elem->name,"connection") == 0) { + unimrcp_client_connection_agents_load(client,elem,pool); + } + else if(strcasecmp(elem->name,"media") == 0) { + unimrcp_client_media_engines_load(client,elem,pool); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Element <%s>",elem->name); + } + } + return TRUE; +} + +/** Load profile */ +static apt_bool_t unimrcp_client_profile_load(mrcp_client_t *client, const apr_xml_elem *root, apr_pool_t *pool) +{ + const char *name = NULL; + mrcp_profile_t *profile; + mrcp_sig_agent_t *sig_agent = NULL; + mrcp_connection_agent_t *cnt_agent = NULL; + mpf_engine_t *media_engine = NULL; + mpf_termination_factory_t *rtp_factory = NULL; + const apr_xml_elem *elem; + const apr_xml_attr *attr; + for(attr = root->attr; attr; attr = attr->next) { + if(strcasecmp(attr->name,"name") == 0) { + name = apr_pstrdup(pool,attr->value); + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Profile [%s]",name); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr->name); + } + } + if(!name) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Load Profile: no profile name specified"); + return FALSE; + } + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"param") == 0) { + const apr_xml_attr *attr_name; + const apr_xml_attr *attr_value; + if(param_name_value_get(elem,&attr_name,&attr_value) == TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Loading Profile %s [%s]",attr_name->value,attr_value->value); + if(strcasecmp(attr_name->value,"signaling-agent") == 0) { + sig_agent = mrcp_client_signaling_agent_get(client,attr_value->value); + } + else if(strcasecmp(attr_name->value,"connection-agent") == 0) { + cnt_agent = mrcp_client_connection_agent_get(client,attr_value->value); + } + else if(strcasecmp(attr_name->value,"media-engine") == 0) { + media_engine = mrcp_client_media_engine_get(client,attr_value->value); + } + else if(strcasecmp(attr_name->value,"rtp-factory") == 0) { + rtp_factory = mrcp_client_rtp_factory_get(client,attr_value->value); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr_name->value); + } + } + } + } + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create Profile [%s]",name); + profile = mrcp_client_profile_create(NULL,sig_agent,cnt_agent,media_engine,rtp_factory,pool); + return mrcp_client_profile_register(client,profile,name); +} + +/** Load profiles */ +static apt_bool_t unimrcp_client_profiles_load(mrcp_client_t *client, const apr_xml_elem *root, apr_pool_t *pool) +{ + const apr_xml_elem *elem; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Profiles"); + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"profile") == 0) { + unimrcp_client_profile_load(client,elem,pool); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Element <%s>",elem->name); + } + } + return TRUE; +} + +/** Load configuration (settings and profiles) */ +static apt_bool_t unimrcp_client_config_load(mrcp_client_t *client, const apr_xml_doc *doc, apr_pool_t *pool) +{ + const apr_xml_elem *elem; + const apr_xml_elem *root = doc->root; + if(!root || strcasecmp(root->name,"unimrcpclient") != 0) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Document"); + return FALSE; + } + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"settings") == 0) { + unimrcp_client_settings_load(client,elem,pool); + } + else if(strcasecmp(elem->name,"profiles") == 0) { + unimrcp_client_profiles_load(client,elem,pool); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Element <%s>",elem->name); + } + } + + return TRUE; +} diff --git a/libs/unimrcp/platforms/libunimrcp-server/Makefile.am b/libs/unimrcp/platforms/libunimrcp-server/Makefile.am new file mode 100644 index 0000000000..a67510ee49 --- /dev/null +++ b/libs/unimrcp/platforms/libunimrcp-server/Makefile.am @@ -0,0 +1,36 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_srcdir)/platforms/libunimrcp-server/include \ + -I$(top_srcdir)/modules/mrcp-sofiasip/include \ + -I$(top_srcdir)/modules/mrcp-unirtsp/include \ + -I$(top_srcdir)/libs/mrcp-server/include \ + -I$(top_srcdir)/libs/mrcp-engine/include \ + -I$(top_srcdir)/libs/mrcp-signaling/include \ + -I$(top_srcdir)/libs/mrcpv2-transport/include \ + -I$(top_srcdir)/libs/mrcp/include \ + -I$(top_srcdir)/libs/mrcp/message/include \ + -I$(top_srcdir)/libs/mrcp/control/include \ + -I$(top_srcdir)/libs/mrcp/resources/include \ + -I$(top_srcdir)/libs/mpf/include \ + -I$(top_srcdir)/libs/apr-toolkit/include \ + -I$(top_srcdir)/build \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) + +lib_LTLIBRARIES = libunimrcpserver.la + +include_HEADERS = include/unimrcp_server.h + +libunimrcpserver_la_SOURCES = src/unimrcp_server.c + +libunimrcpserver_la_LIBADD = $(top_builddir)/modules/mrcp-sofiasip/libmrcpsofiasip.la \ + $(top_builddir)/modules/mrcp-unirtsp/libmrcpunirtsp.la \ + $(top_builddir)/libs/mrcp-server/libmrcpserver.la \ + $(top_builddir)/libs/mrcp-signaling/libmrcpsignaling.la \ + $(top_builddir)/libs/mrcpv2-transport/libmrcpv2transport.la \ + $(top_builddir)/libs/mrcp-engine/libmrcpengine.la \ + $(top_builddir)/libs/mrcp/libmrcp.la \ + $(top_builddir)/libs/mpf/libmpf.la \ + $(top_builddir)/libs/apr-toolkit/libaprtoolkit.la \ + $(UNIMRCP_APR_LIBS) $(UNIMRCP_APU_LIBS) $(UNIMRCP_SOFIA_LIBS) + +libunimrcpserver_la_LDFLAGS = $(UNI_LT_VERSION) diff --git a/libs/unimrcp/platforms/libunimrcp-server/include/unimrcp_server.h b/libs/unimrcp/platforms/libunimrcp-server/include/unimrcp_server.h new file mode 100644 index 0000000000..39f3813048 --- /dev/null +++ b/libs/unimrcp/platforms/libunimrcp-server/include/unimrcp_server.h @@ -0,0 +1,43 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __UNIMRCP_SERVER_H__ +#define __UNIMRCP_SERVER_H__ + +/** + * @file unimrcp_server.h + * @brief UniMRCP Server + */ + +#include "mrcp_server.h" + +APT_BEGIN_EXTERN_C + +/** + * Start UniMRCP server. + * @param dir_layout the dir layout structure + */ +MRCP_DECLARE(mrcp_server_t*) unimrcp_server_start(apt_dir_layout_t *dir_layout); + +/** + * Shutdown UniMRCP server. + * @param server the MRCP server to shutdown + */ +MRCP_DECLARE(apt_bool_t) unimrcp_server_shutdown(mrcp_server_t *server); + +APT_END_EXTERN_C + +#endif /*__UNIMRCP_SERVER_H__*/ diff --git a/libs/unimrcp/platforms/libunimrcp-server/libunimrcpserver.vcproj b/libs/unimrcp/platforms/libunimrcp-server/libunimrcpserver.vcproj new file mode 100644 index 0000000000..0a89e9d36f --- /dev/null +++ b/libs/unimrcp/platforms/libunimrcp-server/libunimrcpserver.vcproj @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/platforms/libunimrcp-server/src/unimrcp_server.c b/libs/unimrcp/platforms/libunimrcp-server/src/unimrcp_server.c new file mode 100644 index 0000000000..f95e62ff32 --- /dev/null +++ b/libs/unimrcp/platforms/libunimrcp-server/src/unimrcp_server.c @@ -0,0 +1,701 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include "unimrcp_server.h" +#include "uni_version.h" +#include "mrcp_default_factory.h" +#include "mpf_engine.h" +#include "mpf_codec_manager.h" +#include "mpf_rtp_termination_factory.h" +#include "mrcp_sofiasip_server_agent.h" +#include "mrcp_unirtsp_server_agent.h" +#include "mrcp_server_connection.h" +#include "apt_net.h" +#include "apt_log.h" + +#define CONF_FILE_NAME "unimrcpserver.xml" +#define DEFAULT_CONF_DIR_PATH "../conf" +#define DEFAULT_PLUGIN_DIR_PATH "../plugin" +#ifdef WIN32 +#define DEFAULT_PLUGIN_EXT "dll" +#else +#define DEFAULT_PLUGIN_EXT "so" +#endif + +#define DEFAULT_IP_ADDRESS "127.0.0.1" +#define DEFAULT_SIP_PORT 8060 +#define DEFAULT_RTSP_PORT 1554 +#define DEFAULT_MRCP_PORT 1544 +#define DEFAULT_RTP_PORT_MIN 5000 +#define DEFAULT_RTP_PORT_MAX 6000 + +#define DEFAULT_SOFIASIP_UA_NAME "UniMRCP SofiaSIP" +#define DEFAULT_SDP_ORIGIN "UniMRCPServer" + +#define XML_FILE_BUFFER_LENGTH 2000 + +static apr_xml_doc* unimrcp_server_config_parse(const char *path, apr_pool_t *pool); +static apt_bool_t unimrcp_server_config_load(mrcp_server_t *server, const char *plugin_dir_path, const apr_xml_doc *doc, apr_pool_t *pool); + +/** Start UniMRCP server */ +MRCP_DECLARE(mrcp_server_t*) unimrcp_server_start(apt_dir_layout_t *dir_layout) +{ + apr_pool_t *pool; + apr_xml_doc *doc; + mrcp_resource_factory_t *resource_factory; + mpf_codec_manager_t *codec_manager; + mrcp_server_t *server; + + if(!dir_layout) { + return NULL; + } + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"UniMRCP Server ["UNI_VERSION_STRING"]"); + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"APR ["APR_VERSION_STRING"]"); + server = mrcp_server_create(dir_layout); + if(!server) { + return NULL; + } + pool = mrcp_server_memory_pool_get(server); + if(!pool) { + return NULL; + } + + resource_factory = mrcp_default_factory_create(pool); + if(resource_factory) { + mrcp_server_resource_factory_register(server,resource_factory); + } + + codec_manager = mpf_engine_codec_manager_create(pool); + if(codec_manager) { + mrcp_server_codec_manager_register(server,codec_manager); + } + + doc = unimrcp_server_config_parse(dir_layout->conf_dir_path,pool); + if(doc) { + unimrcp_server_config_load(server,dir_layout->plugin_dir_path,doc,pool); + } + + mrcp_server_start(server); + return server; +} + +/** Shutdown UniMRCP server */ +MRCP_DECLARE(apt_bool_t) unimrcp_server_shutdown(mrcp_server_t *server) +{ + if(mrcp_server_shutdown(server) == FALSE) { + return FALSE; + } + return mrcp_server_destroy(server); +} + + +/** Parse config file */ +static apr_xml_doc* unimrcp_server_config_parse(const char *dir_path, apr_pool_t *pool) +{ + apr_xml_parser *parser = NULL; + apr_xml_doc *doc = NULL; + apr_file_t *fd = NULL; + apr_status_t rv; + const char *file_path; + + if(!dir_path) { + dir_path = DEFAULT_CONF_DIR_PATH; + } + if(*dir_path == '\0') { + file_path = CONF_FILE_NAME; + } + else { + file_path = apr_psprintf(pool,"%s/%s",dir_path,CONF_FILE_NAME); + } + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Open Config File [%s]",file_path); + rv = apr_file_open(&fd,file_path,APR_READ|APR_BINARY,0,pool); + if(rv != APR_SUCCESS) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Open Config File [%s]",file_path); + return NULL; + } + + rv = apr_xml_parse_file(pool,&parser,&doc,fd,XML_FILE_BUFFER_LENGTH); + if(rv != APR_SUCCESS) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Parse Config File [%s]",file_path); + return NULL; + } + + apr_file_close(fd); + return doc; +} + +static apt_bool_t param_name_value_get(const apr_xml_elem *elem, const apr_xml_attr **name, const apr_xml_attr **value) +{ + const apr_xml_attr *attr; + if(!name || !value) { + return FALSE; + } + + *name = NULL; + *value = NULL; + for(attr = elem->attr; attr; attr = attr->next) { + if(strcasecmp(attr->name,"name") == 0) { + *name = attr; + } + else if(strcasecmp(attr->name,"value") == 0) { + *value = attr; + } + } + return (*name && *value) ? TRUE : FALSE; +} + +static char* ip_addr_get(const char *value, apr_pool_t *pool) +{ + if(!value || strcasecmp(value,"auto") == 0) { + char *addr = DEFAULT_IP_ADDRESS; + apt_ip_get(&addr,pool); + return addr; + } + return apr_pstrdup(pool,value); +} + +/** Load map of MRCP resource names */ +static apt_bool_t resource_map_load(apr_table_t *resource_map, const apr_xml_elem *root, apr_pool_t *pool) +{ + const apr_xml_attr *attr_name; + const apr_xml_attr *attr_value; + const apr_xml_elem *elem; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Resource Map"); + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"param") == 0) { + if(param_name_value_get(elem,&attr_name,&attr_value) == TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Param %s:%s",attr_name->value,attr_value->value); + apr_table_set(resource_map,attr_name->value,attr_value->value); + } + } + } + return TRUE; +} + +/** Load map of plugins */ +static apt_bool_t plugin_map_load(apr_table_t *plugin_map, const apr_xml_elem *root, apr_pool_t *pool) +{ + const apr_xml_attr *attr_name; + const apr_xml_attr *attr_value; + const apr_xml_elem *elem; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Plugin Map"); + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"param") == 0) { + if(param_name_value_get(elem,&attr_name,&attr_value) == TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Param %s:%s",attr_name->value,attr_value->value); + apr_table_set(plugin_map,attr_name->value,attr_value->value); + } + } + } + return TRUE; +} + +/** Load SofiaSIP signaling agent */ +static mrcp_sig_agent_t* unimrcp_server_sofiasip_agent_load(mrcp_server_t *server, const apr_xml_elem *root, apr_pool_t *pool) +{ + const apr_xml_elem *elem; + mrcp_sofia_server_config_t *config = mrcp_sofiasip_server_config_alloc(pool); + config->local_ip = DEFAULT_IP_ADDRESS; + config->local_port = DEFAULT_SIP_PORT; + config->ext_ip = NULL; + config->user_agent_name = DEFAULT_SOFIASIP_UA_NAME; + config->origin = DEFAULT_SDP_ORIGIN; + + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading SofiaSIP Agent"); + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"param") == 0) { + const apr_xml_attr *attr_name; + const apr_xml_attr *attr_value; + if(param_name_value_get(elem,&attr_name,&attr_value) == TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Param %s:%s",attr_name->value,attr_value->value); + if(strcasecmp(attr_name->value,"sip-ip") == 0) { + config->local_ip = ip_addr_get(attr_value->value,pool); + } + else if(strcasecmp(attr_name->value,"sip-ext-ip") == 0) { + config->ext_ip = ip_addr_get(attr_value->value,pool); + } + else if(strcasecmp(attr_name->value,"sip-port") == 0) { + config->local_port = (apr_port_t)atol(attr_value->value); + } + else if(strcasecmp(attr_name->value,"sip-transport") == 0) { + config->transport = apr_pstrdup(pool,attr_value->value); + } + else if(strcasecmp(attr_name->value,"ua-name") == 0) { + config->user_agent_name = apr_pstrdup(pool,attr_value->value); + } + else if(strcasecmp(attr_name->value,"sdp-origin") == 0) { + config->origin = apr_pstrdup(pool,attr_value->value); + } + else if(strcasecmp(attr_name->value,"force-destination") == 0) { + config->force_destination = atoi(attr_value->value); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr_name->value); + } + } + } + } + return mrcp_sofiasip_server_agent_create(config,pool); +} + +/** Load UniRTSP signaling agent */ +static mrcp_sig_agent_t* unimrcp_server_rtsp_agent_load(mrcp_server_t *server, const apr_xml_elem *root, apr_pool_t *pool) +{ + const apr_xml_elem *elem; + rtsp_server_config_t *config = mrcp_unirtsp_server_config_alloc(pool); + config->local_ip = DEFAULT_IP_ADDRESS; + config->local_port = DEFAULT_RTSP_PORT; + config->origin = DEFAULT_SDP_ORIGIN; + + + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading UniRTSP Agent"); + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"param") == 0) { + const apr_xml_attr *attr_name; + const apr_xml_attr *attr_value; + if(param_name_value_get(elem,&attr_name,&attr_value) == TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Param %s:%s",attr_name->value,attr_value->value); + if(strcasecmp(attr_name->value,"rtsp-ip") == 0) { + config->local_ip = ip_addr_get(attr_value->value,pool); + } + else if(strcasecmp(attr_name->value,"rtsp-port") == 0) { + config->local_port = (apr_port_t)atol(attr_value->value); + } + else if(strcasecmp(attr_name->value,"sdp-origin") == 0) { + config->origin = apr_pstrdup(pool,attr_value->value); + } + else if(strcasecmp(attr_name->value,"max-connection-count") == 0) { + config->max_connection_count = atol(attr_value->value); + } + else if(strcasecmp(attr_name->value,"force-destination") == 0) { + config->force_destination = atoi(attr_value->value); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr_name->value); + } + } + } + else if(strcasecmp(elem->name,"resourcemap") == 0) { + resource_map_load(config->resource_map,elem,pool); + } + } + return mrcp_unirtsp_server_agent_create(config,pool); +} + +/** Load signaling agents */ +static apt_bool_t unimrcp_server_signaling_agents_load(mrcp_server_t *server, const apr_xml_elem *root, apr_pool_t *pool) +{ + const apr_xml_elem *elem; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Signaling Agents"); + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"agent") == 0) { + mrcp_sig_agent_t *sig_agent = NULL; + const char *name = NULL; + const apr_xml_attr *attr; + for(attr = elem->attr; attr; attr = attr->next) { + if(strcasecmp(attr->name,"name") == 0) { + name = apr_pstrdup(pool,attr->value); + } + else if(strcasecmp(attr->name,"class") == 0) { + if(strcasecmp(attr->value,"SofiaSIP") == 0) { + sig_agent = unimrcp_server_sofiasip_agent_load(server,elem,pool); + } + else if(strcasecmp(attr->value,"UniRTSP") == 0) { + sig_agent = unimrcp_server_rtsp_agent_load(server,elem,pool); + } + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr->name); + } + } + if(sig_agent) { + mrcp_server_signaling_agent_register(server,sig_agent,name); + } + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Element <%s>",elem->name); + } + } + return TRUE; +} + +/** Load MRCPv2 connection agent */ +static mrcp_connection_agent_t* unimrcp_server_connection_agent_load(mrcp_server_t *server, const apr_xml_elem *root, apr_pool_t *pool) +{ + const apr_xml_elem *elem; + char *mrcp_ip = DEFAULT_IP_ADDRESS; + apr_port_t mrcp_port = DEFAULT_MRCP_PORT; + apr_size_t max_connection_count = 100; + apt_bool_t force_new_connection = FALSE; + + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading MRCPv2 Agent"); + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"param") == 0) { + const apr_xml_attr *attr_name; + const apr_xml_attr *attr_value; + if(param_name_value_get(elem,&attr_name,&attr_value) == TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Param %s:%s",attr_name->value,attr_value->value); + if(strcasecmp(attr_name->value,"mrcp-ip") == 0) { + mrcp_ip = ip_addr_get(attr_value->value,pool); + } + else if(strcasecmp(attr_name->value,"mrcp-port") == 0) { + mrcp_port = (apr_port_t)atol(attr_value->value); + } + else if(strcasecmp(attr_name->value,"max-connection-count") == 0) { + max_connection_count = atol(attr_value->value); + } + else if(strcasecmp(attr_name->value,"force-new-connection") == 0) { + force_new_connection = atoi(attr_value->value); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr_name->value); + } + } + } + } + return mrcp_server_connection_agent_create(mrcp_ip,mrcp_port,max_connection_count,force_new_connection,pool); +} + +/** Load MRCPv2 conection agents */ +static apt_bool_t unimrcp_server_connection_agents_load(mrcp_server_t *server, const apr_xml_elem *root, apr_pool_t *pool) +{ + const apr_xml_elem *elem; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Connection Agents"); + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"agent") == 0) { + mrcp_connection_agent_t *connection_agent; + const char *name = NULL; + const apr_xml_attr *attr; + for(attr = elem->attr; attr; attr = attr->next) { + if(strcasecmp(attr->name,"name") == 0) { + name = apr_pstrdup(pool,attr->value); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr->name); + } + } + connection_agent = unimrcp_server_connection_agent_load(server,elem,pool); + if(connection_agent) { + mrcp_server_connection_agent_register(server,connection_agent,name); + } + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Element <%s>",elem->name); + } + } + return TRUE; +} + +/** Load RTP termination factory */ +static mpf_termination_factory_t* unimrcp_server_rtp_factory_load(mrcp_server_t *server, const apr_xml_elem *root, apr_pool_t *pool) +{ + const apr_xml_elem *elem; + char *rtp_ip = DEFAULT_IP_ADDRESS; + char *rtp_ext_ip = NULL; + mpf_rtp_config_t *rtp_config = mpf_rtp_config_create(pool); + rtp_config->rtp_port_min = DEFAULT_RTP_PORT_MIN; + rtp_config->rtp_port_max = DEFAULT_RTP_PORT_MAX; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading RTP Termination Factory"); + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"param") == 0) { + const apr_xml_attr *attr_name; + const apr_xml_attr *attr_value; + if(param_name_value_get(elem,&attr_name,&attr_value) == TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Param %s:%s",attr_name->value,attr_value->value); + if(strcasecmp(attr_name->value,"rtp-ip") == 0) { + rtp_ip = ip_addr_get(attr_value->value,pool); + } + else if(strcasecmp(attr_name->value,"rtp-ext-ip") == 0) { + rtp_ext_ip = ip_addr_get(attr_value->value,pool); + } + else if(strcasecmp(attr_name->value,"rtp-port-min") == 0) { + rtp_config->rtp_port_min = (apr_port_t)atol(attr_value->value); + } + else if(strcasecmp(attr_name->value,"rtp-port-max") == 0) { + rtp_config->rtp_port_max = (apr_port_t)atol(attr_value->value); + } + else if(strcasecmp(attr_name->value,"playout-delay") == 0) { + rtp_config->jb_config.initial_playout_delay = atol(attr_value->value); + } + else if(strcasecmp(attr_name->value,"min-playout-delay") == 0) { + rtp_config->jb_config.min_playout_delay = atol(attr_value->value); + } + else if(strcasecmp(attr_name->value,"max-playout-delay") == 0) { + rtp_config->jb_config.max_playout_delay = atol(attr_value->value); + } + else if(strcasecmp(attr_name->value,"codecs") == 0) { + const mpf_codec_manager_t *codec_manager = mrcp_server_codec_manager_get(server); + if(codec_manager) { + mpf_codec_manager_codec_list_load(codec_manager,&rtp_config->codec_list,attr_value->value,pool); + } + } + else if(strcasecmp(attr_name->value,"ptime") == 0) { + rtp_config->ptime = (apr_uint16_t)atol(attr_value->value); + } + else if(strcasecmp(attr_name->value,"own-preference") == 0) { + rtp_config->own_preferrence = atoi(attr_value->value); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr_name->value); + } + } + } + } + apt_string_set(&rtp_config->ip,rtp_ip); + if(rtp_ext_ip) { + apt_string_set(&rtp_config->ext_ip,rtp_ext_ip); + } + return mpf_rtp_termination_factory_create(rtp_config,pool); +} + +/** Load media engines */ +static apt_bool_t unimrcp_server_media_engines_load(mrcp_server_t *server, const apr_xml_elem *root, apr_pool_t *pool) +{ + const apr_xml_elem *elem; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Media Engines"); + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"engine") == 0) { + mpf_engine_t *media_engine; + const char *name = NULL; + const apr_xml_attr *attr; + for(attr = elem->attr; attr; attr = attr->next) { + if(strcasecmp(attr->name,"name") == 0) { + name = apr_pstrdup(pool,attr->value); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr->name); + } + } + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Media Engine"); + media_engine = mpf_engine_create(pool); + if(media_engine) { + mrcp_server_media_engine_register(server,media_engine,name); + } + } + else if(strcasecmp(elem->name,"rtp") == 0) { + mpf_termination_factory_t *rtp_factory; + const char *name = NULL; + const apr_xml_attr *attr; + for(attr = elem->attr; attr; attr = attr->next) { + if(strcasecmp(attr->name,"name") == 0) { + name = apr_pstrdup(pool,attr->value); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr->name); + } + } + rtp_factory = unimrcp_server_rtp_factory_load(server,elem,pool); + if(rtp_factory) { + mrcp_server_rtp_factory_register(server,rtp_factory,name); + } + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Element <%s>",elem->name); + } + } + return TRUE; +} + +/** Load plugin */ +static apt_bool_t unimrcp_server_plugin_load(mrcp_server_t *server, const char *plugin_dir_path, const apr_xml_elem *root, apr_pool_t *pool) +{ + const char *plugin_name = NULL; + const char *plugin_class = NULL; + const char *plugin_ext = NULL; + const char *plugin_path = NULL; + apt_bool_t plugin_enabled = TRUE; + const apr_xml_attr *attr; + for(attr = root->attr; attr; attr = attr->next) { + if(strcasecmp(attr->name,"name") == 0) { + plugin_name = apr_pstrdup(pool,attr->value); + } + else if(strcasecmp(attr->name,"class") == 0) { + plugin_class = attr->value; + } + else if(strcasecmp(attr->name,"ext") == 0) { + plugin_ext = attr->value; + } + else if(strcasecmp(attr->name,"enable") == 0) { + plugin_enabled = atoi(attr->value); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr->name); + } + } + + if(!plugin_class || !plugin_enabled) { + return FALSE; + } + if(!plugin_dir_path) { + plugin_dir_path = DEFAULT_PLUGIN_DIR_PATH; + } + if(!plugin_ext) { + plugin_ext = DEFAULT_PLUGIN_EXT; + } + + if(*plugin_dir_path == '\0') { + plugin_path = apr_psprintf(pool,"%s.%s",plugin_class,plugin_ext); + } + else { + plugin_path = apr_psprintf(pool,"%s/%s.%s",plugin_dir_path,plugin_class,plugin_ext); + } + + return mrcp_server_plugin_register(server,plugin_path,plugin_name); +} + +/** Load plugins */ +static apt_bool_t unimrcp_server_plugins_load(mrcp_server_t *server, const char *plugin_dir_path, const apr_xml_elem *root, apr_pool_t *pool) +{ + const apr_xml_elem *elem; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Plugins (Resource Engines)"); + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"engine") == 0) { + unimrcp_server_plugin_load(server,plugin_dir_path,elem,pool); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Element <%s>",elem->name); + } + } + return TRUE; +} + + +/** Load settings */ +static apt_bool_t unimrcp_server_settings_load(mrcp_server_t *server, const char *plugin_dir_path, const apr_xml_elem *root, apr_pool_t *pool) +{ + const apr_xml_elem *elem; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Settings"); + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"signaling") == 0) { + unimrcp_server_signaling_agents_load(server,elem,pool); + } + else if(strcasecmp(elem->name,"connection") == 0) { + unimrcp_server_connection_agents_load(server,elem,pool); + } + else if(strcasecmp(elem->name,"media") == 0) { + unimrcp_server_media_engines_load(server,elem,pool); + } + else if(strcasecmp(elem->name,"plugin") == 0) { + unimrcp_server_plugins_load(server,plugin_dir_path,elem,pool); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Element <%s>",elem->name); + } + } + return TRUE; +} + +/** Load profile */ +static apt_bool_t unimrcp_server_profile_load(mrcp_server_t *server, const apr_xml_elem *root, apr_pool_t *pool) +{ + const char *name = NULL; + mrcp_profile_t *profile; + mrcp_sig_agent_t *sig_agent = NULL; + mrcp_connection_agent_t *cnt_agent = NULL; + mpf_engine_t *media_engine = NULL; + mpf_termination_factory_t *rtp_factory = NULL; + apr_table_t *plugin_map = NULL; + const apr_xml_elem *elem; + const apr_xml_attr *attr; + for(attr = root->attr; attr; attr = attr->next) { + if(strcasecmp(attr->name,"name") == 0) { + name = apr_pstrdup(pool,attr->value); + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Profile [%s]",name); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr->name); + } + } + if(!name) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Load Profile: no profile name specified"); + return FALSE; + } + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"param") == 0) { + const apr_xml_attr *attr_name; + const apr_xml_attr *attr_value; + if(param_name_value_get(elem,&attr_name,&attr_value) == TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Loading Profile %s [%s]",attr_name->value,attr_value->value); + if(strcasecmp(attr_name->value,"signaling-agent") == 0) { + sig_agent = mrcp_server_signaling_agent_get(server,attr_value->value); + } + else if(strcasecmp(attr_name->value,"connection-agent") == 0) { + cnt_agent = mrcp_server_connection_agent_get(server,attr_value->value); + } + else if(strcasecmp(attr_name->value,"media-engine") == 0) { + media_engine = mrcp_server_media_engine_get(server,attr_value->value); + } + else if(strcasecmp(attr_name->value,"rtp-factory") == 0) { + rtp_factory = mrcp_server_rtp_factory_get(server,attr_value->value); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr_name->value); + } + } + } + else if(strcasecmp(elem->name,"pluginmap") == 0) { + plugin_map = apr_table_make(pool,2); + plugin_map_load(plugin_map,elem,pool); + } + } + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create Profile [%s]",name); + profile = mrcp_server_profile_create(NULL,sig_agent,cnt_agent,media_engine,rtp_factory,pool); + return mrcp_server_profile_register(server,profile,plugin_map,name); +} + +/** Load profiles */ +static apt_bool_t unimrcp_server_profiles_load(mrcp_server_t *server, const apr_xml_elem *root, apr_pool_t *pool) +{ + const apr_xml_elem *elem; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Profiles"); + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"profile") == 0) { + unimrcp_server_profile_load(server,elem,pool); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Element <%s>",elem->name); + } + } + return TRUE; +} + +/** Load configuration (settings and profiles) */ +static apt_bool_t unimrcp_server_config_load(mrcp_server_t *server, const char *plugin_dir_path, const apr_xml_doc *doc, apr_pool_t *pool) +{ + const apr_xml_elem *elem; + const apr_xml_elem *root = doc->root; + if(!root || strcasecmp(root->name,"unimrcpserver") != 0) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Document"); + return FALSE; + } + for(elem = root->first_child; elem; elem = elem->next) { + if(strcasecmp(elem->name,"settings") == 0) { + unimrcp_server_settings_load(server,plugin_dir_path,elem,pool); + } + else if(strcasecmp(elem->name,"profiles") == 0) { + unimrcp_server_profiles_load(server,elem,pool); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Element <%s>",elem->name); + } + } + + return TRUE; +} diff --git a/libs/unimrcp/platforms/unimrcp-client/Makefile.am b/libs/unimrcp/platforms/unimrcp-client/Makefile.am new file mode 100644 index 0000000000..22fae8c143 --- /dev/null +++ b/libs/unimrcp/platforms/unimrcp-client/Makefile.am @@ -0,0 +1,24 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -Iinclude \ + -I$(top_srcdir)/platforms/libunimrcp-client/include \ + -I$(top_srcdir)/libs/mrcp-client/include \ + -I$(top_srcdir)/libs/mrcp-signaling/include \ + -I$(top_srcdir)/libs/mrcpv2-transport/include \ + -I$(top_srcdir)/libs/mrcp/include \ + -I$(top_srcdir)/libs/mrcp/message/include \ + -I$(top_srcdir)/libs/mrcp/control/include \ + -I$(top_srcdir)/libs/mrcp/resources/include \ + -I$(top_srcdir)/libs/mpf/include \ + -I$(top_srcdir)/libs/apr-toolkit/include \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) + +bin_PROGRAMS = unimrcpclient +unimrcpclient_SOURCES = src/main.c \ + src/demo_framework.c \ + src/demo_synth_application.c \ + src/demo_recog_application.c \ + src/demo_bypass_application.c \ + src/demo_discover_application.c \ + src/demo_util.c +unimrcpclient_LDADD = $(top_builddir)/platforms/libunimrcp-client/libunimrcpclient.la diff --git a/libs/unimrcp/platforms/unimrcp-client/include/demo_application.h b/libs/unimrcp/platforms/unimrcp-client/include/demo_application.h new file mode 100644 index 0000000000..608d996f98 --- /dev/null +++ b/libs/unimrcp/platforms/unimrcp-client/include/demo_application.h @@ -0,0 +1,61 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __DEMO_APPLICATION_H__ +#define __DEMO_APPLICATION_H__ + +/** + * @file demo_application.h + * @brief Demo MRCP Application + */ + +#include "mrcp_application.h" + +APT_BEGIN_EXTERN_C + +/** Demo application declaration */ +typedef struct demo_application_t demo_application_t; + +/** Demo application */ +struct demo_application_t { + /** MRCP application */ + mrcp_application_t *application; + /** Demo framework */ + void *framework; + + /** Virtual run method */ + apt_bool_t (*run)(demo_application_t *application, const char *profile); + /** Virtual app_message handler */ + apt_bool_t (*handler)(demo_application_t *application, const mrcp_app_message_t *app_message); +}; + + +/** Create demo synthesizer application */ +demo_application_t* demo_synth_application_create(apr_pool_t *pool); + +/** Create demo recognizer application */ +demo_application_t* demo_recog_application_create(apr_pool_t *pool); + +/** Create demo bypass media application */ +demo_application_t* demo_bypass_application_create(apr_pool_t *pool); + +/** Create demo resource discover application */ +demo_application_t* demo_discover_application_create(apr_pool_t *pool); + + +APT_END_EXTERN_C + +#endif /*__DEMO_APPLICATION_H__*/ diff --git a/libs/unimrcp/platforms/unimrcp-client/include/demo_framework.h b/libs/unimrcp/platforms/unimrcp-client/include/demo_framework.h new file mode 100644 index 0000000000..5cef92305d --- /dev/null +++ b/libs/unimrcp/platforms/unimrcp-client/include/demo_framework.h @@ -0,0 +1,53 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __DEMO_FRAMEWORK_H__ +#define __DEMO_FRAMEWORK_H__ + +/** + * @file demo_framework.h + * @brief Demo MRCP Application Framework + */ + +#include "mrcp_types.h" + +APT_BEGIN_EXTERN_C + +/** Opaque demo framework declaration */ +typedef struct demo_framework_t demo_framework_t; + +/** + * Create demo framework. + */ +demo_framework_t* demo_framework_create(apt_dir_layout_t *dir_layout); + +/** + * Run demo application. + * @param framework the framework to run application for + * @param app_name the name of the application to run + * @param profile_name the name of the profile to use + */ +apt_bool_t demo_framework_app_run(demo_framework_t *framework, const char *app_name, const char *profile_name); + +/** + * Destroy demo framework. + * @param framework the framework to destroy + */ +apt_bool_t demo_framework_destroy(demo_framework_t *framework); + +APT_END_EXTERN_C + +#endif /*__DEMO_FRAMEWORK_H__*/ diff --git a/libs/unimrcp/platforms/unimrcp-client/include/demo_util.h b/libs/unimrcp/platforms/unimrcp-client/include/demo_util.h new file mode 100644 index 0000000000..78f8ef0b1c --- /dev/null +++ b/libs/unimrcp/platforms/unimrcp-client/include/demo_util.h @@ -0,0 +1,43 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 __DEMO_UTIL_H__ +#define __DEMO_UTIL_H__ + +/** + * @file demo_util.h + * @brief Demo MRCP Utilities + */ + +#include "mrcp_application.h" + +APT_BEGIN_EXTERN_C + +/** Create demo MRCP message (SPEAK request) */ +mrcp_message_t* demo_speak_message_create(mrcp_session_t *session, mrcp_channel_t *channel, const apt_dir_layout_t *dir_layout); + +/** Create demo MRCP message (DEFINE-GRAMMAR request) */ +mrcp_message_t* demo_define_grammar_message_create(mrcp_session_t *session, mrcp_channel_t *channel, const apt_dir_layout_t *dir_layout); +/** Create demo MRCP message (RECOGNIZE request) */ +mrcp_message_t* demo_recognize_message_create(mrcp_session_t *session, mrcp_channel_t *channel, const apt_dir_layout_t *dir_layout); + + +/** Create demo RTP termination descriptor */ +mpf_rtp_termination_descriptor_t* demo_rtp_descriptor_create(apr_pool_t *pool); + +APT_END_EXTERN_C + +#endif /*__DEMO_UTIL_H__*/ diff --git a/libs/unimrcp/platforms/unimrcp-client/src/demo_bypass_application.c b/libs/unimrcp/platforms/unimrcp-client/src/demo_bypass_application.c new file mode 100644 index 0000000000..639b411c12 --- /dev/null +++ b/libs/unimrcp/platforms/unimrcp-client/src/demo_bypass_application.c @@ -0,0 +1,209 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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. + */ + +/* + * Demo synthesizer scenario (client stack stays out of media path). + * C -> S: SIP INVITE or RTPS SETUP (add synthesizer channel) + * S -> C: SIP OK or RTPS OK + * C -> S: MRCP SPEAK + * S -> C: MRCP IN-PROGRESS + * S -> X: RTP Start Transmission (RTP stream is sent directly to external endpoint bypassing client stack) + * S -> C: MRCP SPEAK-COMPLETE + * S -> X: RTP Stop Transmission + * C -> S: SIP INVITE or RTPS SETUP (optionally remove synthesizer channel) + * S -> C: SIP OK or RTPS OK + * C -> S: SIP BYE or RTPS TEARDOWN + * S -> C: SIP OK or RTPS OK + */ + +#include "demo_application.h" +#include "demo_util.h" +#include "mrcp_session.h" +#include "mrcp_message.h" +#include "mrcp_generic_header.h" +#include "mrcp_synth_header.h" +#include "mrcp_synth_resource.h" +#include "apt_log.h" + +typedef struct demo_app_channel_t demo_app_channel_t; + +/** Declaration of synthesizer application channel */ +struct demo_app_channel_t { + /** MRCP control channel */ + mrcp_channel_t *channel; +}; + +/** Declaration of demo application methods */ +static apt_bool_t demo_application_run(demo_application_t *demo_application, const char *profile); +static apt_bool_t demo_application_handler(demo_application_t *application, const mrcp_app_message_t *app_message); + +static apt_bool_t demo_application_on_session_update(mrcp_application_t *application, mrcp_session_t *session, mrcp_sig_status_code_e status); +static apt_bool_t demo_application_on_session_terminate(mrcp_application_t *application, mrcp_session_t *session, mrcp_sig_status_code_e status); +static apt_bool_t demo_application_on_channel_add(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_sig_status_code_e status); +static apt_bool_t demo_application_on_channel_remove(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_sig_status_code_e status); +static apt_bool_t demo_application_on_message_receive(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_message_t *message); + +static const mrcp_app_message_dispatcher_t demo_application_dispatcher = { + demo_application_on_session_update, + demo_application_on_session_terminate, + demo_application_on_channel_add, + demo_application_on_channel_remove, + demo_application_on_message_receive +}; + + +/** Create demo bypass media application */ +demo_application_t* demo_bypass_application_create(apr_pool_t *pool) +{ + demo_application_t *demo_application = apr_palloc(pool,sizeof(demo_application_t)); + demo_application->application = NULL; + demo_application->framework = NULL; + demo_application->handler = demo_application_handler; + demo_application->run = demo_application_run; + return demo_application; +} + +/** Create demo channel */ +static mrcp_channel_t* demo_application_channel_create(mrcp_session_t *session) +{ + mrcp_channel_t *channel; + /* create channel */ + demo_app_channel_t *demo_channel = apr_palloc(session->pool,sizeof(demo_app_channel_t)); + mpf_rtp_termination_descriptor_t *rtp_descriptor = demo_rtp_descriptor_create(session->pool); + channel = mrcp_application_channel_create( + session, /* session, channel belongs to */ + MRCP_SYNTHESIZER_RESOURCE, /* MRCP resource identifier */ + NULL, /* no termination (not to use internal media processing) */ + rtp_descriptor, /* RTP descriptor, used to create RTP termination */ + demo_channel); /* object to associate */ + return channel; +} + + +/** Run demo scenario */ +static apt_bool_t demo_application_run(demo_application_t *demo_application, const char *profile) +{ + mrcp_channel_t *channel; + /* create session */ + mrcp_session_t *session = mrcp_application_session_create(demo_application->application,profile,NULL); + if(!session) { + return FALSE; + } + + /* create channel and associate all the required data */ + channel = demo_application_channel_create(session); + if(!channel) { + mrcp_application_session_destroy(session); + return FALSE; + } + + /* add channel to session (send asynchronous request) */ + if(mrcp_application_channel_add(session,channel) != TRUE) { + /* session and channel are still not referenced + and both are allocated from session pool and will + be freed with session destroy call */ + mrcp_application_session_destroy(session); + return FALSE; + } + + return TRUE; +} + +/** Handle the messages sent from the MRCP client stack */ +static apt_bool_t demo_application_handler(demo_application_t *application, const mrcp_app_message_t *app_message) +{ + /* app_message should be dispatched now, + * the default dispatcher is used in demo. */ + return mrcp_application_message_dispatch(&demo_application_dispatcher,app_message); +} + +/** Handle the responses sent to session update requests */ +static apt_bool_t demo_application_on_session_update(mrcp_application_t *application, mrcp_session_t *session, mrcp_sig_status_code_e status) +{ + return TRUE; +} + +/** Handle the responses sent to session terminate requests */ +static apt_bool_t demo_application_on_session_terminate(mrcp_application_t *application, mrcp_session_t *session, mrcp_sig_status_code_e status) +{ + /* received response to session termination request, + now it's safe to destroy no more referenced session */ + mrcp_application_session_destroy(session); + return TRUE; +} + +/** Handle the responses sent to channel add requests */ +static apt_bool_t demo_application_on_channel_add(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_sig_status_code_e status) +{ + if(status == MRCP_SIG_STATUS_CODE_SUCCESS) { + mpf_rtp_termination_descriptor_t *rtp_descriptor; + mrcp_message_t *mrcp_message; + const apt_dir_layout_t *dir_layout = mrcp_application_dir_layout_get(application); + /* create and send SPEAK request */ + mrcp_message = demo_speak_message_create(session,channel,dir_layout); + if(mrcp_message) { + mrcp_application_message_send(session,channel,mrcp_message); + } + rtp_descriptor = mrcp_application_rtp_descriptor_get(channel); + if(rtp_descriptor) { + mpf_rtp_media_descriptor_t *local_media = rtp_descriptor->audio.local; + mpf_rtp_media_descriptor_t *remote_media = rtp_descriptor->audio.remote; + if(local_media && remote_media) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Media Attributes: L[%s/%d] R[%s/%d]", + local_media->base.ip.buf, + local_media->base.port, + remote_media->base.ip.buf, + remote_media->base.port); + } + } + } + else { + /* error case, just terminate the demo */ + mrcp_application_session_terminate(session); + } + return TRUE; +} + +/** Handle the responses sent to channel remove requests */ +static apt_bool_t demo_application_on_channel_remove(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_sig_status_code_e status) +{ + /* terminate the demo */ + mrcp_application_session_terminate(session); + return TRUE; +} + +/** Handle the MRCP responses/events */ +static apt_bool_t demo_application_on_message_receive(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_message_t *message) +{ + if(message->start_line.message_type == MRCP_MESSAGE_TYPE_RESPONSE) { + /* received MRCP response */ + if(message->start_line.method_id == SYNTHESIZER_SPEAK) { + /* received the response to SPEAK request, + waiting for SPEAK-COMPLETE event */ + } + else { + /* received unexpected response */ + } + } + else if(message->start_line.message_type == MRCP_MESSAGE_TYPE_EVENT) { + /* received MRCP event */ + if(message->start_line.method_id == SYNTHESIZER_SPEAK_COMPLETE) { + /* received SPEAK-COMPLETE event, remove channel */ + mrcp_application_channel_remove(session,channel); + } + } + return TRUE; +} diff --git a/libs/unimrcp/platforms/unimrcp-client/src/demo_discover_application.c b/libs/unimrcp/platforms/unimrcp-client/src/demo_discover_application.c new file mode 100644 index 0000000000..e0b2e35d87 --- /dev/null +++ b/libs/unimrcp/platforms/unimrcp-client/src/demo_discover_application.c @@ -0,0 +1,115 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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. + */ + +/* + * Demo resource discovery. + * C -> S: SIP OPTIONS or RTPS DESCRIBE + * S -> C: SIP OK or RTPS OK + */ + +#include "demo_application.h" +#include "mrcp_session_descriptor.h" +#include "mrcp_control_descriptor.h" +#include "apt_log.h" + + +/** Declaration of synthesizer application methods */ +static apt_bool_t discover_application_run(demo_application_t *demo_application, const char *profile); +static apt_bool_t discover_application_handler(demo_application_t *application, const mrcp_app_message_t *app_message); + +/** Declaration of application message handlers */ +static apt_bool_t discover_application_on_session_terminate(mrcp_application_t *application, mrcp_session_t *session, mrcp_sig_status_code_e status); +static apt_bool_t discover_application_on_resource_discover(mrcp_application_t *application, mrcp_session_t *session, mrcp_session_descriptor_t *descriptor, mrcp_sig_status_code_e status); + +static const mrcp_app_message_dispatcher_t discover_application_dispatcher = { + NULL, + discover_application_on_session_terminate, + NULL, + NULL, + NULL, + NULL, + NULL, + discover_application_on_resource_discover +}; + +/** Create demo resource discover application */ +demo_application_t* demo_discover_application_create(apr_pool_t *pool) +{ + demo_application_t *discover_application = apr_palloc(pool,sizeof(demo_application_t)); + discover_application->application = NULL; + discover_application->framework = NULL; + discover_application->handler = discover_application_handler; + discover_application->run = discover_application_run; + return discover_application; +} + +/** Run demo resource discover scenario */ +static apt_bool_t discover_application_run(demo_application_t *demo_application, const char *profile) +{ + /* create session */ + mrcp_session_t *session = mrcp_application_session_create(demo_application->application,profile,NULL); + if(!session) { + return FALSE; + } + + /* send resource discover request */ + if(mrcp_application_resource_discover(session) != TRUE) { + mrcp_application_session_destroy(session); + return FALSE; + } + + return TRUE; +} + +/** Handle the messages sent from the MRCP client stack */ +static apt_bool_t discover_application_handler(demo_application_t *application, const mrcp_app_message_t *app_message) +{ + /* app_message should be dispatched now, + * the default dispatcher is used in demo. */ + return mrcp_application_message_dispatch(&discover_application_dispatcher,app_message); +} + +/** Handle the responses sent to session terminate requests */ +static apt_bool_t discover_application_on_session_terminate(mrcp_application_t *application, mrcp_session_t *session, mrcp_sig_status_code_e status) +{ + /* received response to session termination request, + now it's safe to destroy no more referenced session */ + mrcp_application_session_destroy(session); + return TRUE; +} + +/** Handle the responses sent to resource discover requests */ +static apt_bool_t discover_application_on_resource_discover(mrcp_application_t *application, mrcp_session_t *session, mrcp_session_descriptor_t *descriptor, mrcp_sig_status_code_e status) +{ + if(descriptor && status == MRCP_SIG_STATUS_CODE_SUCCESS) { + int i; + int count = descriptor->control_media_arr->nelts; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"On Resource Discover [%d]", count); + + for(i = 0; i < count; i++) { + mrcp_control_descriptor_t *control_media = mrcp_session_control_media_get(descriptor,i); + if(control_media) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"[%d] - %s", i,control_media->resource_name.buf); + } + } + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Failed to Discover Resources"); + } + + mrcp_application_session_terminate(session); + return TRUE; +} diff --git a/libs/unimrcp/platforms/unimrcp-client/src/demo_framework.c b/libs/unimrcp/platforms/unimrcp-client/src/demo_framework.c new file mode 100644 index 0000000000..9d65750fbd --- /dev/null +++ b/libs/unimrcp/platforms/unimrcp-client/src/demo_framework.c @@ -0,0 +1,245 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include "demo_framework.h" +#include "demo_application.h" +#include "unimrcp_client.h" +#include "apt_consumer_task.h" +#include "apt_log.h" + +#define FRAMEWORK_TASK_NAME "Framework Task" + +#define MAX_APP_NAME_LENGTH 16 +#define MAX_PROFILE_NAME_LENGTH 16 + +/** Demo framework */ +struct demo_framework_t { + /** MRCP client stack instance */ + mrcp_client_t *client; + /** Message processing task */ + apt_consumer_task_t *task; + /** Table of demo applications */ + apr_hash_t *application_table; + /** Memory to allocate memory from */ + apr_pool_t *pool; +}; + +typedef struct framework_task_data_t framework_task_data_t; +struct framework_task_data_t { + char app_name[MAX_APP_NAME_LENGTH]; + char profile_name[MAX_PROFILE_NAME_LENGTH]; + demo_application_t *demo_application; + const mrcp_app_message_t *app_message; +}; + +typedef enum { + DEMO_APPLICATION_MSG_ID, + DEMO_CONSOLE_MSG_ID +} framework_msg_type_e; + +static apt_bool_t demo_framework_message_handler(const mrcp_app_message_t *app_message); +static apt_bool_t demo_framework_consumer_task_create(demo_framework_t *framework); +static apt_bool_t demo_framework_app_register(demo_framework_t *framework, demo_application_t *demo_application, const char *name); + +/** Create demo framework */ +demo_framework_t* demo_framework_create(apt_dir_layout_t *dir_layout) +{ + demo_framework_t *framework = NULL; + mrcp_client_t *client = unimrcp_client_create(dir_layout); + if(client) { + demo_application_t *demo_application; + apr_pool_t *pool = mrcp_client_memory_pool_get(client); + /* create demo framework */ + framework = apr_palloc(pool,sizeof(demo_framework_t)); + framework->pool = pool; + framework->client = client; + framework->application_table = apr_hash_make(pool); + + /* create demo synthesizer application */ + demo_application = demo_synth_application_create(framework->pool); + if(demo_application) { + demo_framework_app_register(framework,demo_application,"synth"); + } + + /* create demo recognizer application */ + demo_application = demo_recog_application_create(framework->pool); + if(demo_application) { + demo_framework_app_register(framework,demo_application,"recog"); + } + + /* create demo bypass media application */ + demo_application = demo_bypass_application_create(framework->pool); + if(demo_application) { + demo_framework_app_register(framework,demo_application,"bypass"); + } + + /* create demo resource discover application */ + demo_application = demo_discover_application_create(framework->pool); + if(demo_application) { + demo_framework_app_register(framework,demo_application,"discover"); + } + + demo_framework_consumer_task_create(framework); + + if(framework->task) { + apt_task_t *task = apt_consumer_task_base_get(framework->task); + apt_task_start(task); + } + + /* start client stack */ + mrcp_client_start(client); + } + + return framework; +} + +/** Run demo application */ +apt_bool_t demo_framework_app_run(demo_framework_t *framework, const char *app_name, const char *profile_name) +{ + apt_task_t *task = apt_consumer_task_base_get(framework->task); + apt_task_msg_t *task_msg = apt_task_msg_get(task); + if(task_msg) { + framework_task_data_t *framework_task_data = (framework_task_data_t*)task_msg->data; + task_msg->type = TASK_MSG_USER; + task_msg->sub_type = DEMO_CONSOLE_MSG_ID; + framework_task_data = (framework_task_data_t*) task_msg->data; + strcpy(framework_task_data->app_name,app_name); + strcpy(framework_task_data->profile_name,profile_name); + framework_task_data->app_message = NULL; + framework_task_data->demo_application = NULL; + apt_task_msg_signal(task,task_msg); + } + return TRUE; +} + +/** Destroy demo framework */ +apt_bool_t demo_framework_destroy(demo_framework_t *framework) +{ + if(!framework) { + return FALSE; + } + + if(framework->task) { + apt_task_t *task = apt_consumer_task_base_get(framework->task); + apt_task_terminate(task,TRUE); + apt_task_destroy(task); + framework->task = NULL; + } + + mrcp_client_shutdown(framework->client); + return mrcp_client_destroy(framework->client); +} + +static apt_bool_t demo_framework_app_register(demo_framework_t *framework, demo_application_t *demo_application, const char *name) +{ + apr_hash_set(framework->application_table,name,APR_HASH_KEY_STRING,demo_application); + demo_application->framework = framework; + demo_application->application = mrcp_application_create( + demo_framework_message_handler, + demo_application, + framework->pool); + return mrcp_client_application_register(framework->client,demo_application->application,name); +} + +static void demo_framework_on_start_complete(apt_task_t *task) +{ + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Run Demo Framework"); +} + +static apt_bool_t demo_framework_console_msg_process(demo_framework_t *framework, const char *app_name, const char *profile_name) +{ + demo_application_t *demo_application = apr_hash_get(framework->application_table,app_name,APR_HASH_KEY_STRING); + if(!demo_application) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No Such Demo Application [%s]",app_name); + return FALSE; + } + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Run Demo Application Scenario [%s]",app_name); + return demo_application->run(demo_application,profile_name); +} + +static apt_bool_t demo_framework_msg_process(apt_task_t *task, apt_task_msg_t *msg) +{ + if(msg->type == TASK_MSG_USER) { + framework_task_data_t *data = (framework_task_data_t*)msg->data; + switch(msg->sub_type) { + case DEMO_APPLICATION_MSG_ID: + { + data->demo_application->handler(data->demo_application,data->app_message); + break; + } + case DEMO_CONSOLE_MSG_ID: + { + apt_consumer_task_t *consumer_task = apt_task_object_get(task); + demo_framework_t *framework = apt_consumer_task_object_get(consumer_task); + demo_framework_console_msg_process(framework,data->app_name,data->profile_name); + break; + } + } + } + return TRUE; +} + +static apt_bool_t demo_framework_consumer_task_create(demo_framework_t *framework) +{ + apt_task_t *task; + apt_task_vtable_t *vtable; + apt_task_msg_pool_t *msg_pool; + + msg_pool = apt_task_msg_pool_create_dynamic(sizeof(framework_task_data_t),framework->pool); + framework->task = apt_consumer_task_create(framework,msg_pool,framework->pool); + if(!framework->task) { + return FALSE; + } + task = apt_consumer_task_base_get(framework->task); + apt_task_name_set(task,FRAMEWORK_TASK_NAME); + vtable = apt_consumer_task_vtable_get(framework->task); + if(vtable) { + vtable->process_msg = demo_framework_msg_process; + vtable->on_start_complete = demo_framework_on_start_complete; + } + + return TRUE; +} + +/** Callback is called from MRCP client stack (task) context. + * Signal app_message to the main consumer task of the demo framework + * for further processing (see demo_framework_msg_process). + */ +static apt_bool_t demo_framework_message_handler(const mrcp_app_message_t *app_message) +{ + demo_application_t *demo_application; + if(!app_message->application) { + return FALSE; + } + demo_application = mrcp_application_object_get(app_message->application); + if(demo_application && demo_application->framework) { + demo_framework_t *framework = demo_application->framework; + apt_task_t *task = apt_consumer_task_base_get(framework->task); + apt_task_msg_t *task_msg = apt_task_msg_get(task); + if(task_msg) { + framework_task_data_t *framework_task_data = (framework_task_data_t*)task_msg->data; + task_msg->type = TASK_MSG_USER; + task_msg->sub_type = DEMO_APPLICATION_MSG_ID; + framework_task_data = (framework_task_data_t*) task_msg->data; + framework_task_data->app_message = app_message; + framework_task_data->demo_application = demo_application; + apt_task_msg_signal(task,task_msg); + } + } + return TRUE; +} diff --git a/libs/unimrcp/platforms/unimrcp-client/src/demo_recog_application.c b/libs/unimrcp/platforms/unimrcp-client/src/demo_recog_application.c new file mode 100644 index 0000000000..6c45bc17cb --- /dev/null +++ b/libs/unimrcp/platforms/unimrcp-client/src/demo_recog_application.c @@ -0,0 +1,352 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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. + */ + +/* + * Demo recognizer scenario. + * C -> S: SIP INVITE or RTPS SETUP (add recognizer channel) + * S -> C: SIP OK or RTPS OK + * C -> S: MRCP RECOGNIZE + * S -> C: MRCP IN-PROGRESS + * C -> S: RTP Start Transmission + * S -> C: MRCP START-OF-INPUT + * S -> C: MRCP RECOGNITION-COMPLETE + * C -> S: RTP Stop Transmission + * C -> S: SIP INVITE or RTPS SETUP (optionally remove recognizer channel) + * S -> C: SIP OK or RTPS OK + * C -> S: SIP BYE or RTPS TEARDOWN + * S -> C: SIP OK or RTPS OK + */ + +#include "demo_application.h" +#include "demo_util.h" +#include "mrcp_session.h" +#include "mrcp_message.h" +#include "mrcp_generic_header.h" +#include "mrcp_recog_header.h" +#include "mrcp_recog_resource.h" +#include "apt_log.h" + +#define DEMO_SPEECH_SOURCE_FILE "one.pcm" + +typedef struct recog_app_channel_t recog_app_channel_t; + +/** Declaration of recognizer application channel */ +struct recog_app_channel_t { + /** MRCP control channel */ + mrcp_channel_t *channel; + + /** Streaming is in-progress */ + apt_bool_t streaming; + /** File to read audio stream from */ + FILE *audio_in; + /** Estimated time to complete (used if no audio_in available) */ + apr_size_t time_to_complete; +}; + +/** Declaration of recognizer application methods */ +static apt_bool_t recog_application_run(demo_application_t *demo_application, const char *profile); +static apt_bool_t recog_application_handler(demo_application_t *application, const mrcp_app_message_t *app_message); + +/** Declaration of application message handlers */ +static apt_bool_t recog_application_on_session_update(mrcp_application_t *application, mrcp_session_t *session, mrcp_sig_status_code_e status); +static apt_bool_t recog_application_on_session_terminate(mrcp_application_t *application, mrcp_session_t *session, mrcp_sig_status_code_e status); +static apt_bool_t recog_application_on_channel_add(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_sig_status_code_e status); +static apt_bool_t recog_application_on_channel_remove(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_sig_status_code_e status); +static apt_bool_t recog_application_on_message_receive(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_message_t *message); + +static const mrcp_app_message_dispatcher_t recog_application_dispatcher = { + recog_application_on_session_update, + recog_application_on_session_terminate, + recog_application_on_channel_add, + recog_application_on_channel_remove, + recog_application_on_message_receive +}; + +/** Declaration of recognizer audio stream methods */ +static apt_bool_t recog_app_stream_destroy(mpf_audio_stream_t *stream); +static apt_bool_t recog_app_stream_open(mpf_audio_stream_t *stream); +static apt_bool_t recog_app_stream_close(mpf_audio_stream_t *stream); +static apt_bool_t recog_app_stream_read(mpf_audio_stream_t *stream, mpf_frame_t *frame); + +static const mpf_audio_stream_vtable_t audio_stream_vtable = { + recog_app_stream_destroy, + recog_app_stream_open, + recog_app_stream_close, + recog_app_stream_read, + NULL, + NULL, + NULL +}; + + +/** Create demo recognizer application */ +demo_application_t* demo_recog_application_create(apr_pool_t *pool) +{ + demo_application_t *recog_application = apr_palloc(pool,sizeof(demo_application_t)); + recog_application->application = NULL; + recog_application->framework = NULL; + recog_application->handler = recog_application_handler; + recog_application->run = recog_application_run; + return recog_application; +} + +/** Create demo recognizer channel */ +static mrcp_channel_t* recog_application_channel_create(mrcp_session_t *session) +{ + mrcp_channel_t *channel; + mpf_termination_t *termination; + mpf_codec_descriptor_t *codec_descriptor = NULL; + + /* create channel */ + recog_app_channel_t *recog_channel = apr_palloc(session->pool,sizeof(recog_app_channel_t)); + recog_channel->streaming = FALSE; + recog_channel->audio_in = NULL; + recog_channel->time_to_complete = 0; + +#if 0 /* in case your audio source isn't in linear PCM, create appropriate codec descriptor below */ + codec_descriptor = apr_palloc(session->pool,sizeof(mpf_codec_descriptor_t)); + mpf_codec_descriptor_init(codec_descriptor); + codec_descriptor->channel_count = 1; + codec_descriptor->payload_type = 0; + apt_string_set(&codec_descriptor->name,"PCMU"); + codec_descriptor->sampling_rate = 8000; +#endif + + termination = mrcp_application_source_termination_create( + session, /* session, termination belongs to */ + &audio_stream_vtable, /* virtual methods table of audio stream */ + codec_descriptor, /* codec descriptor of audio stream (NULL by default) */ + recog_channel); /* object to associate */ + + channel = mrcp_application_channel_create( + session, /* session, channel belongs to */ + MRCP_RECOGNIZER_RESOURCE, /* MRCP resource identifier */ + termination, /* media termination, used to terminate audio stream */ + NULL, /* RTP descriptor, used to create RTP termination (NULL by default) */ + recog_channel); /* object to associate */ + return channel; +} + +/** Run demo recognizer scenario */ +static apt_bool_t recog_application_run(demo_application_t *demo_application, const char *profile) +{ + mrcp_channel_t *channel; + /* create session */ + mrcp_session_t *session = mrcp_application_session_create(demo_application->application,profile,NULL); + if(!session) { + return FALSE; + } + + /* create channel and associate all the required data */ + channel = recog_application_channel_create(session); + if(!channel) { + mrcp_application_session_destroy(session); + return FALSE; + } + + /* add channel to session (send asynchronous request) */ + if(mrcp_application_channel_add(session,channel) != TRUE) { + /* session and channel are still not referenced + and both are allocated from session pool and will + be freed with session destroy call */ + mrcp_application_session_destroy(session); + return FALSE; + } + + return TRUE; +} + +/** Handle the messages sent from the MRCP client stack */ +static apt_bool_t recog_application_handler(demo_application_t *application, const mrcp_app_message_t *app_message) +{ + /* app_message should be dispatched now, + * the default dispatcher is used in demo. */ + return mrcp_application_message_dispatch(&recog_application_dispatcher,app_message); +} + +/** Handle the responses sent to session update requests */ +static apt_bool_t recog_application_on_session_update(mrcp_application_t *application, mrcp_session_t *session, mrcp_sig_status_code_e status) +{ + /* not used in demo */ + return TRUE; +} + +/** Handle the responses sent to session terminate requests */ +static apt_bool_t recog_application_on_session_terminate(mrcp_application_t *application, mrcp_session_t *session, mrcp_sig_status_code_e status) +{ + /* received response to session termination request, + now it's safe to destroy no more referenced session */ + mrcp_application_session_destroy(session); + return TRUE; +} + +/** Handle the responses sent to channel add requests */ +static apt_bool_t recog_application_on_channel_add(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_sig_status_code_e status) +{ + if(status == MRCP_SIG_STATUS_CODE_SUCCESS) { + mrcp_message_t *mrcp_message; + const apt_dir_layout_t *dir_layout = mrcp_application_dir_layout_get(application); + /* create and send DEFINE-GRAMMAR request */ + mrcp_message = demo_define_grammar_message_create(session,channel,dir_layout); + if(mrcp_message) { + mrcp_application_message_send(session,channel,mrcp_message); + } + } + else { + /* error case, just terminate the demo */ + mrcp_application_session_terminate(session); + } + return TRUE; +} + +static apt_bool_t recog_application_on_channel_remove(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_sig_status_code_e status) +{ + recog_app_channel_t *recog_channel = mrcp_application_channel_object_get(channel); + + /* terminate the demo */ + mrcp_application_session_terminate(session); + + if(recog_channel) { + FILE *audio_in = recog_channel->audio_in; + if(audio_in) { + recog_channel->audio_in = NULL; + fclose(audio_in); + } + } + return TRUE; +} + +/** Handle the DEFINE-GRAMMAR responses */ +static apt_bool_t recog_application_on_define_grammar(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel) +{ + recog_app_channel_t *recog_channel = mrcp_application_channel_object_get(channel); + mrcp_message_t *mrcp_message; + const apt_dir_layout_t *dir_layout = mrcp_application_dir_layout_get(application); + /* create and send RECOGNIZE request */ + mrcp_message = demo_recognize_message_create(session,channel,dir_layout); + if(mrcp_message) { + mrcp_application_message_send(session,channel,mrcp_message); + } + if(recog_channel) { + char *file_path = apt_datadir_filepath_get(dir_layout,DEMO_SPEECH_SOURCE_FILE,session->pool); + if(file_path) { + recog_channel->audio_in = fopen(file_path,"rb"); + if(recog_channel->audio_in) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Set [%s] as Speech Source",file_path); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Cannot Find [%s]",file_path); + /* set some estimated time to complete */ + recog_channel->time_to_complete = 5000; // 5 sec + } + } + } + return TRUE; +} + +static apt_bool_t recog_application_on_message_receive(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_message_t *message) +{ + recog_app_channel_t *recog_channel = mrcp_application_channel_object_get(channel); + if(message->start_line.message_type == MRCP_MESSAGE_TYPE_RESPONSE) { + /* received MRCP response */ + if(message->start_line.method_id == RECOGNIZER_DEFINE_GRAMMAR) { + /* received the response to DEFINE-GRAMMAR request */ + if(message->start_line.request_state == MRCP_REQUEST_STATE_COMPLETE) { + recog_application_on_define_grammar(application,session,channel); + } + else { + /* received unexpected response, remove channel */ + mrcp_application_channel_remove(session,channel); + } + } + else if(message->start_line.method_id == RECOGNIZER_RECOGNIZE) { + /* received the response to RECOGNIZE request */ + if(message->start_line.request_state == MRCP_REQUEST_STATE_INPROGRESS) { + /* start to stream the speech to recognize */ + if(recog_channel) { + recog_channel->streaming = TRUE; + } + } + else { + /* received unexpected response, remove channel */ + mrcp_application_channel_remove(session,channel); + } + } + else { + /* received unexpected response */ + } + } + else if(message->start_line.message_type == MRCP_MESSAGE_TYPE_EVENT) { + if(message->start_line.method_id == RECOGNIZER_RECOGNITION_COMPLETE) { + if(recog_channel) { + recog_channel->streaming = FALSE; + } + mrcp_application_channel_remove(session,channel); + } + else if(message->start_line.method_id == RECOGNIZER_START_OF_INPUT) { + /* received start-of-input, do whatever you need here */ + } + } + return TRUE; +} + +/** Callback is called from MPF engine context to destroy any additional data associated with audio stream */ +static apt_bool_t recog_app_stream_destroy(mpf_audio_stream_t *stream) +{ + return TRUE; +} + +/** Callback is called from MPF engine context to perform application stream specific action before open */ +static apt_bool_t recog_app_stream_open(mpf_audio_stream_t *stream) +{ + return TRUE; +} + +/** Callback is called from MPF engine context to perform application stream specific action after close */ +static apt_bool_t recog_app_stream_close(mpf_audio_stream_t *stream) +{ + return TRUE; +} + +/** Callback is called from MPF engine context to read new frame */ +static apt_bool_t recog_app_stream_read(mpf_audio_stream_t *stream, mpf_frame_t *frame) +{ + recog_app_channel_t *recog_channel = stream->obj; + if(recog_channel && recog_channel->streaming == TRUE) { + if(recog_channel->audio_in) { + if(fread(frame->codec_frame.buffer,1,frame->codec_frame.size,recog_channel->audio_in) == frame->codec_frame.size) { + /* normal read */ + frame->type |= MEDIA_FRAME_TYPE_AUDIO; + } + else { + /* file is over */ + recog_channel->streaming = FALSE; + } + } + else { + /* fill with silence in case no file available */ + if(recog_channel->time_to_complete >= CODEC_FRAME_TIME_BASE) { + frame->type |= MEDIA_FRAME_TYPE_AUDIO; + memset(frame->codec_frame.buffer,0,frame->codec_frame.size); + recog_channel->time_to_complete -= CODEC_FRAME_TIME_BASE; + } + else { + recog_channel->streaming = FALSE; + } + } + } + return TRUE; +} diff --git a/libs/unimrcp/platforms/unimrcp-client/src/demo_synth_application.c b/libs/unimrcp/platforms/unimrcp-client/src/demo_synth_application.c new file mode 100644 index 0000000000..9aba1adc8c --- /dev/null +++ b/libs/unimrcp/platforms/unimrcp-client/src/demo_synth_application.c @@ -0,0 +1,288 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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. + */ + +/* + * Demo synthesizer scenario. + * C -> S: SIP INVITE or RTPS SETUP (add synthesizer channel) + * S -> C: SIP OK or RTPS OK + * C -> S: MRCP SPEAK + * S -> C: MRCP IN-PROGRESS + * S -> C: RTP Start Transmission + * S -> C: MRCP SPEAK-COMPLETE + * S -> C: RTP Stop Transmission + * C -> S: SIP INVITE or RTPS SETUP (optionally remove synthesizer channel) + * S -> C: SIP OK or RTPS OK + * C -> S: SIP BYE or RTPS TEARDOWN + * S -> C: SIP OK or RTPS OK + */ + +#include "demo_application.h" +#include "demo_util.h" +#include "mrcp_session.h" +#include "mrcp_message.h" +#include "mrcp_generic_header.h" +#include "mrcp_synth_header.h" +#include "mrcp_synth_resource.h" + +typedef struct synth_app_channel_t synth_app_channel_t; + +/** Declaration of synthesizer application channel */ +struct synth_app_channel_t { + /** MRCP control channel */ + mrcp_channel_t *channel; + /** File to write audio stream to */ + FILE *audio_out; +}; + +/** Declaration of synthesizer application methods */ +static apt_bool_t synth_application_run(demo_application_t *demo_application, const char *profile); +static apt_bool_t synth_application_handler(demo_application_t *application, const mrcp_app_message_t *app_message); + +/** Declaration of application message handlers */ +static apt_bool_t synth_application_on_session_update(mrcp_application_t *application, mrcp_session_t *session, mrcp_sig_status_code_e status); +static apt_bool_t synth_application_on_session_terminate(mrcp_application_t *application, mrcp_session_t *session, mrcp_sig_status_code_e status); +static apt_bool_t synth_application_on_channel_add(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_sig_status_code_e status); +static apt_bool_t synth_application_on_channel_remove(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_sig_status_code_e status); +static apt_bool_t synth_application_on_message_receive(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_message_t *message); + +static const mrcp_app_message_dispatcher_t synth_application_dispatcher = { + synth_application_on_session_update, + synth_application_on_session_terminate, + synth_application_on_channel_add, + synth_application_on_channel_remove, + synth_application_on_message_receive +}; + +/** Declaration of synthesizer audio stream methods */ +static apt_bool_t synth_app_stream_destroy(mpf_audio_stream_t *stream); +static apt_bool_t synth_app_stream_open(mpf_audio_stream_t *stream); +static apt_bool_t synth_app_stream_close(mpf_audio_stream_t *stream); +static apt_bool_t synth_app_stream_write(mpf_audio_stream_t *stream, const mpf_frame_t *frame); + +static const mpf_audio_stream_vtable_t audio_stream_vtable = { + synth_app_stream_destroy, + NULL, + NULL, + NULL, + synth_app_stream_open, + synth_app_stream_close, + synth_app_stream_write +}; + + +/** Create demo synthesizer application */ +demo_application_t* demo_synth_application_create(apr_pool_t *pool) +{ + demo_application_t *synth_application = apr_palloc(pool,sizeof(demo_application_t)); + synth_application->application = NULL; + synth_application->framework = NULL; + synth_application->handler = synth_application_handler; + synth_application->run = synth_application_run; + return synth_application; +} + +/** Create demo synthesizer channel */ +static mrcp_channel_t* synth_application_channel_create(mrcp_session_t *session) +{ + mrcp_channel_t *channel; + mpf_termination_t *termination; + mpf_codec_descriptor_t *codec_descriptor = NULL; + + /* create channel */ + synth_app_channel_t *synth_channel = apr_palloc(session->pool,sizeof(synth_app_channel_t)); + synth_channel->audio_out = NULL; + +#if 0 + codec_descriptor = apr_palloc(session->pool,sizeof(mpf_codec_descriptor_t)); + mpf_codec_descriptor_init(codec_descriptor); + codec_descriptor->channel_count = 1; + codec_descriptor->payload_type = 0; + apt_string_set(&codec_descriptor->name,"PCMU"); + codec_descriptor->sampling_rate = 8000; +#endif + + termination = mrcp_application_sink_termination_create( + session, /* session, termination belongs to */ + &audio_stream_vtable, /* virtual methods table of audio stream */ + codec_descriptor, /* codec descriptor of audio stream (NULL by default) */ + synth_channel); /* object to associate */ + + channel = mrcp_application_channel_create( + session, /* session, channel belongs to */ + MRCP_SYNTHESIZER_RESOURCE, /* MRCP resource identifier */ + termination, /* media termination, used to terminate audio stream */ + NULL, /* RTP descriptor, used to create RTP termination (NULL by default) */ + synth_channel); /* object to associate */ + return channel; +} + + +/** Run demo synthesizer scenario */ +static apt_bool_t synth_application_run(demo_application_t *demo_application, const char *profile) +{ + mrcp_channel_t *channel; + /* create session */ + mrcp_session_t *session = mrcp_application_session_create(demo_application->application,profile,NULL); + if(!session) { + return FALSE; + } + + /* create channel and associate all the required data */ + channel = synth_application_channel_create(session); + if(!channel) { + mrcp_application_session_destroy(session); + return FALSE; + } + + /* add channel to session (send asynchronous request) */ + if(mrcp_application_channel_add(session,channel) != TRUE) { + /* session and channel are still not referenced + and both are allocated from session pool and will + be freed with session destroy call */ + mrcp_application_session_destroy(session); + return FALSE; + } + + return TRUE; +} + +/** Handle the messages sent from the MRCP client stack */ +static apt_bool_t synth_application_handler(demo_application_t *application, const mrcp_app_message_t *app_message) +{ + /* app_message should be dispatched now, + * the default dispatcher is used in demo. */ + return mrcp_application_message_dispatch(&synth_application_dispatcher,app_message); +} + +/** Handle the responses sent to session update requests */ +static apt_bool_t synth_application_on_session_update(mrcp_application_t *application, mrcp_session_t *session, mrcp_sig_status_code_e status) +{ + /* not used in demo */ + return TRUE; +} + +/** Handle the responses sent to session terminate requests */ +static apt_bool_t synth_application_on_session_terminate(mrcp_application_t *application, mrcp_session_t *session, mrcp_sig_status_code_e status) +{ + /* received response to session termination request, + now it's safe to destroy no more referenced session */ + mrcp_application_session_destroy(session); + return TRUE; +} + +/** Handle the responses sent to channel add requests */ +static apt_bool_t synth_application_on_channel_add(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_sig_status_code_e status) +{ + synth_app_channel_t *synth_channel = mrcp_application_channel_object_get(channel); + if(status == MRCP_SIG_STATUS_CODE_SUCCESS) { + mrcp_message_t *mrcp_message; + const apt_dir_layout_t *dir_layout = mrcp_application_dir_layout_get(application); + /* create and send SPEAK request */ + mrcp_message = demo_speak_message_create(session,channel,dir_layout); + if(mrcp_message) { + mrcp_application_message_send(session,channel,mrcp_message); + } + + if(synth_channel && session) { + char *file_name = apr_pstrcat(session->pool,"synth-",session->id.buf,".pcm",NULL); + char *file_path = apt_datadir_filepath_get(dir_layout,file_name,session->pool); + if(file_path) { + synth_channel->audio_out = fopen(file_path,"wb"); + } + } + } + else { + /* error case, just terminate the demo */ + mrcp_application_session_terminate(session); + } + return TRUE; +} + +/** Handle the responses sent to channel remove requests */ +static apt_bool_t synth_application_on_channel_remove(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_sig_status_code_e status) +{ + synth_app_channel_t *synth_channel = mrcp_application_channel_object_get(channel); + + /* terminate the demo */ + mrcp_application_session_terminate(session); + + if(synth_channel) { + FILE *audio_out = synth_channel->audio_out; + if(audio_out) { + synth_channel->audio_out = NULL; + fclose(audio_out); + } + } + return TRUE; +} + +/** Handle the MRCP responses/events */ +static apt_bool_t synth_application_on_message_receive(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_message_t *message) +{ + if(message->start_line.message_type == MRCP_MESSAGE_TYPE_RESPONSE) { + /* received MRCP response */ + if(message->start_line.method_id == SYNTHESIZER_SPEAK) { + /* received the response to SPEAK request */ + if(message->start_line.request_state == MRCP_REQUEST_STATE_INPROGRESS) { + /* waiting for SPEAK-COMPLETE event */ + } + else { + /* received unexpected response, remove channel */ + mrcp_application_channel_remove(session,channel); + } + } + else { + /* received unexpected response */ + } + } + else if(message->start_line.message_type == MRCP_MESSAGE_TYPE_EVENT) { + /* received MRCP event */ + if(message->start_line.method_id == SYNTHESIZER_SPEAK_COMPLETE) { + /* received SPEAK-COMPLETE event, remove channel */ + mrcp_application_channel_remove(session,channel); + } + } + return TRUE; +} + +/** Callback is called from MPF engine context to destroy any additional data associated with audio stream */ +static apt_bool_t synth_app_stream_destroy(mpf_audio_stream_t *stream) +{ + /* nothing to destroy in demo */ + return TRUE; +} + +/** Callback is called from MPF engine context to perform application stream specific action before open */ +static apt_bool_t synth_app_stream_open(mpf_audio_stream_t *stream) +{ + return TRUE; +} + +/** Callback is called from MPF engine context to perform application stream specific action after close */ +static apt_bool_t synth_app_stream_close(mpf_audio_stream_t *stream) +{ + return TRUE; +} + +/** Callback is called from MPF engine context to make new frame available to write/send */ +static apt_bool_t synth_app_stream_write(mpf_audio_stream_t *stream, const mpf_frame_t *frame) +{ + synth_app_channel_t *synth_channel = stream->obj; + if(synth_channel && synth_channel->audio_out) { + fwrite(frame->codec_frame.buffer,1,frame->codec_frame.size,synth_channel->audio_out); + } + return TRUE; +} diff --git a/libs/unimrcp/platforms/unimrcp-client/src/demo_util.c b/libs/unimrcp/platforms/unimrcp-client/src/demo_util.c new file mode 100644 index 0000000000..d281885e8d --- /dev/null +++ b/libs/unimrcp/platforms/unimrcp-client/src/demo_util.c @@ -0,0 +1,172 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "demo_util.h" +/* common includes */ +#include "mrcp_session.h" +#include "mrcp_message.h" +#include "mrcp_generic_header.h" +/* synthesizer includes */ +#include "mrcp_synth_header.h" +#include "mrcp_synth_resource.h" +/* recognizer includes */ +#include "mrcp_recog_header.h" +#include "mrcp_recog_resource.h" + +static void demo_message_body_set(mrcp_message_t *mrcp_message, const apt_dir_layout_t *dir_layout, const char *file_name) +{ + char *file_path = apt_datadir_filepath_get(dir_layout,file_name,mrcp_message->pool); + if(file_path) { + FILE *file = fopen(file_path,"r"); + if(file) { + char text[1024]; + apr_size_t size; + size = fread(text,1,sizeof(text),file); + apt_string_assign_n(&mrcp_message->body,text,size,mrcp_message->pool); + fclose(file); + } + } +} + +/** Create demo MRCP message (SPEAK request) */ +mrcp_message_t* demo_speak_message_create(mrcp_session_t *session, mrcp_channel_t *channel, const apt_dir_layout_t *dir_layout) +{ + /* create MRCP message */ + mrcp_message_t *mrcp_message = mrcp_application_message_create(session,channel,SYNTHESIZER_SPEAK); + if(mrcp_message) { + mrcp_generic_header_t *generic_header; + mrcp_synth_header_t *synth_header; + /* get/allocate generic header */ + generic_header = mrcp_generic_header_prepare(mrcp_message); + if(generic_header) { + /* set generic header fields */ + apt_string_assign(&generic_header->content_type,"application/synthesis+ssml",mrcp_message->pool); + mrcp_generic_header_property_add(mrcp_message,GENERIC_HEADER_CONTENT_TYPE); + } + /* get/allocate synthesizer header */ + synth_header = mrcp_resource_header_prepare(mrcp_message); + if(synth_header) { + /* set synthesizer header fields */ + synth_header->voice_param.age = 28; + mrcp_resource_header_property_add(mrcp_message,SYNTHESIZER_HEADER_VOICE_AGE); + } + /* set message body */ + demo_message_body_set(mrcp_message,dir_layout,"speak.xml"); + } + return mrcp_message; +} + +/** Create demo MRCP message (DEFINE-GRAMMAR request) */ +mrcp_message_t* demo_define_grammar_message_create(mrcp_session_t *session, mrcp_channel_t *channel, const apt_dir_layout_t *dir_layout) +{ + /* create MRCP message */ + mrcp_message_t *mrcp_message = mrcp_application_message_create(session,channel,RECOGNIZER_DEFINE_GRAMMAR); + if(mrcp_message) { + mrcp_generic_header_t *generic_header; + /* get/allocate generic header */ + generic_header = mrcp_generic_header_prepare(mrcp_message); + if(generic_header) { + /* set generic header fields */ + if(mrcp_message->start_line.version == MRCP_VERSION_2) { + apt_string_assign(&generic_header->content_type,"application/srgs+xml",mrcp_message->pool); + } + else { + apt_string_assign(&generic_header->content_type,"application/grammar+xml",mrcp_message->pool); + } + mrcp_generic_header_property_add(mrcp_message,GENERIC_HEADER_CONTENT_TYPE); + apt_string_assign(&generic_header->content_id,"request1@form-level.store",mrcp_message->pool); + mrcp_generic_header_property_add(mrcp_message,GENERIC_HEADER_CONTENT_ID); + } + /* set message body */ + demo_message_body_set(mrcp_message,dir_layout,"grammar.xml"); + } + return mrcp_message; +} + +/** Create demo MRCP message (RECOGNIZE request) */ +mrcp_message_t* demo_recognize_message_create(mrcp_session_t *session, mrcp_channel_t *channel, const apt_dir_layout_t *dir_layout) +{ + const char text[] = "session:request1@form-level.store"; + + /* create MRCP message */ + mrcp_message_t *mrcp_message = mrcp_application_message_create(session,channel,RECOGNIZER_RECOGNIZE); + if(mrcp_message) { + mrcp_recog_header_t *recog_header; + mrcp_generic_header_t *generic_header; + /* get/allocate generic header */ + generic_header = mrcp_generic_header_prepare(mrcp_message); + if(generic_header) { + /* set generic header fields */ + apt_string_assign(&generic_header->content_type,"text/uri-list",mrcp_message->pool); + mrcp_generic_header_property_add(mrcp_message,GENERIC_HEADER_CONTENT_TYPE); + } + /* get/allocate recognizer header */ + recog_header = mrcp_resource_header_prepare(mrcp_message); + if(recog_header) { + if(mrcp_message->start_line.version == MRCP_VERSION_2) { + /* set recognizer header fields */ + recog_header->cancel_if_queue = FALSE; + mrcp_resource_header_property_add(mrcp_message,RECOGNIZER_HEADER_CANCEL_IF_QUEUE); + } + recog_header->no_input_timeout = 5000; + mrcp_resource_header_property_add(mrcp_message,RECOGNIZER_HEADER_NO_INPUT_TIMEOUT); + recog_header->recognition_timeout = 10000; + mrcp_resource_header_property_add(mrcp_message,RECOGNIZER_HEADER_RECOGNITION_TIMEOUT); + recog_header->start_input_timers = TRUE; + mrcp_resource_header_property_add(mrcp_message,RECOGNIZER_HEADER_START_INPUT_TIMERS); + } + /* set message body */ + apt_string_assign(&mrcp_message->body,text,mrcp_message->pool); + } + return mrcp_message; +} + + +/** Create demo RTP termination descriptor */ +mpf_rtp_termination_descriptor_t* demo_rtp_descriptor_create(apr_pool_t *pool) +{ + mpf_codec_descriptor_t *codec_descriptor; + mpf_rtp_media_descriptor_t *media; + /* create rtp descriptor */ + mpf_rtp_termination_descriptor_t *rtp_descriptor = apr_palloc(pool,sizeof(mpf_rtp_termination_descriptor_t)); + mpf_rtp_termination_descriptor_init(rtp_descriptor); + /* create rtp local media */ + media = apr_palloc(pool,sizeof(mpf_rtp_media_descriptor_t)); + mpf_rtp_media_descriptor_init(media); + apt_string_assign(&media->base.ip,"127.0.0.1",pool); + media->base.port = 6000; + media->base.state = MPF_MEDIA_ENABLED; + media->mode = STREAM_MODE_RECEIVE; + + /* initialize codec list */ + mpf_codec_list_init(&media->codec_list,2,pool); + /* set codec descriptor */ + codec_descriptor = mpf_codec_list_add(&media->codec_list); + if(codec_descriptor) { + codec_descriptor->payload_type = 0; + } + /* set another codec descriptor */ + codec_descriptor = mpf_codec_list_add(&media->codec_list); + if(codec_descriptor) { + codec_descriptor->payload_type = 96; + apt_string_set(&codec_descriptor->name,"PCMU"); + codec_descriptor->sampling_rate = 16000; + codec_descriptor->channel_count = 1; + } + + rtp_descriptor->audio.local = media; + return rtp_descriptor; +} diff --git a/libs/unimrcp/platforms/unimrcp-client/src/main.c b/libs/unimrcp/platforms/unimrcp-client/src/main.c new file mode 100644 index 0000000000..6a242ca4cd --- /dev/null +++ b/libs/unimrcp/platforms/unimrcp-client/src/main.c @@ -0,0 +1,229 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include +#include +#include +#include "demo_framework.h" +#include "apt_pool.h" +#include "apt_log.h" + +typedef struct { + const char *root_dir_path; + apt_log_priority_e log_priority; + apt_log_output_e log_output; +} client_options_t; + +static apt_bool_t demo_framework_cmdline_process(demo_framework_t *framework, char *cmdline) +{ + apt_bool_t running = TRUE; + char *name; + char *last; + name = apr_strtok(cmdline, " ", &last); + + if(strcasecmp(name,"run") == 0) { + char *app_name = apr_strtok(NULL, " ", &last); + if(app_name) { + char *profile_name = apr_strtok(NULL, " ", &last); + if(!profile_name) { + profile_name = "MRCPv2-Default"; + } + demo_framework_app_run(framework,app_name,profile_name); + } + } + else if(strcasecmp(name,"loglevel") == 0) { + char *priority = apr_strtok(NULL, " ", &last); + if(priority) { + apt_log_priority_set(atol(priority)); + } + } + else if(strcasecmp(name,"exit") == 0 || strcmp(name,"quit") == 0) { + running = FALSE; + } + else if(strcasecmp(name,"help") == 0) { + printf("usage:\n" + "\n- run [app_name] [profile_name] (run demo application)\n" + " app_name is one of 'synth', 'recog', 'bypass', 'discover'\n" + " profile_name is one of 'MRCPv2-Default', 'MRCPv1-Default', ...\n" + "\n examples: \n" + " run synth\n" + " run recog\n" + " run synth MRCPv1-Default\n" + " run recog MRCPv1-Default\n" + "\n- loglevel [level] (set loglevel, one of 0,1...7)\n" + "\n- quit, exit\n"); + } + else { + printf("unknown command: %s (input help for usage)\n",name); + } + return running; +} + +static apt_bool_t demo_framework_cmdline_run(demo_framework_t *framework) +{ + apt_bool_t running = TRUE; + char cmdline[1024]; + int i; + do { + printf(">"); + memset(&cmdline, 0, sizeof(cmdline)); + for(i = 0; i < sizeof(cmdline); i++) { + cmdline[i] = (char) getchar(); + if(cmdline[i] == '\n') { + cmdline[i] = '\0'; + break; + } + } + if(*cmdline) { + running = demo_framework_cmdline_process(framework,cmdline); + } + } + while(running != 0); + return TRUE; +} + +static void usage() +{ + printf( + "\n" + "Usage:\n" + "\n" + " unimrcpclient [options]\n" + "\n" + " Available options:\n" + "\n" + " -r [--root-dir] path : Set the project root directory path.\n" + "\n" + " -l [--log-prio] priority : Set the log priority.\n" + " (0-emergency, ..., 7-debug)\n" + "\n" + " -o [--log-output] mode : Set the log output mode.\n" + " (0-none, 1-console only, 2-file only, 3-both)\n" + "\n" + " -h [--help] : Show the help.\n" + "\n"); +} + +static apt_bool_t demo_framework_options_load(client_options_t *options, int argc, const char * const *argv, apr_pool_t *pool) +{ + apr_status_t rv; + apr_getopt_t *opt = NULL; + int optch; + const char *optarg; + + const apr_getopt_option_t opt_option[] = { + /* long-option, short-option, has-arg flag, description */ + { "root-dir", 'r', TRUE, "path to root dir" }, /* -r arg or --root-dir arg */ + { "log-prio", 'l', TRUE, "log priority" }, /* -l arg or --log-prio arg */ + { "log-output", 'o', TRUE, "log output mode" }, /* -o arg or --log-output arg */ + { "help", 'h', FALSE, "show help" }, /* -h or --help */ + { NULL, 0, 0, NULL }, /* end */ + }; + + rv = apr_getopt_init(&opt, pool , argc, argv); + if(rv != APR_SUCCESS) { + return FALSE; + } + + while((rv = apr_getopt_long(opt, opt_option, &optch, &optarg)) == APR_SUCCESS) { + switch(optch) { + case 'r': + options->root_dir_path = optarg; + break; + case 'l': + if(optarg) { + options->log_priority = atoi(optarg); + } + break; + case 'o': + if(optarg) { + options->log_output = atoi(optarg); + } + break; + case 'h': + usage(); + return FALSE; + } + } + + if(rv != APR_EOF) { + usage(); + return FALSE; + } + + return TRUE; +} + +int main(int argc, const char * const *argv) +{ + apr_pool_t *pool = NULL; + client_options_t options; + apt_dir_layout_t *dir_layout; + demo_framework_t *framework; + + /* APR global initialization */ + if(apr_initialize() != APR_SUCCESS) { + apr_terminate(); + return 0; + } + + /* create APR pool */ + pool = apt_pool_create(); + if(!pool) { + apr_terminate(); + return 0; + } + + /* set the default options */ + options.root_dir_path = "../"; + options.log_priority = APT_PRIO_INFO; + options.log_output = APT_LOG_OUTPUT_CONSOLE; + + /* load options */ + if(demo_framework_options_load(&options,argc,argv,pool) != TRUE) { + apr_pool_destroy(pool); + apr_terminate(); + return 0; + } + + /* create the structure of default directories layout */ + dir_layout = apt_default_dir_layout_create(options.root_dir_path,pool); + /* create singleton logger */ + apt_log_instance_create(options.log_output,options.log_priority,pool); + + if((options.log_output & APT_LOG_OUTPUT_FILE) == APT_LOG_OUTPUT_FILE) { + /* open the log file */ + apt_log_file_open(dir_layout->log_dir_path,"unimrcpclient",MAX_LOG_FILE_SIZE,MAX_LOG_FILE_COUNT,pool); + } + + /* create demo framework */ + framework = demo_framework_create(dir_layout); + if(framework) { + /* run command line */ + demo_framework_cmdline_run(framework); + /* destroy demo framework */ + demo_framework_destroy(framework); + } + + /* destroy singleton logger */ + apt_log_instance_destroy(); + /* destroy APR pool */ + apr_pool_destroy(pool); + /* APR global termination */ + apr_terminate(); + return 0; +} diff --git a/libs/unimrcp/platforms/unimrcp-client/unimrcpclient.vcproj b/libs/unimrcp/platforms/unimrcp-client/unimrcpclient.vcproj new file mode 100644 index 0000000000..eb42c462f8 --- /dev/null +++ b/libs/unimrcp/platforms/unimrcp-client/unimrcpclient.vcproj @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/platforms/unimrcp-server/Makefile.am b/libs/unimrcp/platforms/unimrcp-server/Makefile.am new file mode 100644 index 0000000000..284c63a41d --- /dev/null +++ b/libs/unimrcp/platforms/unimrcp-server/Makefile.am @@ -0,0 +1,19 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -Iinclude \ + -I$(top_srcdir)/platforms/libunimrcp-server/include \ + -I$(top_srcdir)/libs/mrcp-server/include \ + -I$(top_srcdir)/libs/mrcp-engine/include \ + -I$(top_srcdir)/libs/mrcp-signaling/include \ + -I$(top_srcdir)/libs/mrcpv2-transport/include \ + -I$(top_srcdir)/libs/mrcp/include \ + -I$(top_srcdir)/libs/mrcp/message/include \ + -I$(top_srcdir)/libs/mrcp/control/include \ + -I$(top_srcdir)/libs/mrcp/resources/include \ + -I$(top_srcdir)/libs/mpf/include \ + -I$(top_srcdir)/libs/apr-toolkit/include \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) + +bin_PROGRAMS = unimrcpserver +unimrcpserver_SOURCES = src/main.c src/uni_cmdline.c src/uni_daemon.c +unimrcpserver_LDADD = $(top_builddir)/platforms/libunimrcp-server/libunimrcpserver.la diff --git a/libs/unimrcp/platforms/unimrcp-server/src/main.c b/libs/unimrcp/platforms/unimrcp-server/src/main.c new file mode 100644 index 0000000000..eb283f957c --- /dev/null +++ b/libs/unimrcp/platforms/unimrcp-server/src/main.c @@ -0,0 +1,198 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include +#include +#include "apt_pool.h" +#include "apt_dir_layout.h" +#include "apt_log.h" + +typedef struct { + const char *root_dir_path; + apt_bool_t foreground; + apt_log_priority_e log_priority; + apt_log_output_e log_output; +} server_options_t; + +#ifdef WIN32 +apt_bool_t uni_service_run(apt_dir_layout_t *dir_layout, apr_pool_t *pool); +#else +apt_bool_t uni_daemon_run(apt_dir_layout_t *dir_layout, apr_pool_t *pool); +#endif + +apt_bool_t uni_cmdline_run(apt_dir_layout_t *dir_layout, apr_pool_t *pool); + + +static void usage() +{ + printf( + "\n" + "Usage:\n" + "\n" + " unimrcpserver [options]\n" + "\n" + " Available options:\n" + "\n" + " -r [--root-dir] path : Set the project root directory path.\n" + "\n" + " -l [--log-prio] priority : Set the log priority.\n" + " (0-emergency, ..., 7-debug)\n" + "\n" + " -o [--log-output] mode : Set the log output mode.\n" + " (0-none, 1-console only, 2-file only, 3-both)\n" + "\n" +#ifdef WIN32 + " -s [--service] : Run as the Windows service.\n" + "\n" +#else + " -d [--daemon] : Run as the daemon.\n" + "\n" +#endif + " -h [--help] : Show the help.\n" + "\n"); +} + +static apt_bool_t options_load(server_options_t *options, int argc, const char * const *argv, apr_pool_t *pool) +{ + apr_status_t rv; + apr_getopt_t *opt = NULL; + int optch; + const char *optarg; + + const apr_getopt_option_t opt_option[] = { + /* long-option, short-option, has-arg flag, description */ + { "root-dir", 'r', TRUE, "path to root dir" }, /* -r arg or --root-dir arg */ + { "log-prio", 'l', TRUE, "log priority" }, /* -l arg or --log-prio arg */ + { "log-output", 'o', TRUE, "log output mode" }, /* -o arg or --log-output arg */ +#ifdef WIN32 + { "service", 's', FALSE, "run as service" }, /* -s or --service */ +#else + { "daemon", 'd', FALSE, "start as daemon" }, /* -d or --daemon */ +#endif + { "help", 'h', FALSE, "show help" }, /* -h or --help */ + { NULL, 0, 0, NULL }, /* end */ + }; + + rv = apr_getopt_init(&opt, pool , argc, argv); + if(rv != APR_SUCCESS) { + return FALSE; + } + + while((rv = apr_getopt_long(opt, opt_option, &optch, &optarg)) == APR_SUCCESS) { + switch(optch) { + case 'r': + options->root_dir_path = optarg; + break; + case 'l': + if(optarg) { + options->log_priority = atoi(optarg); + } + break; + case 'o': + if(optarg) { + options->log_output = atoi(optarg); + } + break; +#ifdef WIN32 + case 's': + options->foreground = FALSE; + break; +#else + case 'd': + options->foreground = FALSE; + break; +#endif + case 'h': + usage(); + return FALSE; + } + } + + if(rv != APR_EOF) { + usage(); + return FALSE; + } + + return TRUE; +} + +int main(int argc, const char * const *argv) +{ + apr_pool_t *pool = NULL; + server_options_t options; + apt_dir_layout_t *dir_layout; + + /* APR global initialization */ + if(apr_initialize() != APR_SUCCESS) { + apr_terminate(); + return 0; + } + + /* create APR pool */ + pool = apt_pool_create(); + if(!pool) { + apr_terminate(); + return 0; + } + + /* set the default options */ + options.root_dir_path = "../"; + options.foreground = TRUE; + options.log_priority = APT_PRIO_INFO; + options.log_output = APT_LOG_OUTPUT_CONSOLE; + + /* load options */ + if(options_load(&options,argc,argv,pool) != TRUE) { + apr_pool_destroy(pool); + apr_terminate(); + return 0; + } + + /* create the structure of default directories layout */ + dir_layout = apt_default_dir_layout_create(options.root_dir_path,pool); + /* create singleton logger */ + apt_log_instance_create(options.log_output,options.log_priority,pool); + + if((options.log_output & APT_LOG_OUTPUT_FILE) == APT_LOG_OUTPUT_FILE) { + /* open the log file */ + apt_log_file_open(dir_layout->log_dir_path,"unimrcpserver",MAX_LOG_FILE_SIZE,MAX_LOG_FILE_COUNT,pool); + } + + if(options.foreground == TRUE) { + /* run command line */ + uni_cmdline_run(dir_layout,pool); + } +#ifdef WIN32 + else { + /* run as windows service */ + uni_service_run(dir_layout,pool); + } +#else + else { + /* run as daemon */ + uni_daemon_run(dir_layout,pool); + } +#endif + + /* destroy singleton logger */ + apt_log_instance_destroy(); + /* destroy APR pool */ + apr_pool_destroy(pool); + /* APR global termination */ + apr_terminate(); + return 0; +} diff --git a/libs/unimrcp/platforms/unimrcp-server/src/uni_cmdline.c b/libs/unimrcp/platforms/unimrcp-server/src/uni_cmdline.c new file mode 100644 index 0000000000..bac1d6685e --- /dev/null +++ b/libs/unimrcp/platforms/unimrcp-server/src/uni_cmdline.c @@ -0,0 +1,81 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include +#include "unimrcp_server.h" +#include "apt_log.h" + +static apt_bool_t cmdline_process(char *cmdline) +{ + apt_bool_t running = TRUE; + char *name; + char *last; + name = apr_strtok(cmdline, " ", &last); + + if(strcasecmp(name,"loglevel") == 0) { + char *priority = apr_strtok(NULL, " ", &last); + if(priority) { + apt_log_priority_set(atol(priority)); + } + } + else if(strcasecmp(name,"exit") == 0 || strcmp(name,"quit") == 0) { + running = FALSE; + } + else if(strcasecmp(name,"help") == 0) { + printf("usage:\n"); + printf("- loglevel [level] (set loglevel, one of 0,1...7)\n"); + printf("- quit, exit\n"); + } + else { + printf("unknown command: %s (input help for usage)\n",name); + } + return running; +} + +apt_bool_t uni_cmdline_run(apt_dir_layout_t *dir_layout, apr_pool_t *pool) +{ + apt_bool_t running = TRUE; + char cmdline[1024]; + int i; + mrcp_server_t *server; + + /* start server */ + server = unimrcp_server_start(dir_layout); + if(!server) { + return FALSE; + } + + do { + printf(">"); + memset(&cmdline, 0, sizeof(cmdline)); + for(i = 0; i < sizeof(cmdline); i++) { + cmdline[i] = (char) getchar(); + if(cmdline[i] == '\n') { + cmdline[i] = '\0'; + break; + } + } + if(*cmdline) { + running = cmdline_process(cmdline); + } + } + while(running != 0); + + /* shutdown server */ + unimrcp_server_shutdown(server); + return TRUE; +} diff --git a/libs/unimrcp/platforms/unimrcp-server/src/uni_daemon.c b/libs/unimrcp/platforms/unimrcp-server/src/uni_daemon.c new file mode 100644 index 0000000000..ee7287009b --- /dev/null +++ b/libs/unimrcp/platforms/unimrcp-server/src/uni_daemon.c @@ -0,0 +1,50 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include +#include "unimrcp_server.h" +#include "apt_log.h" + +static apt_bool_t daemon_running; + +static void sigterm_handler(int signo) +{ + daemon_running = FALSE; +} + +apt_bool_t uni_daemon_run(apt_dir_layout_t *dir_layout, apr_pool_t *pool) +{ + mrcp_server_t *server; + + daemon_running = TRUE; + apr_signal(SIGTERM,sigterm_handler); + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Run as Daemon"); + apr_proc_detach(APR_PROC_DETACH_DAEMONIZE); + + /* start server */ + server = unimrcp_server_start(dir_layout); + if(!server) { + return FALSE; + } + + while(daemon_running) apr_sleep(1000000); + + /* shutdown server */ + unimrcp_server_shutdown(server); + return TRUE; +} diff --git a/libs/unimrcp/platforms/unimrcp-server/src/uni_service.c b/libs/unimrcp/platforms/unimrcp-server/src/uni_service.c new file mode 100644 index 0000000000..9b597d88d9 --- /dev/null +++ b/libs/unimrcp/platforms/unimrcp-server/src/uni_service.c @@ -0,0 +1,104 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include +#include "unimrcp_server.h" +#include "apt_log.h" + +#define WIN_SERVICE_NAME "unimrcp" + +static SERVICE_STATUS_HANDLE win_service_status_handle = NULL; +static SERVICE_STATUS win_service_status; + +static mrcp_server_t *server = NULL; +static apt_dir_layout_t *service_dir_layout = NULL; + +/** SCM state change handler */ +static void WINAPI win_service_handler(DWORD control) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Service Handler %d",control); + switch (control) + { + case SERVICE_CONTROL_INTERROGATE: + break; + case SERVICE_CONTROL_SHUTDOWN: + case SERVICE_CONTROL_STOP: + if(server) { + win_service_status.dwCurrentState = SERVICE_STOP_PENDING; + if(!SetServiceStatus(win_service_status_handle, &win_service_status)) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Set Service Status %d",GetLastError()); + } + + /* shutdown server */ + unimrcp_server_shutdown(server); + } + win_service_status.dwCurrentState = SERVICE_STOPPED; + win_service_status.dwCheckPoint = 0; + win_service_status.dwWaitHint = 0; + break; + } + + if(!SetServiceStatus(win_service_status_handle, &win_service_status)) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Set Service Status %d",GetLastError()); + } +} + +static void WINAPI win_service_main(DWORD argc, LPTSTR *argv) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Service Main"); + win_service_status.dwServiceType = SERVICE_WIN32; + win_service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP; + win_service_status.dwWin32ExitCode = 0; + win_service_status.dwServiceSpecificExitCode = 0; + win_service_status.dwCheckPoint = 0; + win_service_status.dwWaitHint = 0; + + win_service_status_handle = RegisterServiceCtrlHandler(WIN_SERVICE_NAME, win_service_handler); + if(win_service_status_handle == (SERVICE_STATUS_HANDLE)0) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Register Service Control Handler %d",GetLastError()); + return; + } + + win_service_status.dwCurrentState = SERVICE_START_PENDING; + if(!SetServiceStatus(win_service_status_handle, &win_service_status)) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Set Service Status %d",GetLastError()); + } + + /* start server */ + server = unimrcp_server_start(service_dir_layout); + + win_service_status.dwCurrentState = server ? SERVICE_RUNNING : SERVICE_STOPPED; + if(!SetServiceStatus(win_service_status_handle, &win_service_status)) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Set Service Status %d",GetLastError()); + } +} + +/** Run SCM service */ +apt_bool_t uni_service_run(apt_dir_layout_t *dir_layout, apr_pool_t *pool) +{ + SERVICE_TABLE_ENTRY win_service_table[] = { + { WIN_SERVICE_NAME, win_service_main }, + { NULL, NULL } + }; + + service_dir_layout = dir_layout; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Run as Service"); + if(!StartServiceCtrlDispatcher(win_service_table)) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Connect to SCM %d",GetLastError()); + } + return TRUE; +} diff --git a/libs/unimrcp/platforms/unimrcp-server/unimrcpserver.vcproj b/libs/unimrcp/platforms/unimrcp-server/unimrcpserver.vcproj new file mode 100644 index 0000000000..68fb5d3dba --- /dev/null +++ b/libs/unimrcp/platforms/unimrcp-server/unimrcpserver.vcproj @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/plugins/Makefile.am b/libs/unimrcp/plugins/Makefile.am new file mode 100644 index 0000000000..ff08fd3be7 --- /dev/null +++ b/libs/unimrcp/plugins/Makefile.am @@ -0,0 +1,15 @@ +MAINTAINERCLEANFILES = Makefile.in + +SUBDIRS = + +if DEMOSYNTH_PLUGIN +SUBDIRS += demo-synth +endif + +if DEMORECOG_PLUGIN +SUBDIRS += demo-recog +endif + +if CEPSTRAL_PLUGIN +SUBDIRS += mrcp-cepstral +endif diff --git a/libs/unimrcp/plugins/demo-recog/Makefile.am b/libs/unimrcp/plugins/demo-recog/Makefile.am new file mode 100644 index 0000000000..ebf041f36b --- /dev/null +++ b/libs/unimrcp/plugins/demo-recog/Makefile.am @@ -0,0 +1,16 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -Iinclude \ + -I$(top_srcdir)/libs/mrcp-engine/include \ + -I$(top_srcdir)/libs/mrcp/include \ + -I$(top_srcdir)/libs/mrcp/message/include \ + -I$(top_srcdir)/libs/mrcp/control/include \ + -I$(top_srcdir)/libs/mrcp/resources/include \ + -I$(top_srcdir)/libs/mpf/include \ + -I$(top_srcdir)/libs/apr-toolkit/include \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) + +plugin_LTLIBRARIES = demorecog.la + +demorecog_la_SOURCES = src/demo_recog_engine.c +demorecog_la_LDFLAGS = -module $(PLUGIN_LT_VERSION) diff --git a/libs/unimrcp/plugins/demo-recog/demorecog.vcproj b/libs/unimrcp/plugins/demo-recog/demorecog.vcproj new file mode 100644 index 0000000000..5c8c6e597d --- /dev/null +++ b/libs/unimrcp/plugins/demo-recog/demorecog.vcproj @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/plugins/demo-recog/src/demo_recog_engine.c b/libs/unimrcp/plugins/demo-recog/src/demo_recog_engine.c new file mode 100644 index 0000000000..72a86a5c3e --- /dev/null +++ b/libs/unimrcp/plugins/demo-recog/src/demo_recog_engine.c @@ -0,0 +1,508 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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. + */ + +/* + * Some mandatory rules for plugin implementation. + * 1. Each plugin MUST contain the following function as an entry point of the plugin + * MRCP_PLUGIN_DECLARE(mrcp_resource_engine_t*) mrcp_plugin_create(apr_pool_t *pool) + * 2. One and only one response MUST be sent back to the received request. + * 3. Methods (callbacks) of the MRCP engine channel MUST not block. + * (asynch response can be sent from the context of other thread) + * 4. Methods (callbacks) of the MPF engine stream MUST not block. + */ + +#include "mrcp_resource_engine.h" +#include "mrcp_recog_resource.h" +#include "mrcp_recog_header.h" +#include "mrcp_generic_header.h" +#include "mrcp_message.h" +#include "mpf_activity_detector.h" +#include "apt_consumer_task.h" +#include "apt_log.h" + +#define RECOG_ENGINE_TASK_NAME "Demo Recog Engine" + +typedef struct demo_recog_engine_t demo_recog_engine_t; +typedef struct demo_recog_channel_t demo_recog_channel_t; +typedef struct demo_recog_msg_t demo_recog_msg_t; + +/** Declaration of recognizer engine methods */ +static apt_bool_t demo_recog_engine_destroy(mrcp_resource_engine_t *engine); +static apt_bool_t demo_recog_engine_open(mrcp_resource_engine_t *engine); +static apt_bool_t demo_recog_engine_close(mrcp_resource_engine_t *engine); +static mrcp_engine_channel_t* demo_recog_engine_channel_create(mrcp_resource_engine_t *engine, apr_pool_t *pool); + +static const struct mrcp_engine_method_vtable_t engine_vtable = { + demo_recog_engine_destroy, + demo_recog_engine_open, + demo_recog_engine_close, + demo_recog_engine_channel_create +}; + + +/** Declaration of recognizer channel methods */ +static apt_bool_t demo_recog_channel_destroy(mrcp_engine_channel_t *channel); +static apt_bool_t demo_recog_channel_open(mrcp_engine_channel_t *channel); +static apt_bool_t demo_recog_channel_close(mrcp_engine_channel_t *channel); +static apt_bool_t demo_recog_channel_request_process(mrcp_engine_channel_t *channel, mrcp_message_t *request); + +static const struct mrcp_engine_channel_method_vtable_t channel_vtable = { + demo_recog_channel_destroy, + demo_recog_channel_open, + demo_recog_channel_close, + demo_recog_channel_request_process +}; + +/** Declaration of recognizer audio stream methods */ +static apt_bool_t demo_recog_stream_destroy(mpf_audio_stream_t *stream); +static apt_bool_t demo_recog_stream_open(mpf_audio_stream_t *stream); +static apt_bool_t demo_recog_stream_close(mpf_audio_stream_t *stream); +static apt_bool_t demo_recog_stream_write(mpf_audio_stream_t *stream, const mpf_frame_t *frame); + +static const mpf_audio_stream_vtable_t audio_stream_vtable = { + demo_recog_stream_destroy, + NULL, + NULL, + NULL, + demo_recog_stream_open, + demo_recog_stream_close, + demo_recog_stream_write +}; + +/** Declaration of demo recognizer engine */ +struct demo_recog_engine_t { + apt_consumer_task_t *task; +}; + +/** Declaration of demo recognizer channel */ +struct demo_recog_channel_t { + /** Back pointer to engine */ + demo_recog_engine_t *demo_engine; + /** Engine channel base */ + mrcp_engine_channel_t *channel; + + /** Active (in-progress) recognition request */ + mrcp_message_t *recog_request; + /** Pending stop response */ + mrcp_message_t *stop_response; + /** Indicates whether input timers are started */ + apt_bool_t timers_started; + /** Voice activity detector */ + mpf_activity_detector_t *detector; + /** File to write utterance to */ + FILE *audio_out; +}; + +typedef enum { + DEMO_RECOG_MSG_OPEN_CHANNEL, + DEMO_RECOG_MSG_CLOSE_CHANNEL, + DEMO_RECOG_MSG_REQUEST_PROCESS +} demo_recog_msg_type_e; + +/** Declaration of demo recognizer task message */ +struct demo_recog_msg_t { + demo_recog_msg_type_e type; + mrcp_engine_channel_t *channel; + mrcp_message_t *request; +}; + +static apt_bool_t demo_recog_msg_signal(demo_recog_msg_type_e type, mrcp_engine_channel_t *channel, mrcp_message_t *request); +static apt_bool_t demo_recog_msg_process(apt_task_t *task, apt_task_msg_t *msg); + +/** Declare this macro to use log routine of the server, plugin is loaded from */ +MRCP_PLUGIN_LOGGER_IMPLEMENT + +/** Create demo recognizer engine */ +MRCP_PLUGIN_DECLARE(mrcp_resource_engine_t*) mrcp_plugin_create(apr_pool_t *pool) +{ + demo_recog_engine_t *demo_engine = apr_palloc(pool,sizeof(demo_recog_engine_t)); + apt_task_t *task; + apt_task_vtable_t *vtable; + apt_task_msg_pool_t *msg_pool; + + msg_pool = apt_task_msg_pool_create_dynamic(sizeof(demo_recog_msg_t),pool); + demo_engine->task = apt_consumer_task_create(demo_engine,msg_pool,pool); + if(!demo_engine->task) { + return NULL; + } + task = apt_consumer_task_base_get(demo_engine->task); + apt_task_name_set(task,RECOG_ENGINE_TASK_NAME); + vtable = apt_task_vtable_get(task); + if(vtable) { + vtable->process_msg = demo_recog_msg_process; + } + + /* create resource engine base */ + return mrcp_resource_engine_create( + MRCP_RECOGNIZER_RESOURCE, /* MRCP resource identifier */ + demo_engine, /* object to associate */ + &engine_vtable, /* virtual methods table of resource engine */ + pool); /* pool to allocate memory from */ +} + +/** Destroy recognizer engine */ +static apt_bool_t demo_recog_engine_destroy(mrcp_resource_engine_t *engine) +{ + demo_recog_engine_t *demo_engine = engine->obj; + if(demo_engine->task) { + apt_task_t *task = apt_consumer_task_base_get(demo_engine->task); + apt_task_destroy(task); + demo_engine->task = NULL; + } + return TRUE; +} + +/** Open recognizer engine */ +static apt_bool_t demo_recog_engine_open(mrcp_resource_engine_t *engine) +{ + demo_recog_engine_t *demo_engine = engine->obj; + if(demo_engine->task) { + apt_task_t *task = apt_consumer_task_base_get(demo_engine->task); + apt_task_start(task); + } + return TRUE; +} + +/** Close recognizer engine */ +static apt_bool_t demo_recog_engine_close(mrcp_resource_engine_t *engine) +{ + demo_recog_engine_t *demo_engine = engine->obj; + if(demo_engine->task) { + apt_task_t *task = apt_consumer_task_base_get(demo_engine->task); + apt_task_terminate(task,TRUE); + } + return TRUE; +} + +static mrcp_engine_channel_t* demo_recog_engine_channel_create(mrcp_resource_engine_t *engine, apr_pool_t *pool) +{ + /* create demo recog channel */ + demo_recog_channel_t *recog_channel = apr_palloc(pool,sizeof(demo_recog_channel_t)); + recog_channel->demo_engine = engine->obj; + recog_channel->recog_request = NULL; + recog_channel->stop_response = NULL; + recog_channel->detector = mpf_activity_detector_create(pool); + recog_channel->audio_out = NULL; + /* create engine channel base */ + recog_channel->channel = mrcp_engine_sink_channel_create( + engine, /* resource engine */ + &channel_vtable, /* virtual methods table of engine channel */ + &audio_stream_vtable, /* virtual methods table of audio stream */ + recog_channel, /* object to associate */ + NULL, /* codec descriptor might be NULL by default */ + pool); /* pool to allocate memory from */ + return recog_channel->channel; +} + +/** Destroy engine channel */ +static apt_bool_t demo_recog_channel_destroy(mrcp_engine_channel_t *channel) +{ + demo_recog_channel_t *recog_channel = channel->method_obj; + if(recog_channel->audio_out) { + fclose(recog_channel->audio_out); + recog_channel->audio_out = NULL; + } + + return TRUE; +} + +/** Open engine channel (asynchronous response MUST be sent)*/ +static apt_bool_t demo_recog_channel_open(mrcp_engine_channel_t *channel) +{ + return demo_recog_msg_signal(DEMO_RECOG_MSG_OPEN_CHANNEL,channel,NULL); +} + +/** Close engine channel (asynchronous response MUST be sent)*/ +static apt_bool_t demo_recog_channel_close(mrcp_engine_channel_t *channel) +{ + return demo_recog_msg_signal(DEMO_RECOG_MSG_CLOSE_CHANNEL,channel,NULL); +} + +/** Process MRCP channel request (asynchronous response MUST be sent)*/ +static apt_bool_t demo_recog_channel_request_process(mrcp_engine_channel_t *channel, mrcp_message_t *request) +{ + return demo_recog_msg_signal(DEMO_RECOG_MSG_REQUEST_PROCESS,channel,request); +} + +/** Process RECOGNIZE request */ +static apt_bool_t demo_recog_channel_recognize(mrcp_engine_channel_t *channel, mrcp_message_t *request, mrcp_message_t *response) +{ + /* process RECOGNIZE request */ + mrcp_recog_header_t *recog_header; + demo_recog_channel_t *recog_channel = channel->method_obj; + recog_channel->timers_started = TRUE; + + /* get recognizer header */ + recog_header = mrcp_resource_header_get(request); + if(recog_header) { + if(mrcp_resource_header_property_check(request,RECOGNIZER_HEADER_START_INPUT_TIMERS) == TRUE) { + recog_channel->timers_started = recog_header->start_input_timers; + } + if(mrcp_resource_header_property_check(request,RECOGNIZER_HEADER_NO_INPUT_TIMEOUT) == TRUE) { + mpf_activity_detector_noinput_timeout_set(recog_channel->detector,recog_header->no_input_timeout); + } + if(mrcp_resource_header_property_check(request,RECOGNIZER_HEADER_SPEECH_COMPLETE_TIMEOUT) == TRUE) { + mpf_activity_detector_complete_timeout_set(recog_channel->detector,recog_header->speech_complete_timeout); + } + } + + if(!recog_channel->audio_out) { + const apt_dir_layout_t *dir_layout = channel->engine->dir_layout; + char *file_name = apr_pstrcat(channel->pool,"utter-",request->channel_id.session_id.buf,".pcm",NULL); + char *file_path = apt_datadir_filepath_get(dir_layout,file_name,channel->pool); + if(file_path) { + recog_channel->audio_out = fopen(file_path,"wb"); + } + } + + response->start_line.request_state = MRCP_REQUEST_STATE_INPROGRESS; + /* send asynchronous response */ + mrcp_engine_channel_message_send(channel,response); + recog_channel->recog_request = request; + return TRUE; +} + +/** Process STOP request */ +static apt_bool_t demo_recog_channel_stop(mrcp_engine_channel_t *channel, mrcp_message_t *request, mrcp_message_t *response) +{ + /* process STOP request */ + demo_recog_channel_t *recog_channel = channel->method_obj; + /* store STOP request, make sure there is no more activity and only then send the response */ + recog_channel->stop_response = response; + return TRUE; +} + +/** Process START-INPUT-TIMERS request */ +static apt_bool_t demo_recog_channel_timers_start(mrcp_engine_channel_t *channel, mrcp_message_t *request, mrcp_message_t *response) +{ + demo_recog_channel_t *recog_channel = channel->method_obj; + recog_channel->timers_started = TRUE; + return mrcp_engine_channel_message_send(channel,response); +} + +/** Dispatch MRCP request */ +static apt_bool_t demo_recog_channel_request_dispatch(mrcp_engine_channel_t *channel, mrcp_message_t *request) +{ + apt_bool_t processed = FALSE; + mrcp_message_t *response = mrcp_response_create(request,request->pool); + switch(request->start_line.method_id) { + case RECOGNIZER_SET_PARAMS: + break; + case RECOGNIZER_GET_PARAMS: + break; + case RECOGNIZER_DEFINE_GRAMMAR: + break; + case RECOGNIZER_RECOGNIZE: + processed = demo_recog_channel_recognize(channel,request,response); + break; + case RECOGNIZER_GET_RESULT: + break; + case RECOGNIZER_START_INPUT_TIMERS: + processed = demo_recog_channel_timers_start(channel,request,response); + break; + case RECOGNIZER_STOP: + processed = demo_recog_channel_stop(channel,request,response); + break; + default: + break; + } + if(processed == FALSE) { + /* send asynchronous response for not handled request */ + mrcp_engine_channel_message_send(channel,response); + } + return TRUE; +} + +/** Callback is called from MPF engine context to destroy any additional data associated with audio stream */ +static apt_bool_t demo_recog_stream_destroy(mpf_audio_stream_t *stream) +{ + return TRUE; +} + +/** Callback is called from MPF engine context to perform any action before open */ +static apt_bool_t demo_recog_stream_open(mpf_audio_stream_t *stream) +{ + return TRUE; +} + +/** Callback is called from MPF engine context to perform any action after close */ +static apt_bool_t demo_recog_stream_close(mpf_audio_stream_t *stream) +{ + return TRUE; +} + +/* Raise demo START-OF-INPUT event */ +static apt_bool_t demo_recog_start_of_input(demo_recog_channel_t *recog_channel) +{ + /* create START-OF-INPUT event */ + mrcp_message_t *message = mrcp_event_create( + recog_channel->recog_request, + RECOGNIZER_START_OF_INPUT, + recog_channel->recog_request->pool); + if(!message) { + return FALSE; + } + + /* set request state */ + message->start_line.request_state = MRCP_REQUEST_STATE_INPROGRESS; + /* send asynch event */ + return mrcp_engine_channel_message_send(recog_channel->channel,message); +} + +/* Load demo recognition result */ +static apt_bool_t demo_recog_result_load(demo_recog_channel_t *recog_channel, mrcp_message_t *message) +{ + FILE *file; + mrcp_engine_channel_t *channel = recog_channel->channel; + const apt_dir_layout_t *dir_layout = channel->engine->dir_layout; + char *file_path = apt_datadir_filepath_get(dir_layout,"result.xml",message->pool); + if(!file_path) { + return FALSE; + } + + /* read the demo result from file */ + file = fopen(file_path,"r"); + if(file) { + mrcp_generic_header_t *generic_header; + char text[1024]; + apr_size_t size; + size = fread(text,1,sizeof(text),file); + apt_string_assign_n(&message->body,text,size,message->pool); + fclose(file); + + /* get/allocate generic header */ + generic_header = mrcp_generic_header_prepare(message); + if(generic_header) { + /* set content types */ + apt_string_assign(&generic_header->content_type,"application/x-nlsml",message->pool); + mrcp_generic_header_property_add(message,GENERIC_HEADER_CONTENT_TYPE); + } + } + return TRUE; +} + +/* Raise demo RECOGNITION-COMPLETE event */ +static apt_bool_t demo_recog_recognition_complete(demo_recog_channel_t *recog_channel, mrcp_recog_completion_cause_e cause) +{ + mrcp_recog_header_t *recog_header; + /* create RECOGNITION-COMPLETE event */ + mrcp_message_t *message = mrcp_event_create( + recog_channel->recog_request, + RECOGNIZER_RECOGNITION_COMPLETE, + recog_channel->recog_request->pool); + if(!message) { + return FALSE; + } + + /* get/allocate recognizer header */ + recog_header = mrcp_resource_header_prepare(message); + if(recog_header) { + /* set completion cause */ + recog_header->completion_cause = cause; + mrcp_resource_header_property_add(message,RECOGNIZER_HEADER_COMPLETION_CAUSE); + } + /* set request state */ + message->start_line.request_state = MRCP_REQUEST_STATE_COMPLETE; + + if(cause == RECOGNIZER_COMPLETION_CAUSE_SUCCESS) { + demo_recog_result_load(recog_channel,message); + } + + recog_channel->recog_request = NULL; + /* send asynch event */ + return mrcp_engine_channel_message_send(recog_channel->channel,message); +} + +/** Callback is called from MPF engine context to write/send new frame */ +static apt_bool_t demo_recog_stream_write(mpf_audio_stream_t *stream, const mpf_frame_t *frame) +{ + demo_recog_channel_t *recog_channel = stream->obj; + if(recog_channel->stop_response) { + /* send asynchronous response to STOP request */ + mrcp_engine_channel_message_send(recog_channel->channel,recog_channel->stop_response); + recog_channel->stop_response = NULL; + recog_channel->recog_request = NULL; + return TRUE; + } + + if(recog_channel->recog_request) { + mpf_detector_event_e det_event = mpf_activity_detector_process(recog_channel->detector,frame); + switch(det_event) { + case MPF_DETECTOR_EVENT_ACTIVITY: + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Detected Voice Activity"); + demo_recog_start_of_input(recog_channel); + break; + case MPF_DETECTOR_EVENT_INACTIVITY: + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Detected Voice Inactivity"); + demo_recog_recognition_complete(recog_channel,RECOGNIZER_COMPLETION_CAUSE_SUCCESS); + break; + case MPF_DETECTOR_EVENT_NOINPUT: + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Detected Noinput"); + if(recog_channel->timers_started == TRUE) { + demo_recog_recognition_complete(recog_channel,RECOGNIZER_COMPLETION_CAUSE_NO_INPUT_TIMEOUT); + } + break; + default: + break; + } + + if(recog_channel->audio_out) { + fwrite(frame->codec_frame.buffer,1,frame->codec_frame.size,recog_channel->audio_out); + } + } + return TRUE; +} + +static apt_bool_t demo_recog_msg_signal(demo_recog_msg_type_e type, mrcp_engine_channel_t *channel, mrcp_message_t *request) +{ + apt_bool_t status = FALSE; + demo_recog_channel_t *demo_channel = channel->method_obj; + demo_recog_engine_t *demo_engine = demo_channel->demo_engine; + apt_task_t *task = apt_consumer_task_base_get(demo_engine->task); + apt_task_msg_t *msg = apt_task_msg_get(task); + if(msg) { + demo_recog_msg_t *demo_msg; + msg->type = TASK_MSG_USER; + demo_msg = (demo_recog_msg_t*) msg->data; + + demo_msg->type = type; + demo_msg->channel = channel; + demo_msg->request = request; + status = apt_task_msg_signal(task,msg); + } + return status; +} + +static apt_bool_t demo_recog_msg_process(apt_task_t *task, apt_task_msg_t *msg) +{ + demo_recog_msg_t *demo_msg = (demo_recog_msg_t*)msg->data; + switch(demo_msg->type) { + case DEMO_RECOG_MSG_OPEN_CHANNEL: + /* open channel and send asynch response */ + mrcp_engine_channel_open_respond(demo_msg->channel,TRUE); + break; + case DEMO_RECOG_MSG_CLOSE_CHANNEL: + /* close channel, make sure there is no activity and send asynch response */ + mrcp_engine_channel_close_respond(demo_msg->channel); + break; + case DEMO_RECOG_MSG_REQUEST_PROCESS: + demo_recog_channel_request_dispatch(demo_msg->channel,demo_msg->request); + break; + default: + break; + } + return TRUE; +} diff --git a/libs/unimrcp/plugins/demo-synth/Makefile.am b/libs/unimrcp/plugins/demo-synth/Makefile.am new file mode 100644 index 0000000000..0cb6d84166 --- /dev/null +++ b/libs/unimrcp/plugins/demo-synth/Makefile.am @@ -0,0 +1,16 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -Iinclude \ + -I$(top_srcdir)/libs/mrcp-engine/include \ + -I$(top_srcdir)/libs/mrcp/include \ + -I$(top_srcdir)/libs/mrcp/message/include \ + -I$(top_srcdir)/libs/mrcp/control/include \ + -I$(top_srcdir)/libs/mrcp/resources/include \ + -I$(top_srcdir)/libs/mpf/include \ + -I$(top_srcdir)/libs/apr-toolkit/include \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) + +plugin_LTLIBRARIES = demosynth.la + +demosynth_la_SOURCES = src/demo_synth_engine.c +demosynth_la_LDFLAGS = -module $(PLUGIN_LT_VERSION) diff --git a/libs/unimrcp/plugins/demo-synth/demosynth.vcproj b/libs/unimrcp/plugins/demo-synth/demosynth.vcproj new file mode 100644 index 0000000000..3afd81a741 --- /dev/null +++ b/libs/unimrcp/plugins/demo-synth/demosynth.vcproj @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/plugins/demo-synth/src/demo_synth_engine.c b/libs/unimrcp/plugins/demo-synth/src/demo_synth_engine.c new file mode 100644 index 0000000000..c23129c390 --- /dev/null +++ b/libs/unimrcp/plugins/demo-synth/src/demo_synth_engine.c @@ -0,0 +1,521 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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. + */ + +/* + * Some mandatory rules for plugin implementation. + * 1. Each plugin MUST contain the following function as an entry point of the plugin + * MRCP_PLUGIN_DECLARE(mrcp_resource_engine_t*) mrcp_plugin_create(apr_pool_t *pool) + * 2. One and only one response MUST be sent back to the received request. + * 3. Methods (callbacks) of the MRCP engine channel MUST not block. + * (asynch response can be sent from the context of other thread) + * 4. Methods (callbacks) of the MPF engine stream MUST not block. + */ + +#include "mrcp_resource_engine.h" +#include "mrcp_synth_resource.h" +#include "mrcp_synth_header.h" +#include "mrcp_generic_header.h" +#include "mrcp_message.h" +#include "apt_consumer_task.h" +#include "apt_log.h" + +#define SYNTH_ENGINE_TASK_NAME "Demo Synth Engine" + +typedef struct demo_synth_engine_t demo_synth_engine_t; +typedef struct demo_synth_channel_t demo_synth_channel_t; +typedef struct demo_synth_msg_t demo_synth_msg_t; + +/** Declaration of synthesizer engine methods */ +static apt_bool_t demo_synth_engine_destroy(mrcp_resource_engine_t *engine); +static apt_bool_t demo_synth_engine_open(mrcp_resource_engine_t *engine); +static apt_bool_t demo_synth_engine_close(mrcp_resource_engine_t *engine); +static mrcp_engine_channel_t* demo_synth_engine_channel_create(mrcp_resource_engine_t *engine, apr_pool_t *pool); + +static const struct mrcp_engine_method_vtable_t engine_vtable = { + demo_synth_engine_destroy, + demo_synth_engine_open, + demo_synth_engine_close, + demo_synth_engine_channel_create +}; + + +/** Declaration of synthesizer channel methods */ +static apt_bool_t demo_synth_channel_destroy(mrcp_engine_channel_t *channel); +static apt_bool_t demo_synth_channel_open(mrcp_engine_channel_t *channel); +static apt_bool_t demo_synth_channel_close(mrcp_engine_channel_t *channel); +static apt_bool_t demo_synth_channel_request_process(mrcp_engine_channel_t *channel, mrcp_message_t *request); + +static const struct mrcp_engine_channel_method_vtable_t channel_vtable = { + demo_synth_channel_destroy, + demo_synth_channel_open, + demo_synth_channel_close, + demo_synth_channel_request_process +}; + +/** Declaration of synthesizer audio stream methods */ +static apt_bool_t demo_synth_stream_destroy(mpf_audio_stream_t *stream); +static apt_bool_t demo_synth_stream_open(mpf_audio_stream_t *stream); +static apt_bool_t demo_synth_stream_close(mpf_audio_stream_t *stream); +static apt_bool_t demo_synth_stream_read(mpf_audio_stream_t *stream, mpf_frame_t *frame); + +static const mpf_audio_stream_vtable_t audio_stream_vtable = { + demo_synth_stream_destroy, + demo_synth_stream_open, + demo_synth_stream_close, + demo_synth_stream_read, + NULL, + NULL, + NULL +}; + +/** Declaration of demo synthesizer engine */ +struct demo_synth_engine_t { + apt_consumer_task_t *task; +}; + +/** Declaration of demo synthesizer channel */ +struct demo_synth_channel_t { + /** Back pointer to engine */ + demo_synth_engine_t *demo_engine; + /** Engine channel base */ + mrcp_engine_channel_t *channel; + + /** Active (in-progress) speak request */ + mrcp_message_t *speak_request; + /** Pending stop response */ + mrcp_message_t *stop_response; + /** Estimated time to complete */ + apr_size_t time_to_complete; + /** Is paused */ + apt_bool_t paused; + /** Speech source (used instead of actual synthesizing) */ + FILE *audio_file; +}; + +typedef enum { + DEMO_SYNTH_MSG_OPEN_CHANNEL, + DEMO_SYNTH_MSG_CLOSE_CHANNEL, + DEMO_SYNTH_MSG_REQUEST_PROCESS +} demo_synth_msg_type_e; + +/** Declaration of demo synthesizer task message */ +struct demo_synth_msg_t { + demo_synth_msg_type_e type; + mrcp_engine_channel_t *channel; + mrcp_message_t *request; +}; + + +#define DEMO_SPEECH_SOURCE_FILE "demo.pcm" +static apt_bool_t demo_synth_msg_signal(demo_synth_msg_type_e type, mrcp_engine_channel_t *channel, mrcp_message_t *request); +static apt_bool_t demo_synth_msg_process(apt_task_t *task, apt_task_msg_t *msg); + +/** Declare this macro to use log routine of the server, plugin is loaded from */ +MRCP_PLUGIN_LOGGER_IMPLEMENT + +/** Create demo synthesizer engine */ +MRCP_PLUGIN_DECLARE(mrcp_resource_engine_t*) mrcp_plugin_create(apr_pool_t *pool) +{ + /* create demo engine */ + demo_synth_engine_t *demo_engine = apr_palloc(pool,sizeof(demo_synth_engine_t)); + apt_task_t *task; + apt_task_vtable_t *vtable; + apt_task_msg_pool_t *msg_pool; + + /* create task/thread to run demo engine in the context of this task */ + msg_pool = apt_task_msg_pool_create_dynamic(sizeof(demo_synth_msg_t),pool); + demo_engine->task = apt_consumer_task_create(demo_engine,msg_pool,pool); + if(!demo_engine->task) { + return NULL; + } + task = apt_consumer_task_base_get(demo_engine->task); + apt_task_name_set(task,SYNTH_ENGINE_TASK_NAME); + vtable = apt_task_vtable_get(task); + if(vtable) { + vtable->process_msg = demo_synth_msg_process; + } + + /* create resource engine base */ + return mrcp_resource_engine_create( + MRCP_SYNTHESIZER_RESOURCE, /* MRCP resource identifier */ + demo_engine, /* object to associate */ + &engine_vtable, /* virtual methods table of resource engine */ + pool); /* pool to allocate memory from */ +} + +/** Destroy synthesizer engine */ +static apt_bool_t demo_synth_engine_destroy(mrcp_resource_engine_t *engine) +{ + demo_synth_engine_t *demo_engine = engine->obj; + if(demo_engine->task) { + apt_task_t *task = apt_consumer_task_base_get(demo_engine->task); + apt_task_destroy(task); + demo_engine->task = NULL; + } + return TRUE; +} + +/** Open synthesizer engine */ +static apt_bool_t demo_synth_engine_open(mrcp_resource_engine_t *engine) +{ + demo_synth_engine_t *demo_engine = engine->obj; + if(demo_engine->task) { + apt_task_t *task = apt_consumer_task_base_get(demo_engine->task); + apt_task_start(task); + } + return TRUE; +} + +/** Close synthesizer engine */ +static apt_bool_t demo_synth_engine_close(mrcp_resource_engine_t *engine) +{ + demo_synth_engine_t *demo_engine = engine->obj; + if(demo_engine->task) { + apt_task_t *task = apt_consumer_task_base_get(demo_engine->task); + apt_task_terminate(task,TRUE); + } + return TRUE; +} + +/** Create demo synthesizer channel derived from engine channel base */ +static mrcp_engine_channel_t* demo_synth_engine_channel_create(mrcp_resource_engine_t *engine, apr_pool_t *pool) +{ + /* create demo synth channel */ + demo_synth_channel_t *synth_channel = apr_palloc(pool,sizeof(demo_synth_channel_t)); + synth_channel->demo_engine = engine->obj; + synth_channel->speak_request = NULL; + synth_channel->stop_response = NULL; + synth_channel->time_to_complete = 0; + synth_channel->paused = FALSE; + synth_channel->audio_file = NULL; + /* create engine channel base */ + synth_channel->channel = mrcp_engine_source_channel_create( + engine, /* resource engine */ + &channel_vtable, /* virtual methods table of engine channel */ + &audio_stream_vtable, /* virtual methods table of audio stream */ + synth_channel, /* object to associate */ + NULL, /* codec descriptor might be NULL by default */ + pool); /* pool to allocate memory from */ + return synth_channel->channel; +} + +/** Destroy engine channel */ +static apt_bool_t demo_synth_channel_destroy(mrcp_engine_channel_t *channel) +{ + /* nothing to destroy */ + return TRUE; +} + +/** Open engine channel (asynchronous response MUST be sent)*/ +static apt_bool_t demo_synth_channel_open(mrcp_engine_channel_t *channel) +{ + return demo_synth_msg_signal(DEMO_SYNTH_MSG_OPEN_CHANNEL,channel,NULL); +} + +/** Close engine channel (asynchronous response MUST be sent)*/ +static apt_bool_t demo_synth_channel_close(mrcp_engine_channel_t *channel) +{ + return demo_synth_msg_signal(DEMO_SYNTH_MSG_CLOSE_CHANNEL,channel,NULL); +} + +/** Process MRCP channel request (asynchronous response MUST be sent)*/ +static apt_bool_t demo_synth_channel_request_process(mrcp_engine_channel_t *channel, mrcp_message_t *request) +{ + return demo_synth_msg_signal(DEMO_SYNTH_MSG_REQUEST_PROCESS,channel,request); +} + +/** Process SPEAK request */ +static apt_bool_t demo_synth_channel_speak(mrcp_engine_channel_t *channel, mrcp_message_t *request, mrcp_message_t *response) +{ + char *file_path = NULL; + demo_synth_channel_t *synth_channel = channel->method_obj; + synth_channel->time_to_complete = 0; + if(channel->engine) { + file_path = apt_datadir_filepath_get(channel->engine->dir_layout,DEMO_SPEECH_SOURCE_FILE,channel->pool); + } + if(file_path) { + synth_channel->audio_file = fopen(file_path,"rb"); + if(synth_channel->audio_file) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Set [%s] as Speech Source",file_path); + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"No Speech Source [%s] Found",file_path); + /* calculate estimated time to complete */ + if(mrcp_generic_header_property_check(request,GENERIC_HEADER_CONTENT_LENGTH) == TRUE) { + mrcp_generic_header_t *generic_header = mrcp_generic_header_get(request); + if(generic_header) { + synth_channel->time_to_complete = generic_header->content_length * 10; /* 10 msec per character */ + } + } + } + } + + response->start_line.request_state = MRCP_REQUEST_STATE_INPROGRESS; + /* send asynchronous response */ + mrcp_engine_channel_message_send(channel,response); + synth_channel->speak_request = request; + return TRUE; +} + +/** Process STOP request */ +static apt_bool_t demo_synth_channel_stop(mrcp_engine_channel_t *channel, mrcp_message_t *request, mrcp_message_t *response) +{ + demo_synth_channel_t *synth_channel = channel->method_obj; + /* store the request, make sure there is no more activity and only then send the response */ + synth_channel->stop_response = response; + return TRUE; +} + +/** Process PAUSE request */ +static apt_bool_t demo_synth_channel_pause(mrcp_engine_channel_t *channel, mrcp_message_t *request, mrcp_message_t *response) +{ + demo_synth_channel_t *synth_channel = channel->method_obj; + synth_channel->paused = TRUE; + /* send asynchronous response */ + mrcp_engine_channel_message_send(channel,response); + return TRUE; +} + +/** Process RESUME request */ +static apt_bool_t demo_synth_channel_resume(mrcp_engine_channel_t *channel, mrcp_message_t *request, mrcp_message_t *response) +{ + demo_synth_channel_t *synth_channel = channel->method_obj; + synth_channel->paused = FALSE; + /* send asynchronous response */ + mrcp_engine_channel_message_send(channel,response); + return TRUE; +} + +/** Process SET-PARAMS request */ +static apt_bool_t demo_synth_channel_set_params(mrcp_engine_channel_t *channel, mrcp_message_t *request, mrcp_message_t *response) +{ + mrcp_synth_header_t *req_synth_header; + /* get synthesizer header */ + req_synth_header = mrcp_resource_header_get(request); + if(req_synth_header) { + /* check voice age header */ + if(mrcp_resource_header_property_check(request,SYNTHESIZER_HEADER_VOICE_AGE) == TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Set Voice Age [%d]",req_synth_header->voice_param.age); + } + /* check voice name header */ + if(mrcp_resource_header_property_check(request,SYNTHESIZER_HEADER_VOICE_NAME) == TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Set Voice Name [%s]",req_synth_header->voice_param.name); + } + } + + /* send asynchronous response */ + mrcp_engine_channel_message_send(channel,response); + return TRUE; +} + +/** Process GET-PARAMS request */ +static apt_bool_t demo_synth_channel_get_params(mrcp_engine_channel_t *channel, mrcp_message_t *request, mrcp_message_t *response) +{ + mrcp_synth_header_t *req_synth_header; + /* get synthesizer header */ + req_synth_header = mrcp_resource_header_get(request); + if(req_synth_header) { + mrcp_synth_header_t *res_synth_header = mrcp_resource_header_prepare(response); + /* check voice age header */ + if(mrcp_resource_header_property_check(request,SYNTHESIZER_HEADER_VOICE_AGE) == TRUE) { + res_synth_header->voice_param.age = 25; + mrcp_resource_header_property_add(response,SYNTHESIZER_HEADER_VOICE_AGE); + } + /* check voice name header */ + if(mrcp_resource_header_property_check(request,SYNTHESIZER_HEADER_VOICE_NAME) == TRUE) { + apt_string_set(&res_synth_header->voice_param.name,"David"); + mrcp_resource_header_property_add(response,SYNTHESIZER_HEADER_VOICE_NAME); + } + } + + /* send asynchronous response */ + mrcp_engine_channel_message_send(channel,response); + return TRUE; +} + +/** Dispatch MRCP request */ +static apt_bool_t demo_synth_channel_request_dispatch(mrcp_engine_channel_t *channel, mrcp_message_t *request) +{ + apt_bool_t processed = FALSE; + mrcp_message_t *response = mrcp_response_create(request,request->pool); + switch(request->start_line.method_id) { + case SYNTHESIZER_SET_PARAMS: + processed = demo_synth_channel_set_params(channel,request,response); + break; + case SYNTHESIZER_GET_PARAMS: + processed = demo_synth_channel_get_params(channel,request,response); + break; + case SYNTHESIZER_SPEAK: + processed = demo_synth_channel_speak(channel,request,response); + break; + case SYNTHESIZER_STOP: + processed = demo_synth_channel_stop(channel,request,response); + break; + case SYNTHESIZER_PAUSE: + processed = demo_synth_channel_pause(channel,request,response); + break; + case SYNTHESIZER_RESUME: + processed = demo_synth_channel_resume(channel,request,response); + break; + case SYNTHESIZER_BARGE_IN_OCCURRED: + processed = demo_synth_channel_stop(channel,request,response); + break; + case SYNTHESIZER_CONTROL: + break; + case SYNTHESIZER_DEFINE_LEXICON: + break; + default: + break; + } + if(processed == FALSE) { + /* send asynchronous response for not handled request */ + mrcp_engine_channel_message_send(channel,response); + } + return TRUE; +} + +/** Callback is called from MPF engine context to destroy any additional data associated with audio stream */ +static apt_bool_t demo_synth_stream_destroy(mpf_audio_stream_t *stream) +{ + return TRUE; +} + +/** Callback is called from MPF engine context to perform any action before open */ +static apt_bool_t demo_synth_stream_open(mpf_audio_stream_t *stream) +{ + return TRUE; +} + +/** Callback is called from MPF engine context to perform any action after close */ +static apt_bool_t demo_synth_stream_close(mpf_audio_stream_t *stream) +{ + return TRUE; +} + +/** Callback is called from MPF engine context to read/get new frame */ +static apt_bool_t demo_synth_stream_read(mpf_audio_stream_t *stream, mpf_frame_t *frame) +{ + demo_synth_channel_t *synth_channel = stream->obj; + /* check if STOP was requested */ + if(synth_channel->stop_response) { + /* send asynchronous response to STOP request */ + mrcp_engine_channel_message_send(synth_channel->channel,synth_channel->stop_response); + synth_channel->stop_response = NULL; + synth_channel->speak_request = NULL; + synth_channel->paused = FALSE; + if(synth_channel->audio_file) { + fclose(synth_channel->audio_file); + synth_channel->audio_file = NULL; + } + return TRUE; + } + + /* check if there is active SPEAK request and it isn't in paused state */ + if(synth_channel->speak_request && synth_channel->paused == FALSE) { + /* normal processing */ + apt_bool_t completed = FALSE; + if(synth_channel->audio_file) { + /* read speech from file */ + apr_size_t size = frame->codec_frame.size; + if(fread(frame->codec_frame.buffer,1,size,synth_channel->audio_file) == size) { + frame->type |= MEDIA_FRAME_TYPE_AUDIO; + } + else { + completed = TRUE; + } + } + else { + /* fill with silence in case no file available */ + if(synth_channel->time_to_complete >= CODEC_FRAME_TIME_BASE) { + memset(frame->codec_frame.buffer,0,frame->codec_frame.size); + frame->type |= MEDIA_FRAME_TYPE_AUDIO; + synth_channel->time_to_complete -= CODEC_FRAME_TIME_BASE; + } + else { + completed = TRUE; + } + } + + if(completed) { + /* raise SPEAK-COMPLETE event */ + mrcp_message_t *message = mrcp_event_create( + synth_channel->speak_request, + SYNTHESIZER_SPEAK_COMPLETE, + synth_channel->speak_request->pool); + if(message) { + /* get/allocate synthesizer header */ + mrcp_synth_header_t *synth_header = mrcp_resource_header_prepare(message); + if(synth_header) { + /* set completion cause */ + synth_header->completion_cause = SYNTHESIZER_COMPLETION_CAUSE_NORMAL; + mrcp_resource_header_property_add(message,SYNTHESIZER_HEADER_COMPLETION_CAUSE); + } + /* set request state */ + message->start_line.request_state = MRCP_REQUEST_STATE_COMPLETE; + + synth_channel->speak_request = NULL; + if(synth_channel->audio_file) { + fclose(synth_channel->audio_file); + synth_channel->audio_file = NULL; + } + /* send asynch event */ + mrcp_engine_channel_message_send(synth_channel->channel,message); + } + } + } + return TRUE; +} + +static apt_bool_t demo_synth_msg_signal(demo_synth_msg_type_e type, mrcp_engine_channel_t *channel, mrcp_message_t *request) +{ + apt_bool_t status = FALSE; + demo_synth_channel_t *demo_channel = channel->method_obj; + demo_synth_engine_t *demo_engine = demo_channel->demo_engine; + apt_task_t *task = apt_consumer_task_base_get(demo_engine->task); + apt_task_msg_t *msg = apt_task_msg_get(task); + if(msg) { + demo_synth_msg_t *demo_msg; + msg->type = TASK_MSG_USER; + demo_msg = (demo_synth_msg_t*) msg->data; + + demo_msg->type = type; + demo_msg->channel = channel; + demo_msg->request = request; + status = apt_task_msg_signal(task,msg); + } + return status; +} + +static apt_bool_t demo_synth_msg_process(apt_task_t *task, apt_task_msg_t *msg) +{ + demo_synth_msg_t *demo_msg = (demo_synth_msg_t*)msg->data; + switch(demo_msg->type) { + case DEMO_SYNTH_MSG_OPEN_CHANNEL: + /* open channel and send asynch response */ + mrcp_engine_channel_open_respond(demo_msg->channel,TRUE); + break; + case DEMO_SYNTH_MSG_CLOSE_CHANNEL: + /* close channel, make sure there is no activity and send asynch response */ + mrcp_engine_channel_close_respond(demo_msg->channel); + break; + case DEMO_SYNTH_MSG_REQUEST_PROCESS: + demo_synth_channel_request_dispatch(demo_msg->channel,demo_msg->request); + break; + default: + break; + } + return TRUE; +} diff --git a/libs/unimrcp/plugins/mrcp-cepstral/Makefile.am b/libs/unimrcp/plugins/mrcp-cepstral/Makefile.am new file mode 100644 index 0000000000..38b9bab308 --- /dev/null +++ b/libs/unimrcp/plugins/mrcp-cepstral/Makefile.am @@ -0,0 +1,17 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -Iinclude \ + -I$(top_srcdir)/libs/mrcp-engine/include \ + -I$(top_srcdir)/libs/mrcp/include \ + -I$(top_srcdir)/libs/mrcp/message/include \ + -I$(top_srcdir)/libs/mrcp/control/include \ + -I$(top_srcdir)/libs/mrcp/resources/include \ + -I$(top_srcdir)/libs/mpf/include \ + -I$(top_srcdir)/libs/apr-toolkit/include \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) $(UNIMRCP_SWIFT_INCLUDES) + +plugin_LTLIBRARIES = mrcpcepstral.la + +mrcpcepstral_la_SOURCES = src/mrcp_swift.c +mrcpcepstral_la_LDFLAGS = -module $(PLUGIN_LT_VERSION) $(UNIMRCP_SWIFT_LDFLAGS) +mrcpcepstral_la_LIBADD = $(UNIMRCP_SWIFT_LIBS) diff --git a/libs/unimrcp/plugins/mrcp-cepstral/mrcpcepstral.vcproj b/libs/unimrcp/plugins/mrcp-cepstral/mrcpcepstral.vcproj new file mode 100644 index 0000000000..d496f42ec6 --- /dev/null +++ b/libs/unimrcp/plugins/mrcp-cepstral/mrcpcepstral.vcproj @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/plugins/mrcp-cepstral/src/mrcp_swift.c b/libs/unimrcp/plugins/mrcp-cepstral/src/mrcp_swift.c new file mode 100644 index 0000000000..51558fd530 --- /dev/null +++ b/libs/unimrcp/plugins/mrcp-cepstral/src/mrcp_swift.c @@ -0,0 +1,701 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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. + */ + +/* + * Some mandatory rules concerning plugin implementation. + * 1. Each plugin MUST contain the following function as an entry point of the plugin + * MRCP_PLUGIN_DECLARE(mrcp_resource_engine_t*) mrcp_plugin_create(apr_pool_t *pool) + * 2. One and only one response MUST be sent back to the received request. + * 3. Methods (callbacks) of the MRCP engine channel MUST not block. + * (asynch response can be sent from the context of other thread) + * 4. Methods (callbacks) of the MPF engine stream MUST not block. + */ + +#include +#include "mrcp_resource_engine.h" +#include "mrcp_synth_resource.h" +#include "mrcp_synth_header.h" +#include "mrcp_generic_header.h" +#include "mrcp_message.h" +#include "mpf_buffer.h" +#include "apt_log.h" + +/** Declaration of synthesizer engine methods */ +static apt_bool_t mrcp_swift_engine_destroy(mrcp_resource_engine_t *engine); +static apt_bool_t mrcp_swift_engine_open(mrcp_resource_engine_t *engine); +static apt_bool_t mrcp_swift_engine_close(mrcp_resource_engine_t *engine); +static mrcp_engine_channel_t* mrcp_swift_engine_channel_create(mrcp_resource_engine_t *engine, apr_pool_t *pool); + +static const struct mrcp_engine_method_vtable_t engine_vtable = { + mrcp_swift_engine_destroy, + mrcp_swift_engine_open, + mrcp_swift_engine_close, + mrcp_swift_engine_channel_create +}; + + +/** Declaration of synthesizer channel methods */ +static apt_bool_t mrcp_swift_channel_destroy(mrcp_engine_channel_t *channel); +static apt_bool_t mrcp_swift_channel_open(mrcp_engine_channel_t *channel); +static apt_bool_t mrcp_swift_channel_close(mrcp_engine_channel_t *channel); +static apt_bool_t mrcp_swift_channel_request_process(mrcp_engine_channel_t *channel, mrcp_message_t *request); + +static const struct mrcp_engine_channel_method_vtable_t channel_vtable = { + mrcp_swift_channel_destroy, + mrcp_swift_channel_open, + mrcp_swift_channel_close, + mrcp_swift_channel_request_process +}; + +/** Declaration of synthesizer audio stream methods */ +static apt_bool_t synth_stream_destroy(mpf_audio_stream_t *stream); +static apt_bool_t synth_stream_open(mpf_audio_stream_t *stream); +static apt_bool_t synth_stream_close(mpf_audio_stream_t *stream); +static apt_bool_t synth_stream_read(mpf_audio_stream_t *stream, mpf_frame_t *frame); + +static const mpf_audio_stream_vtable_t audio_stream_vtable = { + synth_stream_destroy, + synth_stream_open, + synth_stream_close, + synth_stream_read, + NULL, + NULL, + NULL +}; + +typedef struct mrcp_swift_channel_t mrcp_swift_channel_t; +/** Declaration of Swift synthesizer channel */ +struct mrcp_swift_channel_t { + /** Swift port */ + swift_port *port; + swift_background_t tts_stream; + + /** Audio buffer */ + mpf_buffer_t *audio_buffer; + + /** Engine channel base */ + mrcp_engine_channel_t *channel; + + /** Active (in-progress) speak request */ + mrcp_message_t *speak_request; + /** Pending stop response */ + mrcp_message_t *stop_response; + /** Is paused */ + apt_bool_t paused; +}; + +/** Table of prosody volumes for Swift engine */ +static const int swift_prosody_volume_table[PROSODY_VOLUME_COUNT] = { + 25, /* PROSODY_VOLUME_SILENT */ + 50, /* PROSODY_VOLUME_XSOFT */ + 75, /* PROSODY_VOLUME_SOFT */ + 100, /* PROSODY_VOLUME_MEDIUM */ + 125, /* PROSODY_VOLUME_LOUD */ + 150, /* PROSODY_VOLUME_XLOUD */ + 100 /* PROSODY_VOLUME_DEFAULT */ +}; + +/** Table of prosody rates for Swift engine */ +static const int swift_prosody_rate_table[PROSODY_RATE_COUNT] = { + 85, /* PROSODY_RATE_XSLOW */ + 113, /* PROSODY_RATE_SLOW */ + 170, /* PROSODY_RATE_MEDIUM */ + 225, /* PROSODY_RATE_FAST */ + 340, /* PROSODY_RATE_XFAST */ + 170 /* PROSODY_RATE_DEFAULT */ +}; + +static apr_table_t *swift_speech_language_table; + +static apr_table_t* mrcp_swift_language_table_create(apr_pool_t *pool); +static void mrcp_swift_voices_show(swift_engine *engine); +static swift_result_t mrcp_swift_write_audio(swift_event *event, swift_event_t type, void *udata); +static apt_bool_t mrcp_swift_channel_voice_set(mrcp_swift_channel_t *synth_channel, mrcp_message_t *message); +static apt_bool_t mrcp_swift_channel_params_set(mrcp_swift_channel_t *synth_channel, mrcp_message_t *message); + +/** Declare this macro to use log routine of the server, plugin is loaded from */ +MRCP_PLUGIN_LOGGER_IMPLEMENT + +/** Create Swift synthesizer engine */ +MRCP_PLUGIN_DECLARE(mrcp_resource_engine_t*) mrcp_plugin_create(apr_pool_t *pool) +{ + swift_engine *synth_engine; + mrcp_resource_engine_t *engine; + /* open swift engine */ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Open Swift Engine [%s]",swift_version); + if((synth_engine = swift_engine_open(NULL)) == NULL) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Open Swift Engine"); + return NULL; + } + swift_speech_language_table = mrcp_swift_language_table_create(pool); + mrcp_swift_voices_show(synth_engine); + + /* create resource engine base */ + engine = mrcp_resource_engine_create( + MRCP_SYNTHESIZER_RESOURCE, /* MRCP resource identifier */ + synth_engine, /* object to associate */ + &engine_vtable, /* virtual methods table of resource engine */ + pool); /* pool to allocate memory from */ + if(!engine) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Resource Engine"); + swift_engine_close(synth_engine); + } + return engine; +} + +/** Destroy synthesizer engine */ +static apt_bool_t mrcp_swift_engine_destroy(mrcp_resource_engine_t *engine) +{ + swift_engine *synth_engine = engine->obj; + /* close swift engine */ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Close Swift Engine"); + if(synth_engine) { + swift_engine_close(synth_engine); + engine->obj = NULL; + } + return TRUE; +} + +/** Open synthesizer engine */ +static apt_bool_t mrcp_swift_engine_open(mrcp_resource_engine_t *engine) +{ + return TRUE; +} + +/** Close synthesizer engine */ +static apt_bool_t mrcp_swift_engine_close(mrcp_resource_engine_t *engine) +{ + return TRUE; +} + +/** Create demo synthesizer channel derived from engine channel base */ +static mrcp_engine_channel_t* mrcp_swift_engine_channel_create(mrcp_resource_engine_t *engine, apr_pool_t *pool) +{ + swift_engine *synth_engine = engine->obj; + mrcp_swift_channel_t *synth_channel; + mrcp_engine_channel_t *channel; + swift_params *params; + swift_port *port; + mpf_codec_descriptor_t *codec_descriptor; + + codec_descriptor = apr_palloc(pool,sizeof(mpf_codec_descriptor_t)); + mpf_codec_descriptor_init(codec_descriptor); + codec_descriptor->channel_count = 1; + codec_descriptor->payload_type = 96; + apt_string_set(&codec_descriptor->name,"L16"); + codec_descriptor->sampling_rate = 8000; + + params = swift_params_new(NULL); + swift_params_set_string(params, "audio/encoding", "pcm16"); + swift_params_set_int(params, "audio/sampling-rate", codec_descriptor->sampling_rate); + /* open swift port */ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Open Swift Port"); + if((port = swift_port_open(synth_engine,params)) == NULL) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Open Swift Port"); + return NULL; + } + + /* create swift synth channel */ + synth_channel = apr_palloc(pool,sizeof(mrcp_swift_channel_t)); + synth_channel->speak_request = NULL; + synth_channel->stop_response = NULL; + synth_channel->paused = FALSE; + synth_channel->channel = NULL; + synth_channel->port = port; + synth_channel->tts_stream = 0; + /* create engine channel base */ + channel = mrcp_engine_source_channel_create( + engine, /* resource engine */ + &channel_vtable, /* virtual methods table of engine channel */ + &audio_stream_vtable, /* virtual methods table of audio stream */ + synth_channel, /* object to associate */ + codec_descriptor, /* codec descriptor might be NULL by default */ + pool); /* pool to allocate memory from */ + + if(!channel) { + swift_port_close(port); + synth_channel->port = NULL; + return NULL; + } + + synth_channel->audio_buffer = mpf_buffer_create(pool); + + /* set swift_write_audio as a callback, with the output file as its param */ + swift_port_set_callback(port, &mrcp_swift_write_audio, SWIFT_EVENT_AUDIO | SWIFT_EVENT_END, synth_channel); + synth_channel->channel = channel; + return channel; +} + +/** Destroy engine channel */ +static apt_bool_t mrcp_swift_channel_destroy(mrcp_engine_channel_t *channel) +{ + mrcp_swift_channel_t *synth_channel = channel->method_obj; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Close Swift Port"); + if(synth_channel->port) { + /* close swift port */ + swift_port_close(synth_channel->port); + synth_channel->port = NULL; + } + return TRUE; +} + +/** Open engine channel (asynchronous response MUST be sent)*/ +static apt_bool_t mrcp_swift_channel_open(mrcp_engine_channel_t *channel) +{ + /* open channel and send asynch response */ + return mrcp_engine_channel_open_respond(channel,TRUE); +} + +/** Close engine channel (asynchronous response MUST be sent)*/ +static apt_bool_t mrcp_swift_channel_close(mrcp_engine_channel_t *channel) +{ + /* close channel, make sure there is no activity and send asynch response */ + return mrcp_engine_channel_close_respond(channel); +} + +/** Process SPEAK request */ +static apt_bool_t mrcp_swift_channel_speak(mrcp_engine_channel_t *channel, mrcp_message_t *request, mrcp_message_t *response) +{ + mrcp_swift_channel_t *synth_channel = channel->method_obj; + + /* set voice */ + mrcp_swift_channel_voice_set(synth_channel,request); + /* set params */ + mrcp_swift_channel_params_set(synth_channel,request); + /* (re)start audio buffer */ + mpf_buffer_restart(synth_channel->audio_buffer); + response->start_line.request_state = MRCP_REQUEST_STATE_INPROGRESS; + /* start to synthesize */ + if(swift_port_speak_text(synth_channel->port,request->body.buf,0,NULL,&synth_channel->tts_stream,NULL) != SWIFT_SUCCESS) { + response->start_line.request_state = MRCP_REQUEST_STATE_COMPLETE; + response->start_line.status_code = MRCP_STATUS_CODE_METHOD_FAILED; + } + /* send asynchronous response */ + mrcp_engine_channel_message_send(channel,response); + synth_channel->speak_request = request; + return TRUE; +} + +/** Process STOP request */ +static apt_bool_t mrcp_swift_channel_stop(mrcp_engine_channel_t *channel, mrcp_message_t *request, mrcp_message_t *response) +{ + mrcp_swift_channel_t *synth_channel = channel->method_obj; + /* store the request, make sure there is no more activity and only then send the response */ + swift_port_stop(synth_channel->port,synth_channel->tts_stream,SWIFT_EVENT_NOW); + synth_channel->stop_response = response; + return TRUE; +} + +/** Process PAUSE request */ +static apt_bool_t mrcp_swift_channel_pause(mrcp_engine_channel_t *channel, mrcp_message_t *request, mrcp_message_t *response) +{ + mrcp_swift_channel_t *synth_channel = channel->method_obj; + synth_channel->paused = TRUE; + /* send asynchronous response */ + mrcp_engine_channel_message_send(channel,response); + return TRUE; +} + +/** Process RESUME request */ +static apt_bool_t mrcp_swift_channel_resume(mrcp_engine_channel_t *channel, mrcp_message_t *request, mrcp_message_t *response) +{ + mrcp_swift_channel_t *synth_channel = channel->method_obj; + synth_channel->paused = FALSE; + /* send asynchronous response */ + mrcp_engine_channel_message_send(channel,response); + return TRUE; +} + +/** Process CONTROL request */ +static apt_bool_t mrcp_swift_channel_control(mrcp_engine_channel_t *channel, mrcp_message_t *request, mrcp_message_t *response) +{ + mrcp_swift_channel_t *synth_channel = channel->method_obj; + /* set params */ + mrcp_swift_channel_params_set(synth_channel,request); + /* send asynchronous response */ + mrcp_engine_channel_message_send(channel,response); + return TRUE; +} + +/** Process MRCP channel request (asynchronous response MUST be sent)*/ +static apt_bool_t mrcp_swift_channel_request_process(mrcp_engine_channel_t *channel, mrcp_message_t *request) +{ + apt_bool_t processed = FALSE; + mrcp_message_t *response = mrcp_response_create(request,request->pool); + switch(request->start_line.method_id) { + case SYNTHESIZER_SET_PARAMS: + break; + case SYNTHESIZER_GET_PARAMS: + break; + case SYNTHESIZER_SPEAK: + processed = mrcp_swift_channel_speak(channel,request,response); + break; + case SYNTHESIZER_STOP: + processed = mrcp_swift_channel_stop(channel,request,response); + break; + case SYNTHESIZER_PAUSE: + processed = mrcp_swift_channel_pause(channel,request,response); + break; + case SYNTHESIZER_RESUME: + processed = mrcp_swift_channel_resume(channel,request,response); + break; + case SYNTHESIZER_BARGE_IN_OCCURRED: + processed = mrcp_swift_channel_stop(channel,request,response); + break; + case SYNTHESIZER_CONTROL: + processed = mrcp_swift_channel_control(channel,request,response); + break; + case SYNTHESIZER_DEFINE_LEXICON: + break; + default: + break; + } + if(processed == FALSE) { + /* send asynchronous response for not handled request */ + mrcp_engine_channel_message_send(channel,response); + } + return TRUE; +} + + + +/** Callback is called from MPF engine context to destroy any additional data associated with audio stream */ +static apt_bool_t synth_stream_destroy(mpf_audio_stream_t *stream) +{ + return TRUE; +} + +/** Callback is called from MPF engine context to perform any action before open */ +static apt_bool_t synth_stream_open(mpf_audio_stream_t *stream) +{ + return TRUE; +} + +/** Callback is called from MPF engine context to perform any action after close */ +static apt_bool_t synth_stream_close(mpf_audio_stream_t *stream) +{ + return TRUE; +} + +/** Raise SPEAK-COMPLETE event */ +static apt_bool_t synth_speak_complete_raise(mrcp_swift_channel_t *synth_channel) +{ + mrcp_message_t *message; + mrcp_synth_header_t *synth_header; + if(!synth_channel->speak_request) { + return FALSE; + } + message = mrcp_event_create( + synth_channel->speak_request, + SYNTHESIZER_SPEAK_COMPLETE, + synth_channel->speak_request->pool); + if(!message) { + return FALSE; + } + + /* get/allocate synthesizer header */ + synth_header = mrcp_resource_header_prepare(message); + if(synth_header) { + /* set completion cause */ + synth_header->completion_cause = SYNTHESIZER_COMPLETION_CAUSE_NORMAL; + mrcp_resource_header_property_add(message,SYNTHESIZER_HEADER_COMPLETION_CAUSE); + } + /* set request state */ + message->start_line.request_state = MRCP_REQUEST_STATE_COMPLETE; + + synth_channel->speak_request = NULL; + /* send asynch event */ + return mrcp_engine_channel_message_send(synth_channel->channel,message); +} + +/** Callback is called from MPF engine context to read/get new frame */ +static apt_bool_t synth_stream_read(mpf_audio_stream_t *stream, mpf_frame_t *frame) +{ + mrcp_swift_channel_t *synth_channel = stream->obj; + /* check if STOP was requested */ + if(synth_channel->stop_response) { + /* send asynchronous response to STOP request */ + mrcp_engine_channel_message_send(synth_channel->channel,synth_channel->stop_response); + synth_channel->stop_response = NULL; + synth_channel->speak_request = NULL; + synth_channel->paused = FALSE; + return TRUE; + } + + /* check if there is active SPEAK request and it isn't in paused state */ + if(synth_channel->speak_request && synth_channel->paused == FALSE) { + /* normal processing */ + mpf_buffer_frame_read(synth_channel->audio_buffer,frame); + + if((frame->type & MEDIA_FRAME_TYPE_EVENT) == MEDIA_FRAME_TYPE_EVENT) { + synth_speak_complete_raise(synth_channel); + } + } + return TRUE; +} + +/** Swift engine callback */ +static swift_result_t mrcp_swift_write_audio(swift_event *event, swift_event_t type, void *udata) +{ + void *buf; + int len; + mrcp_swift_channel_t *synth_channel = udata; + swift_event_t rv = SWIFT_SUCCESS; + + if(type & SWIFT_EVENT_END) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Swift Engine: Write End-of-Speech Event"); + mpf_buffer_event_write(synth_channel->audio_buffer,MEDIA_FRAME_TYPE_EVENT); + return rv; + } + + rv = swift_event_get_audio(event, &buf, &len); + if(!SWIFT_FAILED(rv)) { +#if 0 + /* Get the event times */ + float time_start, time_len; + swift_event_get_times(event, &time_start, &time_len); + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Swift Engine: Write Audio [%d | %0.4f | %0.4f]",len, time_start, time_len); +#endif + mpf_buffer_audio_write(synth_channel->audio_buffer,buf,len); + } + + return rv; +} + +/** Add delimiter (&) to search criteria */ +static APR_INLINE int search_criteria_delimiter_add(char *search_criteria, int size, apt_bool_t initial) +{ + if(initial == FALSE && size >= 3) { + search_criteria[0] = ' '; + search_criteria[1] = '&'; + search_criteria[2] = ' '; + return 3; + } + return 0; +} + +/** Set voice matching specified criteria */ +static apt_bool_t mrcp_swift_channel_voice_set(mrcp_swift_channel_t *synth_channel, mrcp_message_t *message) +{ + mrcp_synth_header_t *synth_header = mrcp_resource_header_get(message); + char search_criteria[1024]; + int offset = 0; + swift_voice *voice; + swift_result_t res; + + if(!synth_header) { + /* no params to set */ + return TRUE; + } + + if(mrcp_resource_header_property_check(message,SYNTHESIZER_HEADER_VOICE_NAME) == TRUE) { + offset += search_criteria_delimiter_add(search_criteria+offset,sizeof(search_criteria)-offset,(offset == 0)); + offset += apr_snprintf(search_criteria+offset,sizeof(search_criteria)-offset,"speaker/name=%s",synth_header->voice_param.name.buf); + } + if(mrcp_resource_header_property_check(message,SYNTHESIZER_HEADER_VOICE_GENDER) == TRUE) { + switch(synth_header->voice_param.gender) { + case VOICE_GENDER_MALE: + offset += search_criteria_delimiter_add(search_criteria+offset,sizeof(search_criteria)-offset,offset == 0); + offset += apr_snprintf(search_criteria+offset,sizeof(search_criteria)-offset,"speaker/gender=male"); + break; + case VOICE_GENDER_FEMALE: + offset += search_criteria_delimiter_add(search_criteria+offset,sizeof(search_criteria)-offset,offset == 0); + offset += apr_snprintf(search_criteria+offset,sizeof(search_criteria)-offset,"speaker/gender=female"); + break; + default: + break; + } + } + if(mrcp_resource_header_property_check(message,SYNTHESIZER_HEADER_VOICE_AGE) == TRUE) { + offset += search_criteria_delimiter_add(search_criteria+offset,sizeof(search_criteria)-offset,offset == 0); + offset += apr_snprintf(search_criteria+offset,sizeof(search_criteria)-offset,"speaker/age=%d",synth_header->voice_param.age); + } + if(mrcp_resource_header_property_check(message,SYNTHESIZER_HEADER_SPEECH_LANGUAGE) == TRUE) { + const char *swift_lang_name = NULL; + if(swift_speech_language_table) { + swift_lang_name = apr_table_get(swift_speech_language_table,synth_header->speech_language.buf); + } + if(!swift_lang_name) { + swift_lang_name = synth_header->speech_language.buf; + } + offset += search_criteria_delimiter_add(search_criteria+offset,sizeof(search_criteria)-offset,offset == 0); + offset += apr_snprintf(search_criteria+offset,sizeof(search_criteria)-offset,"language/name=%s",swift_lang_name); + } + + if(offset > 0) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Find Voices Matching the Criteria [%s]",search_criteria); + if((voice = swift_port_find_first_voice(synth_channel->port,search_criteria,NULL)) == NULL) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"No Swift Voice Available Matching the Criteria [%s]",search_criteria); + voice = swift_port_find_first_voice(synth_channel->port,NULL,NULL); + } + if(SWIFT_FAILED(res = swift_port_set_voice(synth_channel->port,voice)) ) { + const char *error_string = swift_strerror(res); + apt_log(APT_LOG_MARK,APT_PRIO_INFO,error_string); + return FALSE; + } + } + return TRUE; +} + +apt_bool_t swift_prosody_volume_get(const mrcp_prosody_volume_t *prosody_volume, int *volume) +{ + apt_bool_t res = FALSE; + if(prosody_volume->type == PROSODY_VOLUME_TYPE_LABEL) { + if(prosody_volume->value.label < PROSODY_VOLUME_COUNT) { + *volume = swift_prosody_volume_table[prosody_volume->value.label]; + res = TRUE; + } + } + else if(prosody_volume->type == PROSODY_VOLUME_TYPE_NUMERIC) { + *volume = (int)prosody_volume->value.numeric; + res = TRUE; + } + else if(prosody_volume->type == PROSODY_VOLUME_TYPE_RELATIVE_CHANGE) { + int def = swift_prosody_volume_table[PROSODY_VOLUME_DEFAULT]; + *volume = (int)(prosody_volume->value.relative * def); + res = TRUE; + } + return res; +} + +apt_bool_t swift_prosody_rate_get(const mrcp_prosody_rate_t *prosody_rate, int *rate) +{ + apt_bool_t res = FALSE; + if(prosody_rate->type == PROSODY_RATE_TYPE_LABEL) { + if(prosody_rate->value.label < PROSODY_RATE_COUNT) { + *rate = swift_prosody_rate_table[prosody_rate->value.label]; + res = TRUE; + } + } + else if(prosody_rate->type == PROSODY_RATE_TYPE_RELATIVE_CHANGE) { + int def = swift_prosody_rate_table[PROSODY_RATE_DEFAULT]; + *rate = (int)(prosody_rate->value.relative * def); + res = TRUE; + } + return res; +} + + +/** Set Swift port param */ +static apt_bool_t mrcp_swift_channel_param_set(mrcp_swift_channel_t *synth_channel, const char *name, swift_val *val) +{ + swift_result_t res; + if(SWIFT_FAILED(res = swift_port_set_param(synth_channel->port,name,val,synth_channel->tts_stream)) ) { + const char *error_string = swift_strerror(res); + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Swift Param %s: %s",name,error_string); + return FALSE; + } + return TRUE; +} + +/** Set Swift port params */ +static apt_bool_t mrcp_swift_channel_params_set(mrcp_swift_channel_t *synth_channel, mrcp_message_t *message) +{ + const char *name; + mrcp_synth_header_t *synth_header = mrcp_resource_header_get(message); + mrcp_generic_header_t *generic_header = mrcp_generic_header_get(message); + + if(synth_header) { + if(mrcp_resource_header_property_check(message,SYNTHESIZER_HEADER_PROSODY_VOLUME) == TRUE) { + int volume = 0; + if(swift_prosody_volume_get(&synth_header->prosody_param.volume,&volume) == TRUE) { + name = "audio/volume"; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Swift Param %s=%d",name,volume); + mrcp_swift_channel_param_set(synth_channel,name,swift_val_int(volume)); + } + } + if(mrcp_resource_header_property_check(message,SYNTHESIZER_HEADER_PROSODY_RATE) == TRUE) { + int rate = 0; + if(swift_prosody_rate_get(&synth_header->prosody_param.rate,&rate) == TRUE) { + name = "speech/rate"; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Swift Param %s=%d",name,rate); + mrcp_swift_channel_param_set(synth_channel,name,swift_val_int(rate)); + } + } + } + + if(generic_header) { + if(mrcp_generic_header_property_check(message,GENERIC_HEADER_CONTENT_TYPE) == TRUE) { + name = "tts/content-type"; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Swift Param %s=%s",name,generic_header->content_type); + mrcp_swift_channel_param_set(synth_channel,name,swift_val_string(generic_header->content_type.buf)); + } + if(mrcp_generic_header_property_check(message,GENERIC_HEADER_CONTENT_ENCODING) == TRUE) { + name = "tts/text-encoding"; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Swift Param %s=%s",name,generic_header->content_encoding); + mrcp_swift_channel_param_set(synth_channel,name,swift_val_string(generic_header->content_encoding.buf)); + } + } + + return TRUE; +} + +/** Show Swift available voices */ +static void mrcp_swift_voices_show(swift_engine *engine) +{ + swift_port *port; + swift_voice *voice; + const char *license_status; + + /* open swift port*/ + if((port = swift_port_open(engine, NULL)) == NULL) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Open Swift Port"); + return; + } + + /* find the first voice on the system */ + if((voice = swift_port_find_first_voice(port, NULL, NULL)) == NULL) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No Swift Voice Available"); + swift_port_close(port); + return; + } + /* go through all of the voices on the system and print some info about each */ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Swift Available Voices:"); + for(; voice; voice = swift_port_find_next_voice(port)) { + if(swift_voice_get_attribute(voice, "license/key")) { + license_status = "licensed"; + } + else { + license_status = "unlicensed"; + } + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"%s: %s, age %s, %s, %sHz, %s", + swift_voice_get_attribute(voice, "name"), + swift_voice_get_attribute(voice, "speaker/gender"), + swift_voice_get_attribute(voice, "speaker/age"), + swift_voice_get_attribute(voice, "language/name"), + swift_voice_get_attribute(voice, "sample-rate"), + license_status); + } + + swift_port_close(port); +} + +/** Create speech language lookup table */ +static apr_table_t* mrcp_swift_language_table_create(apr_pool_t *pool) +{ + apr_table_t *table = apr_table_make(pool,1); + if(!table) { + return NULL; + } + + apr_table_setn(table,"en-US","US English"); + apr_table_setn(table,"en-UK","UK English"); + apr_table_setn(table,"fr-CA","Canadian French"); + apr_table_setn(table,"es-MX","Americas Spanish"); + apr_table_setn(table,"de-DE","German"); + apr_table_setn(table,"it-IT","Italian"); + return table; +} diff --git a/libs/unimrcp/tests/Makefile.am b/libs/unimrcp/tests/Makefile.am new file mode 100644 index 0000000000..fc52c02681 --- /dev/null +++ b/libs/unimrcp/tests/Makefile.am @@ -0,0 +1,3 @@ +MAINTAINERCLEANFILES = Makefile.in + +SUBDIRS = apttest mpftest mrcptest rtsptest strtablegen diff --git a/libs/unimrcp/tests/apttest/Makefile.am b/libs/unimrcp/tests/apttest/Makefile.am new file mode 100644 index 0000000000..8a80cd8dc8 --- /dev/null +++ b/libs/unimrcp/tests/apttest/Makefile.am @@ -0,0 +1,11 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_srcdir)/libs/apr-toolkit/include \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) + +noinst_PROGRAMS = apttest +apttest_LDADD = $(top_builddir)/libs/apr-toolkit/libaprtoolkit.la \ + $(UNIMRCP_APR_LIBS) $(UNIMRCP_APU_LIBS) +apttest_SOURCES = src/main.c \ + src/task_suite.c \ + src/consumer_task_suite.c diff --git a/libs/unimrcp/tests/apttest/apttest.vcproj b/libs/unimrcp/tests/apttest/apttest.vcproj new file mode 100644 index 0000000000..a9e6db59dc --- /dev/null +++ b/libs/unimrcp/tests/apttest/apttest.vcproj @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/tests/apttest/src/consumer_task_suite.c b/libs/unimrcp/tests/apttest/src/consumer_task_suite.c new file mode 100644 index 0000000000..7dc3a27bf9 --- /dev/null +++ b/libs/unimrcp/tests/apttest/src/consumer_task_suite.c @@ -0,0 +1,100 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include "apt_test_suite.h" +#include "apt_consumer_task.h" +#include "apt_log.h" + +typedef struct { + apr_time_t timestamp; + int number; +} sample_msg_data_t; + +static void task_on_start_complete(apt_task_t *task) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"On Task Start"); +} + +static void task_on_terminate_complete(apt_task_t *task) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"On Task Terminate"); +} + +static apt_bool_t task_msg_process(apt_task_t *task, apt_task_msg_t *msg) +{ + sample_msg_data_t *data = (sample_msg_data_t*)msg->data; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Process Message [%d]",data->number); + return TRUE; +} + + +static apt_bool_t consumer_task_test_run(apt_test_suite_t *suite, int argc, const char * const *argv) +{ + apt_consumer_task_t *consumer_task; + apt_task_t *task; + apt_task_vtable_t *vtable; + apt_task_msg_pool_t *msg_pool; + apt_task_msg_t *msg; + sample_msg_data_t *data; + int i; + + msg_pool = apt_task_msg_pool_create_dynamic(sizeof(sample_msg_data_t),suite->pool); + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create Consumer Task"); + consumer_task = apt_consumer_task_create(NULL,msg_pool,suite->pool); + if(!consumer_task) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Consumer Task"); + return FALSE; + } + task = apt_consumer_task_base_get(consumer_task); + vtable = apt_task_vtable_get(task); + if(vtable) { + vtable->process_msg = task_msg_process; + vtable->on_start_complete = task_on_start_complete; + vtable->on_terminate_complete = task_on_terminate_complete; + } + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Start Task"); + if(apt_task_start(task) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Start Task"); + apt_task_destroy(task); + return FALSE; + } + + for(i=0; i<10; i++) { + msg = apt_task_msg_acquire(msg_pool); + msg->type = TASK_MSG_USER; + data = (sample_msg_data_t*) msg->data; + + data->number = i; + data->timestamp = apr_time_now(); + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Signal Message [%d]",data->number); + apt_task_msg_signal(task,msg); + } + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Terminate Task [wait till complete]"); + apt_task_terminate(task,TRUE); + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Destroy Task"); + apt_task_destroy(task); + return TRUE; +} + +apt_test_suite_t* consumer_task_test_suite_create(apr_pool_t *pool) +{ + apt_test_suite_t *suite = apt_test_suite_create(pool,"consumer",NULL,consumer_task_test_run); + return suite; +} diff --git a/libs/unimrcp/tests/apttest/src/main.c b/libs/unimrcp/tests/apttest/src/main.c new file mode 100644 index 0000000000..291c5fe0dc --- /dev/null +++ b/libs/unimrcp/tests/apttest/src/main.c @@ -0,0 +1,54 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "apt_test_suite.h" +#include "apt_log.h" + +apt_test_suite_t* task_test_suite_create(apr_pool_t *pool); +apt_test_suite_t* consumer_task_test_suite_create(apr_pool_t *pool); + +int main(int argc, const char * const *argv) +{ + apt_test_framework_t *test_framework; + apt_test_suite_t *test_suite; + apr_pool_t *pool; + + /* one time apr global initialization */ + if(apr_initialize() != APR_SUCCESS) { + return 0; + } + + /* create test framework */ + test_framework = apt_test_framework_create(); + pool = apt_test_framework_pool_get(test_framework); + + /* create test suites and add them to test framework */ + test_suite = task_test_suite_create(pool); + apt_test_framework_suite_add(test_framework,test_suite); + + test_suite = consumer_task_test_suite_create(pool); + apt_test_framework_suite_add(test_framework,test_suite); + + /* run tests */ + apt_test_framework_run(test_framework,argc,argv); + + /* destroy test framework */ + apt_test_framework_destroy(test_framework); + + /* final apr global termination */ + apr_terminate(); + return 0; +} diff --git a/libs/unimrcp/tests/apttest/src/task_suite.c b/libs/unimrcp/tests/apttest/src/task_suite.c new file mode 100644 index 0000000000..ae3a18b69e --- /dev/null +++ b/libs/unimrcp/tests/apttest/src/task_suite.c @@ -0,0 +1,62 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "apt_test_suite.h" +#include "apt_task.h" +#include "apt_log.h" + +static apt_bool_t task_main(apt_task_t *task) +{ + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Do the Job"); + apt_task_delay(3000); + return TRUE; +} + +static apt_bool_t task_test_run(apt_test_suite_t *suite, int argc, const char * const *argv) +{ + apt_task_t *task; + apt_task_vtable_t *vtable; + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create Task"); + task = apt_task_create(NULL,NULL,suite->pool); + if(!task) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Task"); + return FALSE; + } + vtable = apt_task_vtable_get(task); + if(vtable) { + vtable->run = task_main; + } + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Start Task"); + if(apt_task_start(task) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Start Task"); + apt_task_destroy(task); + return FALSE; + } + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Wait for Task to Complete"); + apt_task_wait_till_complete(task); + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Destroy Task"); + apt_task_destroy(task); + return TRUE; +} + +apt_test_suite_t* task_test_suite_create(apr_pool_t *pool) +{ + apt_test_suite_t *suite = apt_test_suite_create(pool,"task",NULL,task_test_run); + return suite; +} diff --git a/libs/unimrcp/tests/mpftest/Makefile.am b/libs/unimrcp/tests/mpftest/Makefile.am new file mode 100644 index 0000000000..7f5c7b3310 --- /dev/null +++ b/libs/unimrcp/tests/mpftest/Makefile.am @@ -0,0 +1,12 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_srcdir)/libs/mpf/include \ + -I$(top_srcdir)/libs/apr-toolkit/include \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) + +noinst_PROGRAMS = mpftest +mpftest_LDADD = $(top_builddir)/libs/mpf/libmpf.la \ + $(top_builddir)/libs/apr-toolkit/libaprtoolkit.la \ + $(UNIMRCP_APR_LIBS) $(UNIMRCP_APU_LIBS) +mpftest_SOURCES = src/main.c \ + src/mpf_suite.c diff --git a/libs/unimrcp/tests/mpftest/mpftest.vcproj b/libs/unimrcp/tests/mpftest/mpftest.vcproj new file mode 100644 index 0000000000..28f51e019e --- /dev/null +++ b/libs/unimrcp/tests/mpftest/mpftest.vcproj @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/tests/mpftest/src/main.c b/libs/unimrcp/tests/mpftest/src/main.c new file mode 100644 index 0000000000..3b3e843ea8 --- /dev/null +++ b/libs/unimrcp/tests/mpftest/src/main.c @@ -0,0 +1,50 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "apt_test_suite.h" +#include "apt_log.h" + +apt_test_suite_t* mpf_suite_create(apr_pool_t *pool); + +int main(int argc, const char * const *argv) +{ + apt_test_framework_t *test_framework; + apt_test_suite_t *test_suite; + apr_pool_t *pool; + + /* one time apr global initialization */ + if(apr_initialize() != APR_SUCCESS) { + return 0; + } + + /* create test framework */ + test_framework = apt_test_framework_create(); + pool = apt_test_framework_pool_get(test_framework); + + /* create test suites and add them to test framework */ + test_suite = mpf_suite_create(pool); + apt_test_framework_suite_add(test_framework,test_suite); + + /* run tests */ + apt_test_framework_run(test_framework,argc,argv); + + /* destroy test framework */ + apt_test_framework_destroy(test_framework); + + /* final apr global termination */ + apr_terminate(); + return 0; +} diff --git a/libs/unimrcp/tests/mpftest/src/mpf_suite.c b/libs/unimrcp/tests/mpftest/src/mpf_suite.c new file mode 100644 index 0000000000..af144b53d1 --- /dev/null +++ b/libs/unimrcp/tests/mpftest/src/mpf_suite.c @@ -0,0 +1,416 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include "apt_test_suite.h" +#include "mpf_engine.h" +#include "mpf_user.h" +#include "mpf_termination.h" +#include "mpf_rtp_termination_factory.h" +#include "mpf_file_termination_factory.h" +#include "mpf_audio_file_descriptor.h" +#include "mpf_rtp_descriptor.h" +#include "apt_pool.h" +#include "apt_consumer_task.h" +#include "apt_log.h" + +typedef struct mpf_suite_session_t mpf_suite_session_t; +typedef struct mpf_suite_engine_t mpf_suite_engine_t; + + +/** Test suite session */ +struct mpf_suite_session_t { + /** Pool to allocate memory from */ + apr_pool_t *pool; + + /** Media context associated with the session */ + mpf_context_t *context; + /** The first termination in the context */ + mpf_termination_t *termination1; + /** The second termination in the context */ + mpf_termination_t *termination2; + /** RTP or file termination mode */ + apt_bool_t rtp_mode; +}; + +/** Test suite engine */ +struct mpf_suite_engine_t { + /** The main task of the test engine, which sends messages to MPF engine and + * processes responses and events sent back from MPF engine */ + apt_consumer_task_t *consumer_task; + /** MPF engine task */ + apt_task_t *engine_task; + /** RTP termination factory */ + mpf_termination_factory_t *rtp_termination_factory; + /** File termination factory */ + mpf_termination_factory_t *file_termination_factory; + + /** Wait object, which is signalled to indicate shutdown */ + apr_thread_cond_t *wait_object; + /** Mutex of the wait object */ + apr_thread_mutex_t *wait_object_mutex; +}; + +static apt_bool_t mpf_test_run(apt_test_suite_t *suite, int argc, const char * const *argv); + +static void mpf_suite_on_start_complete(apt_task_t *task); +static void mpf_suite_on_terminate_complete(apt_task_t *task); +static apt_bool_t mpf_suite_msg_process(apt_task_t *task, apt_task_msg_t *msg); + +static mpf_audio_file_descriptor_t* mpf_file_reader_descriptor_create(mpf_suite_session_t *session); +static mpf_audio_file_descriptor_t* mpf_file_writer_descriptor_create(mpf_suite_session_t *session); +static mpf_rtp_stream_descriptor_t* mpf_rtp_local_descriptor_create(mpf_suite_session_t *session); +static mpf_rtp_stream_descriptor_t* mpf_rtp_remote_descriptor_create(mpf_suite_session_t *session); + + +/** Create MPF test suite */ +apt_test_suite_t* mpf_suite_create(apr_pool_t *pool) +{ + apt_test_suite_t *suite = apt_test_suite_create(pool,"mpf",NULL,mpf_test_run); + return suite; +} + +/** Run MPF test suite */ +static apt_bool_t mpf_test_run(apt_test_suite_t *suite, int argc, const char * const *argv) +{ + mpf_suite_engine_t *suite_engine; + mpf_codec_manager_t *codec_manager; + mpf_rtp_config_t *config; + mpf_engine_t *engine; + + apt_task_t *task; + apt_task_vtable_t *vtable; + apt_task_msg_pool_t *msg_pool; + + suite_engine = apr_palloc(suite->pool,sizeof(mpf_suite_engine_t)); + + engine = mpf_engine_create(suite->pool); + if(!engine) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create MPF Engine"); + return FALSE; + } + codec_manager = mpf_engine_codec_manager_create(suite->pool); + if(codec_manager) { + mpf_engine_codec_manager_register(engine,codec_manager); + } + suite_engine->engine_task = mpf_task_get(engine); + + config = mpf_rtp_config_create(suite->pool); + apt_string_set(&config->ip,"127.0.0.1"); + config->rtp_port_min = 5000; + config->rtp_port_min = 6000; + suite_engine->rtp_termination_factory = mpf_rtp_termination_factory_create(config,suite->pool); + suite_engine->file_termination_factory = mpf_file_termination_factory_create(suite->pool); + + msg_pool = apt_task_msg_pool_create_dynamic(sizeof(mpf_message_t),suite->pool); + + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create Consumer Task"); + suite_engine->consumer_task = apt_consumer_task_create(suite_engine,msg_pool,suite->pool); + if(!suite_engine->consumer_task) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Consumer Task"); + return FALSE; + } + task = apt_consumer_task_base_get(suite_engine->consumer_task); + vtable = apt_task_vtable_get(task); + if(vtable) { + vtable->process_msg = mpf_suite_msg_process; + vtable->on_start_complete = mpf_suite_on_start_complete; + vtable->on_terminate_complete = mpf_suite_on_terminate_complete; + } + + apt_task_add(task,suite_engine->engine_task); + + apr_thread_mutex_create(&suite_engine->wait_object_mutex,APR_THREAD_MUTEX_UNNESTED,suite->pool); + apr_thread_cond_create(&suite_engine->wait_object,suite->pool); + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Start Task"); + if(apt_task_start(task) == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Start Task"); + apt_task_destroy(task); + return FALSE; + } + + apr_thread_mutex_lock(suite_engine->wait_object_mutex); + apr_thread_cond_wait(suite_engine->wait_object,suite_engine->wait_object_mutex); + apr_thread_mutex_unlock(suite_engine->wait_object_mutex); + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Terminate Task [wait till complete]"); + apt_task_terminate(task,TRUE); + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Destroy Task"); + apt_task_destroy(task); + + apr_thread_cond_destroy(suite_engine->wait_object); + apr_thread_mutex_destroy(suite_engine->wait_object_mutex); + return TRUE; +} + +/** Start execution of MPF test suite scenario */ +static void mpf_suite_on_start_complete(apt_task_t *task) +{ + mpf_suite_session_t *session; + apt_task_t *consumer_task; + mpf_suite_engine_t *suite_engine; + apt_task_msg_t *msg; + mpf_message_t *mpf_message; + apr_pool_t *pool = NULL; + + consumer_task = apt_task_object_get(task); + suite_engine = apt_task_object_get(consumer_task); + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"On MPF Suite Start"); + pool = apt_pool_create(); + session = apr_palloc(pool,sizeof(mpf_suite_session_t)); + session->pool = pool; + session->context = NULL; + session->termination1 = NULL; + session->termination2 = NULL; + session->rtp_mode = TRUE; + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Create MPF Context"); + session->context = mpf_context_create(session,2,pool); + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Create Termination [1]"); + session->termination1 = mpf_termination_create(suite_engine->file_termination_factory,session,session->pool); + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Add Termination [1]"); + msg = apt_task_msg_get(task); + msg->type = TASK_MSG_USER; + mpf_message = (mpf_message_t*) msg->data; + + mpf_message->message_type = MPF_MESSAGE_TYPE_REQUEST; + mpf_message->command_id = MPF_COMMAND_ADD; + mpf_message->context = session->context; + mpf_message->termination = session->termination1; + mpf_message->descriptor = mpf_file_reader_descriptor_create(session); + apt_task_msg_signal(suite_engine->engine_task,msg); + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Create Termination [2]"); + if(session->rtp_mode == TRUE) { + session->termination2 = mpf_termination_create(suite_engine->rtp_termination_factory,session,session->pool); + } + else { + session->termination2 = mpf_termination_create(suite_engine->file_termination_factory,session,session->pool); + } + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Add Termination [2]"); + msg = apt_task_msg_get(task); + msg->type = TASK_MSG_USER; + mpf_message = (mpf_message_t*) msg->data; + + mpf_message->message_type = MPF_MESSAGE_TYPE_REQUEST; + mpf_message->command_id = MPF_COMMAND_ADD; + mpf_message->context = session->context; + mpf_message->termination = session->termination2; + if(session->rtp_mode == TRUE) { + mpf_message->descriptor = mpf_rtp_local_descriptor_create(session); + } + else { + mpf_message->descriptor = mpf_file_writer_descriptor_create(session); + } + apt_task_msg_signal(suite_engine->engine_task,msg); +} + +/** Execution of MPF test suite scenario is terminated */ +static void mpf_suite_on_terminate_complete(apt_task_t *task) +{ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"On MPF Suite Terminate"); +} + +/** Process task messages */ +static apt_bool_t mpf_suite_msg_process(apt_task_t *task, apt_task_msg_t *msg) +{ + const mpf_message_t *mpf_message = (const mpf_message_t*) msg->data; + if(mpf_message->message_type == MPF_MESSAGE_TYPE_RESPONSE) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Process MPF Response"); + if(mpf_message->command_id == MPF_COMMAND_ADD) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"On Add Termination"); + if(mpf_message->termination) { + mpf_suite_session_t *session; + session = mpf_termination_object_get(mpf_message->termination); + if(session->termination2 == mpf_message->termination && session->rtp_mode == TRUE) { + apt_task_msg_t *msg; + mpf_message_t *request; + apt_task_t *consumer_task; + mpf_suite_engine_t *suite_engine; + + consumer_task = apt_task_object_get(task); + suite_engine = apt_task_object_get(consumer_task); + + msg = apt_task_msg_get(task); + msg->type = TASK_MSG_USER; + request = (mpf_message_t*) msg->data; + + request->message_type = MPF_MESSAGE_TYPE_REQUEST; + request->command_id = MPF_COMMAND_MODIFY; + request->context = session->context; + request->termination = session->termination2; + request->descriptor = mpf_rtp_remote_descriptor_create(session); + apt_task_msg_signal(suite_engine->engine_task,msg); + } + } + } + else if(mpf_message->command_id == MPF_COMMAND_SUBTRACT) { + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"On Subtract Termination"); + if(mpf_message->termination) { + mpf_suite_session_t *session; + session = mpf_termination_object_get(mpf_message->termination); + if(session->termination1 == mpf_message->termination) { + session->termination1 = NULL; + } + if(session->termination2 == mpf_message->termination) { + session->termination2 = NULL; + } + mpf_termination_destroy(mpf_message->termination); + + if(!session->termination1 && !session->termination2) { + apt_task_t *consumer_task; + mpf_suite_engine_t *suite_engine; + + mpf_context_destroy(session->context); + session->context = NULL; + apr_pool_destroy(session->pool); + + consumer_task = apt_task_object_get(task); + suite_engine = apt_task_object_get(consumer_task); + + apr_thread_mutex_lock(suite_engine->wait_object_mutex); + apr_thread_cond_signal(suite_engine->wait_object); + apr_thread_mutex_unlock(suite_engine->wait_object_mutex); + } + } + } + } + else if(mpf_message->message_type == MPF_MESSAGE_TYPE_EVENT) { + apt_task_t *consumer_task; + mpf_suite_engine_t *suite_engine; + apt_task_msg_t *msg; + mpf_message_t *request; + mpf_suite_session_t *session; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Process MPF Event"); + if(mpf_message->termination) { + session = mpf_termination_object_get(mpf_message->termination); + if(session->termination1) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Subtract Termination [1]"); + msg = apt_task_msg_get(task); + msg->type = TASK_MSG_USER; + request = (mpf_message_t*) msg->data; + + request->message_type = MPF_MESSAGE_TYPE_REQUEST; + request->command_id = MPF_COMMAND_SUBTRACT; + request->context = session->context; + request->termination = session->termination1; + + consumer_task = apt_task_object_get(task); + suite_engine = apt_task_object_get(consumer_task); + apt_task_msg_signal(suite_engine->engine_task,msg); + } + if(session->termination2) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Subtract Termination [2]"); + msg = apt_task_msg_get(task); + msg->type = TASK_MSG_USER; + request = (mpf_message_t*) msg->data; + + request->message_type = MPF_MESSAGE_TYPE_REQUEST; + request->command_id = MPF_COMMAND_SUBTRACT; + request->context = session->context; + request->termination = session->termination2; + + consumer_task = apt_task_object_get(task); + suite_engine = apt_task_object_get(consumer_task); + apt_task_msg_signal(suite_engine->engine_task,msg); + } + } + } + + return TRUE; +} + +/** Create sample file reader descriptor */ +static mpf_audio_file_descriptor_t* mpf_file_reader_descriptor_create(mpf_suite_session_t *session) +{ + mpf_codec_descriptor_t *codec_descriptor; + mpf_audio_file_descriptor_t *descriptor = apr_palloc(session->pool,sizeof(mpf_audio_file_descriptor_t)); + descriptor->mask = FILE_READER; + descriptor->read_handle = fopen("demo.pcm","rb"); + descriptor->write_handle = NULL; + + codec_descriptor = &descriptor->codec_descriptor; + codec_descriptor->payload_type = 11; + apt_string_set(&codec_descriptor->name,"L16"); + codec_descriptor->sampling_rate = 8000; + codec_descriptor->channel_count = 1; + return descriptor; +} + +/** Create sample file writer descriptor */ +static mpf_audio_file_descriptor_t* mpf_file_writer_descriptor_create(mpf_suite_session_t *session) +{ + mpf_codec_descriptor_t *codec_descriptor; + mpf_audio_file_descriptor_t *descriptor = apr_palloc(session->pool,sizeof(mpf_audio_file_descriptor_t)); + descriptor->mask = FILE_WRITER; + descriptor->max_write_size = 500000; /* 500Kb */ + descriptor->write_handle = fopen("demo_out.pcm","wb"); + descriptor->read_handle = NULL; + + codec_descriptor = &descriptor->codec_descriptor; + codec_descriptor->payload_type = 11; + apt_string_set(&codec_descriptor->name,"L16"); + codec_descriptor->sampling_rate = 8000; + codec_descriptor->channel_count = 1; + return descriptor; +} + +/** Create sample RTP local descriptor */ +static mpf_rtp_stream_descriptor_t* mpf_rtp_local_descriptor_create(mpf_suite_session_t *session) +{ + mpf_rtp_stream_descriptor_t *descriptor = apr_palloc(session->pool,sizeof(mpf_rtp_stream_descriptor_t)); + mpf_rtp_stream_descriptor_init(descriptor); + descriptor->local = apr_palloc(session->pool,sizeof(mpf_rtp_media_descriptor_t)); + mpf_rtp_media_descriptor_init(descriptor->local); + descriptor->local->mode = STREAM_MODE_NONE; + apt_string_set(&descriptor->local->base.ip,"127.0.0.1"); + descriptor->local->base.port = 5000; + return descriptor; +} + +/** Create sample RTP remote descriptor */ +static mpf_rtp_stream_descriptor_t* mpf_rtp_remote_descriptor_create(mpf_suite_session_t *session) +{ + mpf_codec_list_t *codec_list; + mpf_codec_descriptor_t *codec_descriptor; + mpf_rtp_stream_descriptor_t *descriptor = apr_palloc(session->pool,sizeof(mpf_rtp_stream_descriptor_t)); + mpf_rtp_stream_descriptor_init(descriptor); + descriptor->remote = apr_palloc(session->pool,sizeof(mpf_rtp_media_descriptor_t)); + mpf_rtp_media_descriptor_init(descriptor->remote); + descriptor->remote->mode = STREAM_MODE_SEND_RECEIVE; + apt_string_set(&descriptor->remote->base.ip,"127.0.0.1"); + descriptor->remote->base.port = 5002; + codec_list = &descriptor->remote->codec_list; + mpf_codec_list_init(codec_list,2,session->pool); + codec_descriptor = mpf_codec_list_add(codec_list); + if(codec_descriptor) { + codec_descriptor->payload_type = 0; + } + codec_descriptor = mpf_codec_list_add(codec_list); + if(codec_descriptor) { + codec_descriptor->payload_type = 96; + apt_string_set(&codec_descriptor->name,"PCMU"); + codec_descriptor->sampling_rate = 16000; + codec_descriptor->channel_count = 1; + } + + return descriptor; +} diff --git a/libs/unimrcp/tests/mrcptest/Makefile.am b/libs/unimrcp/tests/mrcptest/Makefile.am new file mode 100644 index 0000000000..9a4c634e2b --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/Makefile.am @@ -0,0 +1,16 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_srcdir)/libs/mrcp/include \ + -I$(top_srcdir)/libs/mrcp/message/include \ + -I$(top_srcdir)/libs/mrcp/control/include \ + -I$(top_srcdir)/libs/mrcp/resources/include \ + -I$(top_srcdir)/libs/apr-toolkit/include \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) + +noinst_PROGRAMS = mrcptest +mrcptest_LDADD = $(top_builddir)/libs/mrcp/libmrcp.la \ + $(top_builddir)/libs/apr-toolkit/libaprtoolkit.la \ + $(UNIMRCP_APR_LIBS) $(UNIMRCP_APU_LIBS) +mrcptest_SOURCES = src/main.c \ + src/parse_gen_suite.c \ + src/set_get_suite.c diff --git a/libs/unimrcp/tests/mrcptest/mrcptest.vcproj b/libs/unimrcp/tests/mrcptest/mrcptest.vcproj new file mode 100644 index 0000000000..9a262bd1f2 --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/mrcptest.vcproj @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/tests/mrcptest/src/main.c b/libs/unimrcp/tests/mrcptest/src/main.c new file mode 100644 index 0000000000..a93fd33c4c --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/src/main.c @@ -0,0 +1,53 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "apt_test_suite.h" +#include "apt_log.h" + +apt_test_suite_t* parse_gen_test_suite_create(apr_pool_t *pool); +apt_test_suite_t* set_get_test_suite_create(apr_pool_t *pool); + +int main(int argc, const char * const *argv) +{ + apt_test_framework_t *test_framework; + apt_test_suite_t *test_suite; + apr_pool_t *pool; + + /* one time apr global initialization */ + if(apr_initialize() != APR_SUCCESS) { + return 0; + } + + /* create test framework */ + test_framework = apt_test_framework_create(); + pool = apt_test_framework_pool_get(test_framework); + + /* create test suites and add them to test framework */ + test_suite = set_get_test_suite_create(pool); + apt_test_framework_suite_add(test_framework,test_suite); + test_suite = parse_gen_test_suite_create(pool); + apt_test_framework_suite_add(test_framework,test_suite); + + /* run tests */ + apt_test_framework_run(test_framework,argc,argv); + + /* destroy test framework */ + apt_test_framework_destroy(test_framework); + + /* final apr global termination */ + apr_terminate(); + return 0; +} diff --git a/libs/unimrcp/tests/mrcptest/src/parse_gen_suite.c b/libs/unimrcp/tests/mrcptest/src/parse_gen_suite.c new file mode 100644 index 0000000000..11aeb97695 --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/src/parse_gen_suite.c @@ -0,0 +1,193 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include +#include "apt_test_suite.h" +#include "apt_log.h" +#include "mrcp_default_factory.h" +#include "mrcp_message.h" +#include "mrcp_stream.h" + +static apt_bool_t test_stream_generate(mrcp_generator_t *generator, mrcp_message_t *message) +{ + char buffer[500]; + apt_text_stream_t stream; + mrcp_stream_result_e result; + apt_bool_t continuation; + + mrcp_generator_message_set(generator,message); + do { + apt_text_stream_init(&stream,buffer,sizeof(buffer)-1); + continuation = FALSE; + result = mrcp_generator_run(generator,&stream); + if(result == MRCP_STREAM_MESSAGE_COMPLETE) { + stream.text.length = stream.pos - stream.text.buf; + *stream.pos = '\0'; + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Generated MRCP Stream [%lu bytes]\n%s",stream.text.length,stream.text.buf); + } + else if(result == MRCP_STREAM_MESSAGE_TRUNCATED) { + *stream.pos = '\0'; + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Generated MRCP Stream [%lu bytes] continuation awaiting\n%s",stream.text.length,stream.text.buf); + continuation = TRUE; + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Generate MRCP Stream"); + } + } + while(continuation == TRUE); + return TRUE; +} + +static apt_bool_t mrcp_message_handler(void *obj, mrcp_message_t *message, mrcp_stream_result_e result) +{ + if(result == MRCP_STREAM_MESSAGE_COMPLETE) { + /* message is completely parsed */ + mrcp_generator_t *generator = obj; + test_stream_generate(generator,message); + } + return TRUE; +} + +static apt_bool_t resource_name_read(apr_file_t *file, mrcp_parser_t *parser) +{ + char buffer[100]; + apt_text_stream_t stream; + apt_bool_t status = FALSE; + apt_text_stream_init(&stream,buffer,sizeof(buffer)-1); + if(apr_file_read(file,stream.pos,&stream.text.length) != APR_SUCCESS) { + return FALSE; + } + + /* skip the first line in a test file, which indicates resource name */ + if(*stream.pos =='/' && *(stream.pos+1)=='/') { + apt_str_t line; + stream.pos += 2; + if(apt_text_line_read(&stream,&line) == TRUE) { + apr_off_t offset = stream.pos - stream.text.buf; + apr_file_seek(file,APR_SET,&offset); + mrcp_parser_resource_name_set(parser,&line); + status = TRUE; + } + } + return status; +} + +static apt_bool_t test_file_process(apt_test_suite_t *suite, mrcp_resource_factory_t *factory, mrcp_version_e version, const char *file_path) +{ + apr_file_t *file; + char buffer[500]; + apt_text_stream_t stream; + mrcp_parser_t *parser; + mrcp_generator_t *generator; + apr_size_t length; + apr_size_t offset; + apt_str_t resource_name; + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Open File [%s]",file_path); + if(apr_file_open(&file,file_path,APR_FOPEN_READ | APR_FOPEN_BINARY,APR_OS_DEFAULT,suite->pool) != APR_SUCCESS) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Open File"); + return FALSE; + } + + parser = mrcp_parser_create(factory,suite->pool); + generator = mrcp_generator_create(factory,suite->pool); + + apt_string_reset(&resource_name); + if(version == MRCP_VERSION_1) { + resource_name_read(file,parser); + } + + apt_text_stream_init(&stream,buffer,sizeof(buffer)-1); + do { + /* init length of the stream */ + stream.text.length = sizeof(buffer)-1; + /* calculate offset remaining from the previous receive / if any */ + offset = stream.pos - stream.text.buf; + /* calculate available length */ + length = stream.text.length - offset; + + if(apr_file_read(file,stream.pos,&length) != APR_SUCCESS) { + break; + } + /* calculate actual length of the stream */ + stream.text.length = offset + length; + stream.pos[length] = '\0'; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Parse MRCP Stream [%lu bytes]\n%s",length,stream.pos); + + /* reset pos */ + stream.pos = stream.text.buf; + mrcp_stream_walk(parser,&stream,mrcp_message_handler,generator); + } + while(apr_file_eof(file) != APR_EOF); + + apr_file_close(file); + return TRUE; +} + +static apt_bool_t test_dir_process(apt_test_suite_t *suite, mrcp_resource_factory_t *factory, mrcp_version_e version) +{ + apr_status_t rv; + apr_dir_t *dir; + + const char *dir_name = "v2"; + if(version == MRCP_VERSION_1) { + dir_name = "v1"; + } + if(apr_dir_open(&dir,dir_name,suite->pool) != APR_SUCCESS) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot Open Directory [%s]",dir_name); + return FALSE; + } + + do { + apr_finfo_t finfo; + rv = apr_dir_read(&finfo,APR_FINFO_DIRENT,dir); + if(rv == APR_SUCCESS) { + if(finfo.filetype == APR_REG && finfo.name) { + char *file_path; + apr_filepath_merge(&file_path,dir_name,finfo.name,0,suite->pool); + test_file_process(suite,factory,version,file_path); + printf("\nPress ENTER to continue\n"); + getchar(); + } + } + } + while(rv == APR_SUCCESS); + + apr_dir_close(dir); + return TRUE; +} + +static apt_bool_t parse_gen_test_run(apt_test_suite_t *suite, int argc, const char * const *argv) +{ + mrcp_resource_factory_t *factory = mrcp_default_factory_create(suite->pool); + if(!factory) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Resource Factory"); + return FALSE; + } + + test_dir_process(suite,factory,MRCP_VERSION_2); + test_dir_process(suite,factory,MRCP_VERSION_1); + + mrcp_resource_factory_destroy(factory); + return TRUE; +} + +apt_test_suite_t* parse_gen_test_suite_create(apr_pool_t *pool) +{ + apt_test_suite_t *suite = apt_test_suite_create(pool,"parse-gen",NULL,parse_gen_test_run); + return suite; +} diff --git a/libs/unimrcp/tests/mrcptest/src/set_get_suite.c b/libs/unimrcp/tests/mrcptest/src/set_get_suite.c new file mode 100644 index 0000000000..830cba3fde --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/src/set_get_suite.c @@ -0,0 +1,309 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "apt_test_suite.h" +#include "apt_log.h" +/* common includes */ +#include "mrcp_default_factory.h" +#include "mrcp_message.h" +#include "mrcp_generic_header.h" +/* synthesizer includes */ +#include "mrcp_synth_header.h" +#include "mrcp_synth_resource.h" +/* recognizer includes */ +#include "mrcp_recog_header.h" +#include "mrcp_recog_resource.h" + +#define SAMPLE_VOICE_AGE 28 +#define SAMPLE_CONTENT_TYPE "application/synthesis+ssml" +#define SAMPLE_CONTENT_ID "123456" +#define SAMPLE_CONTENT "SSML content goes here" +#define SAMPLE_PARAM_NAME "SampleParamName" +#define SAMPLE_PARAM_VALUE "SampleParamValue" + +/* Create SPEAK request */ +static mrcp_message_t* speak_request_create(mrcp_resource_factory_t *factory, apr_pool_t *pool) +{ + mrcp_message_t *message; + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create SPEAK Request"); + message = mrcp_request_create(MRCP_SYNTHESIZER_RESOURCE,SYNTHESIZER_SPEAK,pool); + mrcp_message_resourcify_by_id(factory,message); + if(message) { + mrcp_generic_header_t *generic_header; + mrcp_synth_header_t *synth_header; + /* get/allocate generic header */ + generic_header = mrcp_generic_header_prepare(message); + if(generic_header) { + /* set generic header fields */ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Set Content-Type: %s",SAMPLE_CONTENT_TYPE); + apt_string_assign(&generic_header->content_type,SAMPLE_CONTENT_TYPE,message->pool); + mrcp_generic_header_property_add(message,GENERIC_HEADER_CONTENT_TYPE); + } + /* get/allocate synthesizer header */ + synth_header = mrcp_resource_header_prepare(message); + if(synth_header) { + /* set synthesizer header fields */ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Set Voice-Age: %d",SAMPLE_VOICE_AGE); + synth_header->voice_param.age = SAMPLE_VOICE_AGE; + mrcp_resource_header_property_add(message,SYNTHESIZER_HEADER_VOICE_AGE); + } + /* set message body */ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Set Body: %s",SAMPLE_CONTENT); + apt_string_assign(&message->body,SAMPLE_CONTENT,message->pool); + } + return message; +} + +/* Test SPEAK request */ +static apt_bool_t speak_request_test(mrcp_resource_factory_t *factory, mrcp_message_t *message) +{ + apt_bool_t res; + mrcp_generic_header_t *generic_header; + mrcp_synth_header_t *synth_header; + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Test SPEAK Request"); + res = FALSE; + /* get generic header */ + generic_header = mrcp_generic_header_get(message); + if(generic_header) { + /* test content type header */ + if(mrcp_generic_header_property_check(message,GENERIC_HEADER_CONTENT_TYPE) == TRUE) { + if(strncasecmp(generic_header->content_type.buf,SAMPLE_CONTENT_TYPE,generic_header->content_type.length) == 0) { + /* OK */ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Get Content-Type: %s",generic_header->content_type.buf); + res = TRUE; + } + } + } + if(res == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Test Generic Header"); + return FALSE; + } + + res = FALSE; + /* get synthesizer header */ + synth_header = mrcp_resource_header_get(message); + if(synth_header) { + /* test voice age header */ + if(mrcp_resource_header_property_check(message,SYNTHESIZER_HEADER_VOICE_AGE) == TRUE) { + if(synth_header->voice_param.age == SAMPLE_VOICE_AGE) { + /* OK */ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Get Voice-Age: %d",synth_header->voice_param.age); + res = TRUE; + } + } + } + if(res == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Test Synthesizer Header"); + return FALSE; + } + + if(strncasecmp(message->body.buf,SAMPLE_CONTENT,message->body.length) != 0) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Test Message Body"); + return FALSE; + } + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Get Body: %s",message->body.buf); + return TRUE; +} + +/* Create SPEAK response */ +static mrcp_message_t* speak_response_create(mrcp_resource_factory_t *factory, const mrcp_message_t *request) +{ + mrcp_message_t *response; + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create SPEAK Response"); + response = mrcp_response_create(request,request->pool); + if(response) { + /* set IN-PROGRESS state */ + response->start_line.request_state = MRCP_REQUEST_STATE_INPROGRESS; + } + return response; +} + +/* Create SPEAK-COMPLETE event */ +static mrcp_message_t* speak_event_create(mrcp_resource_factory_t *factory, const mrcp_message_t *request) +{ + mrcp_message_t *event_message; + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create SPEAK-COMPLETE Event"); + event_message = mrcp_event_create(request,SYNTHESIZER_SPEAK_COMPLETE,request->pool); + if(event_message) { + /* get/allocate synthesizer header */ + mrcp_synth_header_t *synth_header = mrcp_resource_header_prepare(event_message); + if(synth_header) { + /* set completion cause */ + synth_header->completion_cause = SYNTHESIZER_COMPLETION_CAUSE_NORMAL; + mrcp_resource_header_property_add(event_message,SYNTHESIZER_HEADER_COMPLETION_CAUSE); + } + /* set request state */ + event_message->start_line.request_state = MRCP_REQUEST_STATE_COMPLETE; + } + return event_message; +} + + +/* Create GET-PARAMS request */ +static mrcp_message_t* get_params_request_create(mrcp_resource_factory_t *factory, apr_pool_t *pool) +{ + mrcp_message_t *message; + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create GET-PARAMS Request"); + message = mrcp_request_create(MRCP_SYNTHESIZER_RESOURCE,SYNTHESIZER_GET_PARAMS,pool); + mrcp_message_resourcify_by_id(factory,message); + if(message) { + apt_str_t param_name; + apt_str_t param_value; + mrcp_generic_header_t *generic_header; + mrcp_synth_header_t *synth_header; + /* get/allocate generic header */ + generic_header = mrcp_generic_header_prepare(message); + if(generic_header) { + /* set content id empty header */ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Set Content-ID: "); + mrcp_generic_header_name_property_add(message,GENERIC_HEADER_CONTENT_ID); + + /* set vendor specific params header */ + generic_header->vendor_specific_params = apt_pair_array_create(1,pool); + apt_string_set(¶m_name,SAMPLE_PARAM_NAME); + apt_string_reset(¶m_value); + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Set Vendor-Specific-Params: %s",param_name.buf); + apt_pair_array_append(generic_header->vendor_specific_params,¶m_name,¶m_value,pool); + mrcp_generic_header_property_add(message,GENERIC_HEADER_VENDOR_SPECIFIC_PARAMS); + } + /* get/allocate synthesizer header */ + synth_header = mrcp_resource_header_prepare(message); + if(synth_header) { + /* set voice age empty header */ + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Set Voice-Age: "); + mrcp_resource_header_name_property_add(message,SYNTHESIZER_HEADER_VOICE_AGE); + } + } + return message; +} + +/* Create GET-PARAMS response */ +static mrcp_message_t* get_params_response_create(mrcp_resource_factory_t *factory, mrcp_message_t *request) +{ + apt_bool_t res; + mrcp_message_t *response; + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create GET-PARAMS Response"); + response = mrcp_response_create(request,request->pool); + if(response) { + mrcp_generic_header_t *generic_header; + mrcp_synth_header_t *synth_header; + res = FALSE; + /* get generic header */ + generic_header = mrcp_generic_header_get(request); + if(generic_header) { + mrcp_generic_header_t *res_generic_header = mrcp_generic_header_prepare(response); + /* test content id header */ + if(mrcp_generic_header_property_check(request,GENERIC_HEADER_CONTENT_ID) == TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Set Content-ID: %s",SAMPLE_CONTENT_ID); + apt_string_assign(&res_generic_header->content_id,SAMPLE_CONTENT_ID,response->pool); + mrcp_generic_header_property_add(response,GENERIC_HEADER_CONTENT_ID); + res = TRUE; + } + /* test vendor specific header */ + if(mrcp_generic_header_property_check(request,GENERIC_HEADER_VENDOR_SPECIFIC_PARAMS) == TRUE) { + apt_str_t name; + const apt_pair_t *pair; + res_generic_header->vendor_specific_params = apt_pair_array_create(1,response->pool); + apt_string_set(&name,SAMPLE_PARAM_NAME); + pair = apt_pair_array_find(generic_header->vendor_specific_params,&name); + if(pair) { + apt_str_t value; + apt_string_set(&value,SAMPLE_PARAM_VALUE); + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Set Vendor-Specific-Params: %s=%s",name.buf,value.buf); + apt_pair_array_append(res_generic_header->vendor_specific_params,&name,&value,response->pool); + } + mrcp_generic_header_property_add(response,GENERIC_HEADER_VENDOR_SPECIFIC_PARAMS); + res = TRUE; + } + } + + if(res == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Test Generic Header"); + return NULL; + } + + res = FALSE; + /* get synthesizer header */ + synth_header = mrcp_resource_header_get(request); + if(synth_header) { + mrcp_synth_header_t *res_synth_header = mrcp_resource_header_prepare(response); + /* test voice age header */ + if(mrcp_resource_header_property_check(request,SYNTHESIZER_HEADER_VOICE_AGE) == TRUE) { + res_synth_header->voice_param.age = SAMPLE_VOICE_AGE; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Set Voice-Age: %d",res_synth_header->voice_param.age); + mrcp_resource_header_property_add(response,SYNTHESIZER_HEADER_VOICE_AGE); + res = TRUE; + } + } + if(res == FALSE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Test Synthesizer Header"); + return NULL; + } + + } + return response; +} + + + + +static apt_bool_t speak_test_run(apt_test_suite_t *suite, mrcp_resource_factory_t *factory) +{ + mrcp_message_t *message = speak_request_create(factory,suite->pool); + if(!message) { + return FALSE; + } + + if(speak_request_test(factory,message) != TRUE) { + return FALSE; + } + + speak_response_create(factory,message); + speak_event_create(factory,message); + return TRUE; +} + +static apt_bool_t get_params_test_run(apt_test_suite_t *suite, mrcp_resource_factory_t *factory) +{ + mrcp_message_t *message = get_params_request_create(factory,suite->pool); + if(!message) { + return FALSE; + } + + get_params_response_create(factory,message); + return TRUE; +} + +static apt_bool_t set_get_test_run(apt_test_suite_t *suite, int argc, const char * const *argv) +{ + mrcp_resource_factory_t *factory = mrcp_default_factory_create(suite->pool); + if(!factory) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Resource Factory"); + return FALSE; + } + + speak_test_run(suite,factory); + get_params_test_run(suite,factory); + + mrcp_resource_factory_destroy(factory); + return TRUE; +} + +apt_test_suite_t* set_get_test_suite_create(apr_pool_t *pool) +{ + apt_test_suite_t *suite = apt_test_suite_create(pool,"set-gen",NULL,set_get_test_run); + return suite; +} diff --git a/libs/unimrcp/tests/mrcptest/v1/definegrammar.msg b/libs/unimrcp/tests/mrcptest/v1/definegrammar.msg new file mode 100644 index 0000000000..1ea765535a --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/v1/definegrammar.msg @@ -0,0 +1,18 @@ +//speechrecog +DEFINE-GRAMMAR 543257 MRCP/1.0 +Content-Type:application/grammar+xml +Content-Id:request1@form-level.store +Content-Length:309 + + + + + + + + + + oui + yes + + \ No newline at end of file diff --git a/libs/unimrcp/tests/mrcptest/v1/getparams.msg b/libs/unimrcp/tests/mrcptest/v1/getparams.msg new file mode 100644 index 0000000000..61ef0b9f93 --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/v1/getparams.msg @@ -0,0 +1,5 @@ +//speechsynth +GET-PARAMS 543256 MRCP/1.0 +Voice-gender: +Voice-variant: + diff --git a/libs/unimrcp/tests/mrcptest/v1/multi.msg b/libs/unimrcp/tests/mrcptest/v1/multi.msg new file mode 100644 index 0000000000..8a1f44e772 --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/v1/multi.msg @@ -0,0 +1,27 @@ +//speechsynth +GET-PARAMS 543256 MRCP/1.0 +Voice-gender: +Voice-variant: + +SET-PARAMS 543256 MRCP/1.0 +Voice-gender:female +Voice-variant:3 + +SPEAK 543257 MRCP/1.0 +Kill-On-Barge-In:false +Voice-gender:neutral +Prosody-volume:medium +Content-Type:application/synthesis+ssml +Content-Length:412 + + + + + You have 4 new messages. + The first is from Stephanie Williams + and arrived at + 3:45pm. + + The subject is ski trip + + \ No newline at end of file diff --git a/libs/unimrcp/tests/mrcptest/v1/pause.msg b/libs/unimrcp/tests/mrcptest/v1/pause.msg new file mode 100644 index 0000000000..1ba3d79deb --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/v1/pause.msg @@ -0,0 +1,3 @@ +//speechsynth +PAUSE 543258 MRCP/1.0 + diff --git a/libs/unimrcp/tests/mrcptest/v1/recognitioncomplete.msg b/libs/unimrcp/tests/mrcptest/v1/recognitioncomplete.msg new file mode 100644 index 0000000000..054700a496 --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/v1/recognitioncomplete.msg @@ -0,0 +1,20 @@ +//speechrecog +RECOGNITION-COMPLETE 543257 COMPLETE MRCP/1.0 +Completion-Cause:000 success +Waveform-URL:http://web.media.com/session123/audio.wav +Content-Type:application/x-nlsml +Content-Length:446 + + + + + + + Andre Roy + + + may I speak to Andre Roy + + \ No newline at end of file diff --git a/libs/unimrcp/tests/mrcptest/v1/recognize.msg b/libs/unimrcp/tests/mrcptest/v1/recognize.msg new file mode 100644 index 0000000000..0f1c55d6d3 --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/v1/recognize.msg @@ -0,0 +1,30 @@ +//speechrecog +RECOGNIZE 543257 MRCP/1.0 +Confidence-Threshold:90 +Content-Type:application/grammar+xml +Content-Id:request1@form-level.store +Content-Length:608 + + + + + + + + + + oui + yes + + + + + + may I speak to + + Michel Tremblay + Andre Roy + + + + \ No newline at end of file diff --git a/libs/unimrcp/tests/mrcptest/v1/response.msg b/libs/unimrcp/tests/mrcptest/v1/response.msg new file mode 100644 index 0000000000..744fe11ec7 --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/v1/response.msg @@ -0,0 +1,3 @@ +//speechsynth +MRCP/1.0 543257 200 IN-PROGRESS + diff --git a/libs/unimrcp/tests/mrcptest/v1/resume.msg b/libs/unimrcp/tests/mrcptest/v1/resume.msg new file mode 100644 index 0000000000..25bd8ab873 --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/v1/resume.msg @@ -0,0 +1,3 @@ +//speechsynth +RESUME 543260 MRCP/1.0 + diff --git a/libs/unimrcp/tests/mrcptest/v1/setparams.msg b/libs/unimrcp/tests/mrcptest/v1/setparams.msg new file mode 100644 index 0000000000..4fad0b2082 --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/v1/setparams.msg @@ -0,0 +1,5 @@ +//speechsynth +SET-PARAMS 543256 MRCP/1.0 +Voice-gender:female +Voice-variant:3 + diff --git a/libs/unimrcp/tests/mrcptest/v1/speak.msg b/libs/unimrcp/tests/mrcptest/v1/speak.msg new file mode 100644 index 0000000000..d0f7d38db5 --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/v1/speak.msg @@ -0,0 +1,19 @@ +//speechsynth +SPEAK 543257 MRCP/1.0 +Kill-On-Barge-In:false +Voice-gender:neutral +Prosody-volume:medium +Content-Type:application/synthesis+ssml +Content-Length:412 + + + + + You have 4 new messages. + The first is from Stephanie Williams + and arrived at + 3:45pm. + + The subject is ski trip + + \ No newline at end of file diff --git a/libs/unimrcp/tests/mrcptest/v1/speakcomplete.msg b/libs/unimrcp/tests/mrcptest/v1/speakcomplete.msg new file mode 100644 index 0000000000..52cadadbec --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/v1/speakcomplete.msg @@ -0,0 +1,4 @@ +//speechsynth +SPEAK-COMPLETE 543260 COMPLETE MRCP/1.0 +Completion-Cause:000 normal + diff --git a/libs/unimrcp/tests/mrcptest/v1/stop.msg b/libs/unimrcp/tests/mrcptest/v1/stop.msg new file mode 100644 index 0000000000..2f44e1f9aa --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/v1/stop.msg @@ -0,0 +1,3 @@ +//speechsynth +STOP 543258 MRCP/1.0 + diff --git a/libs/unimrcp/tests/mrcptest/v2/definegrammar.msg b/libs/unimrcp/tests/mrcptest/v2/definegrammar.msg new file mode 100644 index 0000000000..e96d3d3fa7 --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/v2/definegrammar.msg @@ -0,0 +1,30 @@ +MRCP/2.0 865 DEFINE-GRAMMAR 543257 +Channel-Identifier:32AECB23433801@speechrecog +Content-Type:application/srgs+xml +Content-ID: +Content-Length:685 + + + + + + + + + + oui + yes + + + + + + may I speak to + + Michel Tremblay + Andre Roy + + + + \ No newline at end of file diff --git a/libs/unimrcp/tests/mrcptest/v2/getparams.msg b/libs/unimrcp/tests/mrcptest/v2/getparams.msg new file mode 100644 index 0000000000..9b02a762ae --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/v2/getparams.msg @@ -0,0 +1,5 @@ +MRCP/2.0 112 GET-PARAMS 543256 +Channel-Identifier:32AECB23433802@speechsynth +Voice-gender: +Voice-variant: + diff --git a/libs/unimrcp/tests/mrcptest/v2/recognitioncomplete.msg b/libs/unimrcp/tests/mrcptest/v2/recognitioncomplete.msg new file mode 100644 index 0000000000..e68d28fe01 --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/v2/recognitioncomplete.msg @@ -0,0 +1,20 @@ +MRCP/2.0 616 RECOGNITION-COMPLETE 543257 COMPLETE +Channel-Identifier:32AECB23433801@speechrecog +Completion-Cause:000 success +Waveform-URI:;size=342456;duration=25435 +Content-Type:application/nlsml+xml +Content-Length:430 + + + + + + + Andre Roy + + + may I speak to Andre Roy + + \ No newline at end of file diff --git a/libs/unimrcp/tests/mrcptest/v2/recognize.msg b/libs/unimrcp/tests/mrcptest/v2/recognize.msg new file mode 100644 index 0000000000..e378491422 --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/v2/recognize.msg @@ -0,0 +1,30 @@ +MRCP/2.0 903 RECOGNIZE 543257 +Channel-Identifier:32AECB23433801@speechrecog +Confidence-Threshold:0.9 +Content-Type:application/srgs+xml +Content-ID: +Content-Length:702 + + + + + + + + + + oui + yes + + + + + + may I speak to + + Michel Tremblay + Andre Roy + + + \ No newline at end of file diff --git a/libs/unimrcp/tests/mrcptest/v2/setparams.msg b/libs/unimrcp/tests/mrcptest/v2/setparams.msg new file mode 100644 index 0000000000..02db19e22e --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/v2/setparams.msg @@ -0,0 +1,5 @@ +MRCP/2.0 119 SET-PARAMS 543256 +Channel-Identifier:32AECB23433802@speechsynth +Voice-gender:female +Voice-variant:3 + diff --git a/libs/unimrcp/tests/mrcptest/v2/speak.msg b/libs/unimrcp/tests/mrcptest/v2/speak.msg new file mode 100644 index 0000000000..4c540322e5 --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/v2/speak.msg @@ -0,0 +1,26 @@ +MRCP/2.0 732 SPEAK 543257 +Channel-Identifier:32AECB23433802@speechsynth +Voice-gender:neutral +Voice-Age:25 +Prosody-volume:medium +Content-Type:application/ssml+xml +Content-Length:542 + + + +

+ You have 4 new messages. + The first is from Stephanie Williams and arrived at + + 0345p. + + The subject is + ski trip + +

+
\ No newline at end of file diff --git a/libs/unimrcp/tests/mrcptest/v2/speakcomplete.msg b/libs/unimrcp/tests/mrcptest/v2/speakcomplete.msg new file mode 100644 index 0000000000..2f1cf96765 --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/v2/speakcomplete.msg @@ -0,0 +1,5 @@ +MRCP/2.0 161 SPEAK-COMPLETE 543257 COMPLETE +Channel-Identifier:32AECB23433802@speechsynth +Completion-Cause:000 normal +Speech-Marker:timestamp=857206027059 + diff --git a/libs/unimrcp/tests/mrcptest/v2/stop.msg b/libs/unimrcp/tests/mrcptest/v2/stop.msg new file mode 100644 index 0000000000..003c767cb7 --- /dev/null +++ b/libs/unimrcp/tests/mrcptest/v2/stop.msg @@ -0,0 +1,3 @@ +MRCP/2.0 74 STOP 543258 +Channel-Identifier:32AECB23433802@speechsynth + diff --git a/libs/unimrcp/tests/rtsptest/Makefile.am b/libs/unimrcp/tests/rtsptest/Makefile.am new file mode 100644 index 0000000000..4821917b5e --- /dev/null +++ b/libs/unimrcp/tests/rtsptest/Makefile.am @@ -0,0 +1,12 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_srcdir)/libs/uni-rtsp/include \ + -I$(top_srcdir)/libs/apr-toolkit/include \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) + +noinst_PROGRAMS = rtsptest +rtsptest_LDADD = $(top_builddir)/libs/uni-rtsp/libunirtsp.la \ + $(top_builddir)/libs/apr-toolkit/libaprtoolkit.la \ + $(UNIMRCP_APR_LIBS) $(UNIMRCP_APU_LIBS) +rtsptest_SOURCES = src/main.c \ + src/parse_gen_suite.c diff --git a/libs/unimrcp/tests/rtsptest/msg/announce.msg b/libs/unimrcp/tests/rtsptest/msg/announce.msg new file mode 100644 index 0000000000..142f16ce35 --- /dev/null +++ b/libs/unimrcp/tests/rtsptest/msg/announce.msg @@ -0,0 +1,24 @@ +ANNOUNCE rtsp://media.server.com/media/synthesizer RTSP/1.0 +CSeq:4 +Session:12345678 +Content-Type:application/mrcp +Content-Length:567 + +SPEAK 543257 MRCP/1.0 +Kill-On-Barge-In:false +Voice-gender:neutral +Prosody-volume:medium +Content-Type:application/synthesis+ssml +Content-Length:412 + + + + + You have 4 new messages. + The first is from Stephanie Williams + and arrived at + 3:45pm. + + The subject is ski trip + + \ No newline at end of file diff --git a/libs/unimrcp/tests/rtsptest/msg/ok.msg b/libs/unimrcp/tests/rtsptest/msg/ok.msg new file mode 100644 index 0000000000..b88ac1ca40 --- /dev/null +++ b/libs/unimrcp/tests/rtsptest/msg/ok.msg @@ -0,0 +1,16 @@ +RTSP/1.0 200 OK +CSeq:2 +Transport:RTP/AVP;unicast;client_port=46456-46457;server_port=46460-46461 +Session:12345678 +Content-Length:188 +Content-Type:application/sdp + +v=0 +o=- 3211724219 3211724219 IN IP4 10.3.2.88 +s=Media Server +c=IN IP4 0.0.0.0 +t=0 0 +m=audio 46460 RTP/AVP 0 96 +a=rtpmap:0 pcmu/8000 +a=rtpmap:96 telephone-event/8000 +a=fmtp:96 0-15 \ No newline at end of file diff --git a/libs/unimrcp/tests/rtsptest/msg/re-ok.msg b/libs/unimrcp/tests/rtsptest/msg/re-ok.msg new file mode 100644 index 0000000000..2b8dd7503a --- /dev/null +++ b/libs/unimrcp/tests/rtsptest/msg/re-ok.msg @@ -0,0 +1,6 @@ +RTSP/1.0 200 OK +CSeq:3 +Transport:RTP/AVP;unicast;client_port=46456-46457; + server_port=46460-46461;mode=record;ttl=127 +Session:12345678 + diff --git a/libs/unimrcp/tests/rtsptest/msg/re-setup.msg b/libs/unimrcp/tests/rtsptest/msg/re-setup.msg new file mode 100644 index 0000000000..288ef23b30 --- /dev/null +++ b/libs/unimrcp/tests/rtsptest/msg/re-setup.msg @@ -0,0 +1,6 @@ +SETUP rtsp://media.server.com/media/recognizer RTSP/1.0 +CSeq:3 +Transport:RTP/AVP;unicast;client_port=46456-46457; + mode=record;ttl=127 +Session:12345678;timeout=200 + diff --git a/libs/unimrcp/tests/rtsptest/msg/setup.msg b/libs/unimrcp/tests/rtsptest/msg/setup.msg new file mode 100644 index 0000000000..1606500258 --- /dev/null +++ b/libs/unimrcp/tests/rtsptest/msg/setup.msg @@ -0,0 +1,16 @@ +SETUP rtsp://media.server.com/media/synthesizer RTSP/1.0 +CSeq:2 +Transport:RTP/AVP;unicast;client_port=46456-46457 +Content-Type:application/sdp +Content-Length:188 + +v=0 +o=- 123 456 IN IP4 10.0.0.1 +s=Media Server +p=+1-888-555-1212 +c=IN IP4 0.0.0.0 +t=0 0 +m=audio 0 RTP/AVP 0 96 +a=rtpmap:0 pcmu/8000 +a=rtpmap:96 telephone-event/8000 +a=fmtp:96 0-15 \ No newline at end of file diff --git a/libs/unimrcp/tests/rtsptest/msg/teardown.msg b/libs/unimrcp/tests/rtsptest/msg/teardown.msg new file mode 100644 index 0000000000..f673a0ccd8 --- /dev/null +++ b/libs/unimrcp/tests/rtsptest/msg/teardown.msg @@ -0,0 +1,4 @@ +TEARDOWN rtsp://media.server.com/media/synthesizer RTSP/1.0 +CSeq:12 +Session:12345678 + diff --git a/libs/unimrcp/tests/rtsptest/msg/ultimate.msg b/libs/unimrcp/tests/rtsptest/msg/ultimate.msg new file mode 100644 index 0000000000..bac7ffe683 --- /dev/null +++ b/libs/unimrcp/tests/rtsptest/msg/ultimate.msg @@ -0,0 +1,46 @@ +RTSP/1.0 200 OK +CSeq:3 +Transport:RTP/AVP;unicast;client_port=46456-46457; + server_port=46460-46461;mode=record;ttl=127 +Session:12345678 + +SETUP rtsp://media.server.com/media/synthesizer RTSP/1.0 +CSeq:2 +Transport:RTP/AVP;unicast;client_port=46456-46457 +Content-Type:application/sdp +Content-Length:190 + +v=0 +o=- 123 456 IN IP4 10.0.0.1 +s=Media Server +p=+1-888-555-1212 +c=IN IP4 0.0.0.0 +t=0 0 +m=audio 0 RTP/AVP 0 96 +a=rtpmap:0 pcmu/8000 +a=rtpmap:96 telephone-event/8000 +a=fmtp:96 0-15 +ANNOUNCE rtsp://media.server.com/media/synthesizer RTSP/1.0 +CSeq:4 +Session:12345678 +Content-Type:application/mrcp +Content-Length:567 + +SPEAK 543257 MRCP/1.0 +Kill-On-Barge-In:false +Voice-gender:neutral +Prosody-volume:medium +Content-Type:application/synthesis+ssml +Content-Length:412 + + + + + You have 4 new messages. + The first is from Stephanie Williams + and arrived at + 3:45pm. + + The subject is ski trip + + \ No newline at end of file diff --git a/libs/unimrcp/tests/rtsptest/rtsptest.vcproj b/libs/unimrcp/tests/rtsptest/rtsptest.vcproj new file mode 100644 index 0000000000..b5ebf45e0b --- /dev/null +++ b/libs/unimrcp/tests/rtsptest/rtsptest.vcproj @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/tests/rtsptest/src/main.c b/libs/unimrcp/tests/rtsptest/src/main.c new file mode 100644 index 0000000000..9627085151 --- /dev/null +++ b/libs/unimrcp/tests/rtsptest/src/main.c @@ -0,0 +1,50 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 "apt_test_suite.h" +#include "apt_log.h" + +apt_test_suite_t* parse_gen_test_suite_create(apr_pool_t *pool); + +int main(int argc, const char * const *argv) +{ + apt_test_framework_t *test_framework; + apt_test_suite_t *test_suite; + apr_pool_t *pool; + + /* one time apr global initialization */ + if(apr_initialize() != APR_SUCCESS) { + return 0; + } + + /* create test framework */ + test_framework = apt_test_framework_create(); + pool = apt_test_framework_pool_get(test_framework); + + /* create test suites and add them to test framework */ + test_suite = parse_gen_test_suite_create(pool); + apt_test_framework_suite_add(test_framework,test_suite); + + /* run tests */ + apt_test_framework_run(test_framework,argc,argv); + + /* destroy test framework */ + apt_test_framework_destroy(test_framework); + + /* final apr global termination */ + apr_terminate(); + return 0; +} diff --git a/libs/unimrcp/tests/rtsptest/src/parse_gen_suite.c b/libs/unimrcp/tests/rtsptest/src/parse_gen_suite.c new file mode 100644 index 0000000000..c88ac6f86a --- /dev/null +++ b/libs/unimrcp/tests/rtsptest/src/parse_gen_suite.c @@ -0,0 +1,150 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include +#include "apt_test_suite.h" +#include "apt_log.h" +#include "rtsp_stream.h" + +static apt_bool_t test_stream_generate(rtsp_generator_t *generator, rtsp_message_t *message) +{ + char buffer[500]; + apt_text_stream_t stream; + rtsp_stream_result_e result; + apt_bool_t continuation; + + rtsp_generator_message_set(generator,message); + do { + apt_text_stream_init(&stream,buffer,sizeof(buffer)-1); + continuation = FALSE; + result = rtsp_generator_run(generator,&stream); + if(result == RTSP_STREAM_MESSAGE_COMPLETE) { + stream.text.length = stream.pos - stream.text.buf; + *stream.pos = '\0'; + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Generated RTSP Stream [%lu bytes]\n%s",stream.text.length,stream.text.buf); + } + else if(result == RTSP_STREAM_MESSAGE_TRUNCATED) { + *stream.pos = '\0'; + apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Generated RTSP Stream [%lu bytes] continuation awaiting\n%s",stream.text.length,stream.text.buf); + continuation = TRUE; + } + else { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Generate RTSP Stream"); + } + } + while(continuation == TRUE); + return TRUE; +} + +static apt_bool_t rtsp_message_handler(void *obj, rtsp_message_t *message, rtsp_stream_result_e result) +{ + if(result == RTSP_STREAM_MESSAGE_COMPLETE) { + /* message is completely parsed */ + rtsp_generator_t *generator = obj; + test_stream_generate(generator,message); + } + return TRUE; +} + +static apt_bool_t test_file_process(apt_test_suite_t *suite, const char *file_path) +{ + apr_file_t *file; + char buffer[500]; + apt_text_stream_t stream; + rtsp_parser_t *parser; + rtsp_generator_t *generator; + apr_size_t length; + apr_size_t offset; + + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Open File [%s]",file_path); + if(apr_file_open(&file,file_path,APR_FOPEN_READ | APR_FOPEN_BINARY,APR_OS_DEFAULT,suite->pool) != APR_SUCCESS) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Open File"); + return FALSE; + } + + parser = rtsp_parser_create(suite->pool); + generator = rtsp_generator_create(suite->pool); + + apt_text_stream_init(&stream,buffer,sizeof(buffer)-1); + + do { + /* init length of the stream */ + stream.text.length = sizeof(buffer)-1; + /* calculate offset remaining from the previous receive / if any */ + offset = stream.pos - stream.text.buf; + /* calculate available length */ + length = stream.text.length - offset; + + if(apr_file_read(file,stream.pos,&length) != APR_SUCCESS) { + break; + } + /* calculate actual length of the stream */ + stream.text.length = offset + length; + stream.pos[length] = '\0'; + apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Parse RTSP Stream [%lu bytes]\n%s",length,stream.pos); + + /* reset pos */ + stream.pos = stream.text.buf; + rtsp_stream_walk(parser,&stream,rtsp_message_handler,generator); + } + while(apr_file_eof(file) != APR_EOF); + + apr_file_close(file); + return TRUE; +} + +static apt_bool_t test_dir_process(apt_test_suite_t *suite) +{ + apr_status_t rv; + apr_dir_t *dir; + + const char *dir_name = "msg"; + if(apr_dir_open(&dir,dir_name,suite->pool) != APR_SUCCESS) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot Open Directory [%s]",dir_name); + return FALSE; + } + + do { + apr_finfo_t finfo; + rv = apr_dir_read(&finfo,APR_FINFO_DIRENT,dir); + if(rv == APR_SUCCESS) { + if(finfo.filetype == APR_REG && finfo.name) { + char *file_path; + apr_filepath_merge(&file_path,dir_name,finfo.name,0,suite->pool); + test_file_process(suite,file_path); + printf("\nPress ENTER to continue\n"); + getchar(); + } + } + } + while(rv == APR_SUCCESS); + + apr_dir_close(dir); + return TRUE; +} + +static apt_bool_t parse_gen_test_run(apt_test_suite_t *suite, int argc, const char * const *argv) +{ + test_dir_process(suite); + return TRUE; +} + +apt_test_suite_t* parse_gen_test_suite_create(apr_pool_t *pool) +{ + apt_test_suite_t *suite = apt_test_suite_create(pool,"parse-gen",NULL,parse_gen_test_run); + return suite; +} diff --git a/libs/unimrcp/tests/sipp/mrcp_uac_multi b/libs/unimrcp/tests/sipp/mrcp_uac_multi new file mode 100644 index 0000000000..5b2cbdab87 --- /dev/null +++ b/libs/unimrcp/tests/sipp/mrcp_uac_multi @@ -0,0 +1,108 @@ + + + + + + + + + ;tag=[call_number] + To: sut + Call-ID: [call_id] + CSeq: 1 INVITE + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Type: application/sdp + Content-Length: [len] + + v=0 + o=user1 53655765 2353687637 IN IP4 [local_ip] + s=- + c=IN IP4 [media_ip] + t=0 0 + m=application 9 TCP/MRCPv2 1 + a=setup:active + a=connection:new + a=resource:speechsynth + a=cmid:1 + m=application 9 TCP/MRCPv2 1 + a=setup:active + a=connection:new + a=resource:speechrecog + a=cmid:1 + m=audio [media_port] RTP/AVP 0 8 + a=sendrecv + a=mid:1 + + ]]> + + + + + + + + + + + + + + + + + + ;tag=[call_number] + To: sut [peer_tag_param] + Call-ID: [call_id] + CSeq: 1 ACK + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Length: 0 + + ]]> + + + + + + + + + ;tag=[call_number] + To: sut [peer_tag_param] + Call-ID: [call_id] + CSeq: 2 BYE + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Length: 0 + + ]]> + + + + + + + + + + + + diff --git a/libs/unimrcp/tests/sipp/mrcp_uac_recog b/libs/unimrcp/tests/sipp/mrcp_uac_recog new file mode 100644 index 0000000000..13ea1f4448 --- /dev/null +++ b/libs/unimrcp/tests/sipp/mrcp_uac_recog @@ -0,0 +1,103 @@ + + + + + + + + + ;tag=[call_number] + To: sut + Call-ID: [call_id] + CSeq: 1 INVITE + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Type: application/sdp + Content-Length: [len] + + v=0 + o=user1 53655765 2353687637 IN IP4 [local_ip] + s=- + c=IN IP4 [media_ip] + t=0 0 + m=application 9 TCP/MRCPv2 1 + a=setup:active + a=connection:new + a=resource:speechrecog + a=cmid:1 + m=audio [media_port] RTP/AVP 0 8 + a=sendonly + a=mid:1 + + ]]> + + + + + + + + + + + + + + + + + + ;tag=[call_number] + To: sut [peer_tag_param] + Call-ID: [call_id] + CSeq: 1 ACK + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Length: 0 + + ]]> + + + + + + + + + ;tag=[call_number] + To: sut [peer_tag_param] + Call-ID: [call_id] + CSeq: 2 BYE + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Length: 0 + + ]]> + + + + + + + + + + + + diff --git a/libs/unimrcp/tests/sipp/mrcp_uac_synth b/libs/unimrcp/tests/sipp/mrcp_uac_synth new file mode 100644 index 0000000000..627c8894db --- /dev/null +++ b/libs/unimrcp/tests/sipp/mrcp_uac_synth @@ -0,0 +1,103 @@ + + + + + + + + + ;tag=[call_number] + To: sut + Call-ID: [call_id] + CSeq: 1 INVITE + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Type: application/sdp + Content-Length: [len] + + v=0 + o=user1 53655765 2353687637 IN IP4 [local_ip] + s=- + c=IN IP4 [media_ip] + t=0 0 + m=application 9 TCP/MRCPv2 1 + a=setup:active + a=connection:new + a=resource:speechsynth + a=cmid:1 + m=audio [media_port] RTP/AVP 0 8 + a=recvonly + a=mid:1 + + ]]> + + + + + + + + + + + + + + + + + + ;tag=[call_number] + To: sut [peer_tag_param] + Call-ID: [call_id] + CSeq: 1 ACK + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Length: 0 + + ]]> + + + + + + + + + ;tag=[call_number] + To: sut [peer_tag_param] + Call-ID: [call_id] + CSeq: 2 BYE + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Length: 0 + + ]]> + + + + + + + + + + + + diff --git a/libs/unimrcp/tests/sipp/mrcp_uac_unknown b/libs/unimrcp/tests/sipp/mrcp_uac_unknown new file mode 100644 index 0000000000..190d924491 --- /dev/null +++ b/libs/unimrcp/tests/sipp/mrcp_uac_unknown @@ -0,0 +1,102 @@ + + + + + + + + + ;tag=[call_number] + To: sut + Call-ID: [call_id] + CSeq: 1 INVITE + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Type: application/sdp + Content-Length: [len] + + v=0 + o=user1 53655765 2353687637 IN IP4 [local_ip] + s=- + c=IN IP4 [media_ip] + t=0 0 + m=application 9 TCP/MRCPv2 1 + a=setup:active + a=connection:new + a=resource:unknown + a=cmid:1 + m=audio [media_port] RTP/AVP 0 8 + a=mid:1 + + ]]> + + + + + + + + + + + + + + + + + + ;tag=[call_number] + To: sut [peer_tag_param] + Call-ID: [call_id] + CSeq: 1 ACK + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Length: 0 + + ]]> + + + + + + + + + ;tag=[call_number] + To: sut [peer_tag_param] + Call-ID: [call_id] + CSeq: 2 BYE + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Length: 0 + + ]]> + + + + + + + + + + + + diff --git a/libs/unimrcp/tests/sipp/mrcp_uac_update b/libs/unimrcp/tests/sipp/mrcp_uac_update new file mode 100644 index 0000000000..da91fee311 --- /dev/null +++ b/libs/unimrcp/tests/sipp/mrcp_uac_update @@ -0,0 +1,179 @@ + + + + + + + + + ;tag=[call_number] + To: sut + Call-ID: [call_id] + CSeq: 1 INVITE + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Type: application/sdp + Content-Length: [len] + + v=0 + o=user1 53655765 2353687637 IN IP4 [local_ip] + s=- + c=IN IP4 [media_ip] + t=0 0 + m=application 9 TCP/MRCPv2 1 + a=setup:active + a=connection:new + a=resource:speechsynth + a=cmid:1 + m=audio [media_port] RTP/AVP 0 8 + a=recvonly + a=mid:1 + + ]]> + + + + + + + + + + + + + + + + + + ;tag=[call_number] + To: sut [peer_tag_param] + Call-ID: [call_id] + CSeq: 1 ACK + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Length: 0 + + ]]> + + + + + + + + + + ;tag=[call_number] + To: sut + Call-ID: [call_id] + CSeq: 1 INVITE + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Type: application/sdp + Content-Length: [len] + + v=0 + o=user1 53655765 2353687637 IN IP4 [local_ip] + s=- + c=IN IP4 [media_ip] + t=0 0 + m=application 9 TCP/MRCPv2 1 + a=setup:active + a=connection:new + a=resource:speechsynth + a=cmid:1 + m=audio [media_port] RTP/AVP 0 8 + a=sendrecv + a=mid:1 + m=application 9 TCP/MRCPv2 1 + a=setup:active + a=connection:existing + a=resource:speechrecog + a=cmid:1 + + ]]> + + + + + + + + + + + + + + + + + + ;tag=[call_number] + To: sut [peer_tag_param] + Call-ID: [call_id] + CSeq: 1 ACK + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Length: 0 + + ]]> + + + + + + + + + + ;tag=[call_number] + To: sut [peer_tag_param] + Call-ID: [call_id] + CSeq: 2 BYE + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Length: 0 + + ]]> + + + + + + + + + + + + diff --git a/libs/unimrcp/tests/sipp/mrcp_uas_recog b/libs/unimrcp/tests/sipp/mrcp_uas_recog new file mode 100644 index 0000000000..27bc361a5d --- /dev/null +++ b/libs/unimrcp/tests/sipp/mrcp_uas_recog @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Content-Type: application/sdp + Content-Length: [len] + + v=0 + o=user1 53655765 2353687637 IN IP4 [local_ip] + s=- + c=IN IP4 [media_ip] + t=0 0 + m=application 1050 TCP/MRCPv2 1 + a=setup:passive + a=connection:new + a=channel:dca48cf082dd584b@speechrecog + a=cmid:1 + m=audio [media_port] RTP/AVP 0 8 + a=recvonly + a=mid:1 + + ]]> + + + + + + + + + + + Content-Type: application/sdp + Content-Length: [len] + + v=0 + o=user1 53655765 2353687637 IN IP4 [local_ip] + s=- + c=IN IP4 [media_ip] + t=0 0 + m=application 0 TCP/MRCPv2 1 + a=setup:passive + a=connection:existing + a=channel:dca48cf082dd584b@speechrecog + a=cmid:1 + m=audio 0 RTP/AVP 0 8 + a=recvonly + a=mid:1 + + ]]> + + + + + + + + + + + Content-Length: 0 + + ]]> + + + + + + + + + + + + + + diff --git a/libs/unimrcp/tests/sipp/mrcp_uas_reject b/libs/unimrcp/tests/sipp/mrcp_uas_reject new file mode 100644 index 0000000000..5f6af6b117 --- /dev/null +++ b/libs/unimrcp/tests/sipp/mrcp_uas_reject @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Content-Length: 0 + ]]> + + + + + + + + + + + + diff --git a/libs/unimrcp/tests/sipp/mrcp_uas_synth b/libs/unimrcp/tests/sipp/mrcp_uas_synth new file mode 100644 index 0000000000..b2400aaddd --- /dev/null +++ b/libs/unimrcp/tests/sipp/mrcp_uas_synth @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Content-Type: application/sdp + Content-Length: [len] + + v=0 + o=user1 53655765 2353687637 IN IP4 [local_ip] + s=- + c=IN IP4 [media_ip] + t=0 0 + m=application 1050 TCP/MRCPv2 1 + a=setup:passive + a=connection:new + a=channel:dca48cf082dd584b@speechsynth + a=cmid:1 + m=audio [media_port] RTP/AVP 0 8 + a=sendonly + a=mid:1 + + ]]> + + + + + + + + + + + Content-Type: application/sdp + Content-Length: [len] + + v=0 + o=user1 53655765 2353687637 IN IP4 [local_ip] + s=- + c=IN IP4 [media_ip] + t=0 0 + m=application 0 TCP/MRCPv2 1 + a=setup:passive + a=connection:existing + a=channel:dca48cf082dd584b@speechsynth + a=cmid:1 + m=audio 0 RTP/AVP 0 8 + a=sendonly + a=mid:1 + + ]]> + + + + + + + + + + + Content-Length: 0 + + ]]> + + + + + + + + + + + + + + diff --git a/libs/unimrcp/tests/sipp/mrcp_uas_unknown b/libs/unimrcp/tests/sipp/mrcp_uas_unknown new file mode 100644 index 0000000000..e3a1b1e50d --- /dev/null +++ b/libs/unimrcp/tests/sipp/mrcp_uas_unknown @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Content-Type: application/sdp + Content-Length: [len] + + v=0 + o=user1 53655765 2353687637 IN IP4 [local_ip] + s=- + c=IN IP4 [media_ip] + t=0 0 + m=application 0 TCP/MRCPv2 1 + a=setup:passive + a=connection:new + a=channel:dca48cf082dd584b@unknown + a=cmid:1 + m=audio 0 RTP/AVP 0 8 + a=sendonly + a=mid:1 + + ]]> + + + + + + + + + + + Content-Length: 0 + + ]]> + + + + + + + + + + + + + + diff --git a/libs/unimrcp/tests/strtablegen/Makefile.am b/libs/unimrcp/tests/strtablegen/Makefile.am new file mode 100644 index 0000000000..b3508c27ec --- /dev/null +++ b/libs/unimrcp/tests/strtablegen/Makefile.am @@ -0,0 +1,9 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_srcdir)/libs/apr-toolkit/include \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) + +noinst_PROGRAMS = strtablegen +strtablegen_LDADD = $(top_builddir)/libs/apr-toolkit/libaprtoolkit.la \ + $(UNIMRCP_APR_LIBS) $(UNIMRCP_APU_LIBS) +strtablegen_SOURCES = src/main.c diff --git a/libs/unimrcp/tests/strtablegen/src/main.c b/libs/unimrcp/tests/strtablegen/src/main.c new file mode 100644 index 0000000000..e547b9b1f1 --- /dev/null +++ b/libs/unimrcp/tests/strtablegen/src/main.c @@ -0,0 +1,143 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * 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 +#include "apt_pool.h" +#include "apt_string_table.h" +#include "apt_text_stream.h" + +static apt_bool_t is_unique(const apt_str_table_item_t table[], apr_size_t count, + apr_size_t item_index, apr_size_t char_index, char value) +{ + size_t i; + const char *buf; + for(i=0; ivalue,&line,pool); + item->key = 0; + count++; + } + while(count < max_count); + + return count; +} + +static apt_bool_t string_table_write(const apt_str_table_item_t table[], apr_size_t count, FILE *file) +{ + size_t i; + const apt_str_table_item_t *item; + for(i=0; ivalue.buf, item->value.length, item->key); + } + return TRUE; +} + +int main(int argc, char *argv[]) +{ + apr_pool_t *pool = NULL; + apt_str_table_item_t table[100]; + size_t count; + FILE *file_in, *file_out; + + /* one time apr global initialization */ + if(apr_initialize() != APR_SUCCESS) { + return 0; + } + pool = apt_pool_create(); + + if(argc < 2) { + printf("usage: stringtablegen stringtable.in [stringtable.out]\n"); + return 0; + } + file_in = fopen(argv[1], "rb"); + if(file_in == NULL) { + printf("cannot open file %s\n", argv[1]); + return 0; + } + + if(argc > 2) { + file_out = fopen(argv[2], "wb"); + } + else { + file_out = stdout; + } + + /* read items (strings) from the file */ + count = string_table_read(table,100,file_in,pool); + + /* generate string table */ + string_table_key_generate(table,count); + + /* dump string table to the file */ + string_table_write(table,count,file_out); + + fclose(file_in); + if(file_out != stdout) { + fclose(file_out); + } + + apr_pool_destroy(pool); + /* final apr global termination */ + apr_terminate(); + return 0; +} diff --git a/libs/unimrcp/tests/strtablegen/stringtable.in b/libs/unimrcp/tests/strtablegen/stringtable.in new file mode 100644 index 0000000000..7f045949cd --- /dev/null +++ b/libs/unimrcp/tests/strtablegen/stringtable.in @@ -0,0 +1,12 @@ +Channel-Identifier +Active-Request-Id-List +Proxy-Sync-Id +Accept-Charset +Content-Type +Content-Id +Content-Base +Content-Encoding +Content-Location +Content-Length +Cache-Control +Logging-Tag diff --git a/libs/unimrcp/tests/strtablegen/strtablegen.vcproj b/libs/unimrcp/tests/strtablegen/strtablegen.vcproj new file mode 100644 index 0000000000..f19a36fdec --- /dev/null +++ b/libs/unimrcp/tests/strtablegen/strtablegen.vcproj @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/unimrcp.sln b/libs/unimrcp/unimrcp.sln new file mode 100644 index 0000000000..de17c86a71 --- /dev/null +++ b/libs/unimrcp/unimrcp.sln @@ -0,0 +1,272 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "libs", "libs", "{5377DC3A-DB96-4819-8AAF-2A75F3A69119}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "modules", "modules", "{493A1DB9-6E7C-48C7-93B5-F75C3C25B9DF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "platforms", "platforms", "{8E282AE2-038C-49FE-AC67-BC9615AFD800}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "plugins", "plugins", "{09BABD45-8F30-4F99-B8B8-8DD78F6804DB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{AC4356E8-48A1-4D2D-AFB1-11CF30B974CD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unimrcpserver", "platforms\unimrcp-server\unimrcpserver.vcproj", "{592CF22D-3F8F-4A77-A174-130D77B7623B}" + ProjectSection(ProjectDependencies) = postProject + {C98AF157-352E-4737-BD30-A24E2647F5AE} = {C98AF157-352E-4737-BD30-A24E2647F5AE} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "aprtoolkit", "libs\apr-toolkit\aprtoolkit.vcproj", "{13DEECA0-BDD4-4744-A1A2-8EB0A44DF3D2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mpf", "libs\mpf\mpf.vcproj", "{B5A00BFA-6083-4FAE-A097-71642D6473B5}" + ProjectSection(ProjectDependencies) = postProject + {13DEECA0-BDD4-4744-A1A2-8EB0A44DF3D2} = {13DEECA0-BDD4-4744-A1A2-8EB0A44DF3D2} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mrcp", "libs\mrcp\mrcp.vcproj", "{1C320193-46A6-4B34-9C56-8AB584FC1B56}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mrcpsignaling", "libs\mrcp-signaling\mrcpsignaling.vcproj", "{12A49562-BAB9-43A3-A21D-15B60BBB4C31}" + ProjectSection(ProjectDependencies) = postProject + {B5A00BFA-6083-4FAE-A097-71642D6473B5} = {B5A00BFA-6083-4FAE-A097-71642D6473B5} + {1C320193-46A6-4B34-9C56-8AB584FC1B56} = {1C320193-46A6-4B34-9C56-8AB584FC1B56} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mrcpserver", "libs\mrcp-server\mrcpserver.vcproj", "{18B1F35A-10F8-4287-9B37-2D10501B0B38}" + ProjectSection(ProjectDependencies) = postProject + {12A49562-BAB9-43A3-A21D-15B60BBB4C31} = {12A49562-BAB9-43A3-A21D-15B60BBB4C31} + {A9EDAC04-6A5F-4BA7-BC0D-CCE7B255B6EA} = {A9EDAC04-6A5F-4BA7-BC0D-CCE7B255B6EA} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libunimrcpserver", "platforms\libunimrcp-server\libunimrcpserver.vcproj", "{C98AF157-352E-4737-BD30-A24E2647F5AE}" + ProjectSection(ProjectDependencies) = postProject + {DEB01ACB-D65F-4A62-AED9-58C1054499E9} = {DEB01ACB-D65F-4A62-AED9-58C1054499E9} + {18B1F35A-10F8-4287-9B37-2D10501B0B38} = {18B1F35A-10F8-4287-9B37-2D10501B0B38} + {746F3632-5BB2-4570-9453-31D6D58A7D8E} = {746F3632-5BB2-4570-9453-31D6D58A7D8E} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mrcpsofiasip", "modules\mrcp-sofiasip\mrcpsofiasip.vcproj", "{746F3632-5BB2-4570-9453-31D6D58A7D8E}" + ProjectSection(ProjectDependencies) = postProject + {12A49562-BAB9-43A3-A21D-15B60BBB4C31} = {12A49562-BAB9-43A3-A21D-15B60BBB4C31} + {A9EDAC04-6A5F-4BA7-BC0D-CCE7B255B6EA} = {A9EDAC04-6A5F-4BA7-BC0D-CCE7B255B6EA} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mrcpclient", "libs\mrcp-client\mrcpclient.vcproj", "{72782932-37CC-46AE-8C7F-9A7B1A6EE108}" + ProjectSection(ProjectDependencies) = postProject + {12A49562-BAB9-43A3-A21D-15B60BBB4C31} = {12A49562-BAB9-43A3-A21D-15B60BBB4C31} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libunimrcpclient", "platforms\libunimrcp-client\libunimrcpclient.vcproj", "{EE157390-1E85-416C-946E-620E32C9AD33}" + ProjectSection(ProjectDependencies) = postProject + {DEB01ACB-D65F-4A62-AED9-58C1054499E9} = {DEB01ACB-D65F-4A62-AED9-58C1054499E9} + {746F3632-5BB2-4570-9453-31D6D58A7D8E} = {746F3632-5BB2-4570-9453-31D6D58A7D8E} + {72782932-37CC-46AE-8C7F-9A7B1A6EE108} = {72782932-37CC-46AE-8C7F-9A7B1A6EE108} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unimrcpclient", "platforms\unimrcp-client\unimrcpclient.vcproj", "{57FAF32E-49FD-491F-895D-132D0D5EFE0A}" + ProjectSection(ProjectDependencies) = postProject + {EE157390-1E85-416C-946E-620E32C9AD33} = {EE157390-1E85-416C-946E-620E32C9AD33} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mrcpv2transport", "libs\mrcpv2-transport\mrcpv2transport.vcproj", "{A9EDAC04-6A5F-4BA7-BC0D-CCE7B255B6EA}" + ProjectSection(ProjectDependencies) = postProject + {1C320193-46A6-4B34-9C56-8AB584FC1B56} = {1C320193-46A6-4B34-9C56-8AB584FC1B56} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mrcpengine", "libs\mrcp-engine\mrcpengine.vcproj", "{843425BE-9A9A-44F4-A4E3-4B57D6ABD53C}" + ProjectSection(ProjectDependencies) = postProject + {B5A00BFA-6083-4FAE-A097-71642D6473B5} = {B5A00BFA-6083-4FAE-A097-71642D6473B5} + {1C320193-46A6-4B34-9C56-8AB584FC1B56} = {1C320193-46A6-4B34-9C56-8AB584FC1B56} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "demosynth", "plugins\demo-synth\demosynth.vcproj", "{92BFA534-C419-4EB2-AAA3-510653F38F08}" + ProjectSection(ProjectDependencies) = postProject + {843425BE-9A9A-44F4-A4E3-4B57D6ABD53C} = {843425BE-9A9A-44F4-A4E3-4B57D6ABD53C} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "demorecog", "plugins\demo-recog\demorecog.vcproj", "{B495B6D9-AF84-479D-B30A-313C16EF8BFD}" + ProjectSection(ProjectDependencies) = postProject + {843425BE-9A9A-44F4-A4E3-4B57D6ABD53C} = {843425BE-9A9A-44F4-A4E3-4B57D6ABD53C} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "strtablegen", "tests\strtablegen\strtablegen.vcproj", "{79EF9F1D-E211-4ED1-91D2-FC935AB3A872}" + ProjectSection(ProjectDependencies) = postProject + {13DEECA0-BDD4-4744-A1A2-8EB0A44DF3D2} = {13DEECA0-BDD4-4744-A1A2-8EB0A44DF3D2} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "apttest", "tests\apttest\apttest.vcproj", "{429C907B-97D1-4B2D-9B0E-A14A5BFDAD15}" + ProjectSection(ProjectDependencies) = postProject + {13DEECA0-BDD4-4744-A1A2-8EB0A44DF3D2} = {13DEECA0-BDD4-4744-A1A2-8EB0A44DF3D2} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mpftest", "tests\mpftest\mpftest.vcproj", "{DCF01B1C-5268-44F3-9130-D647FABFB663}" + ProjectSection(ProjectDependencies) = postProject + {B5A00BFA-6083-4FAE-A097-71642D6473B5} = {B5A00BFA-6083-4FAE-A097-71642D6473B5} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mrcptest", "tests\mrcptest\mrcptest.vcproj", "{3CA97077-6210-4362-998A-D15A35EEAA08}" + ProjectSection(ProjectDependencies) = postProject + {1C320193-46A6-4B34-9C56-8AB584FC1B56} = {1C320193-46A6-4B34-9C56-8AB584FC1B56} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mrcpcepstral", "plugins\mrcp-cepstral\mrcpcepstral.vcproj", "{729EF28E-38C9-40DE-A138-87785F021411}" + ProjectSection(ProjectDependencies) = postProject + {843425BE-9A9A-44F4-A4E3-4B57D6ABD53C} = {843425BE-9A9A-44F4-A4E3-4B57D6ABD53C} + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{62083CC3-13BF-49EA-BFE8-4C9337C0D82C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unirtsp", "libs\uni-rtsp\unirtsp.vcproj", "{504B3154-7A4F-459D-9877-B951021C3F1F}" + ProjectSection(ProjectDependencies) = postProject + {13DEECA0-BDD4-4744-A1A2-8EB0A44DF3D2} = {13DEECA0-BDD4-4744-A1A2-8EB0A44DF3D2} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rtsptest", "tests\rtsptest\rtsptest.vcproj", "{17A33F3F-BAF5-403F-8EF4-FECDA7D9A335}" + ProjectSection(ProjectDependencies) = postProject + {504B3154-7A4F-459D-9877-B951021C3F1F} = {504B3154-7A4F-459D-9877-B951021C3F1F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mrcpunirtsp", "modules\mrcp-unirtsp\mrcpunirtsp.vcproj", "{DEB01ACB-D65F-4A62-AED9-58C1054499E9}" + ProjectSection(ProjectDependencies) = postProject + {12A49562-BAB9-43A3-A21D-15B60BBB4C31} = {12A49562-BAB9-43A3-A21D-15B60BBB4C31} + {504B3154-7A4F-459D-9877-B951021C3F1F} = {504B3154-7A4F-459D-9877-B951021C3F1F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "prepare", "build\tools\prepare.vcproj", "{01D63BF5-7798-4746-852A-4B45229BB735}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unimrcpservice", "build\tools\unimrcpservice.vcproj", "{4714EF49-BFD5-4B22-95F7-95A07F1EAC25}" + ProjectSection(ProjectDependencies) = postProject + {13DEECA0-BDD4-4744-A1A2-8EB0A44DF3D2} = {13DEECA0-BDD4-4744-A1A2-8EB0A44DF3D2} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {592CF22D-3F8F-4A77-A174-130D77B7623B}.Debug|Win32.ActiveCfg = Debug|Win32 + {592CF22D-3F8F-4A77-A174-130D77B7623B}.Debug|Win32.Build.0 = Debug|Win32 + {592CF22D-3F8F-4A77-A174-130D77B7623B}.Release|Win32.ActiveCfg = Release|Win32 + {592CF22D-3F8F-4A77-A174-130D77B7623B}.Release|Win32.Build.0 = Release|Win32 + {13DEECA0-BDD4-4744-A1A2-8EB0A44DF3D2}.Debug|Win32.ActiveCfg = Debug|Win32 + {13DEECA0-BDD4-4744-A1A2-8EB0A44DF3D2}.Debug|Win32.Build.0 = Debug|Win32 + {13DEECA0-BDD4-4744-A1A2-8EB0A44DF3D2}.Release|Win32.ActiveCfg = Release|Win32 + {13DEECA0-BDD4-4744-A1A2-8EB0A44DF3D2}.Release|Win32.Build.0 = Release|Win32 + {B5A00BFA-6083-4FAE-A097-71642D6473B5}.Debug|Win32.ActiveCfg = Debug|Win32 + {B5A00BFA-6083-4FAE-A097-71642D6473B5}.Debug|Win32.Build.0 = Debug|Win32 + {B5A00BFA-6083-4FAE-A097-71642D6473B5}.Release|Win32.ActiveCfg = Release|Win32 + {B5A00BFA-6083-4FAE-A097-71642D6473B5}.Release|Win32.Build.0 = Release|Win32 + {1C320193-46A6-4B34-9C56-8AB584FC1B56}.Debug|Win32.ActiveCfg = Debug|Win32 + {1C320193-46A6-4B34-9C56-8AB584FC1B56}.Debug|Win32.Build.0 = Debug|Win32 + {1C320193-46A6-4B34-9C56-8AB584FC1B56}.Release|Win32.ActiveCfg = Release|Win32 + {1C320193-46A6-4B34-9C56-8AB584FC1B56}.Release|Win32.Build.0 = Release|Win32 + {12A49562-BAB9-43A3-A21D-15B60BBB4C31}.Debug|Win32.ActiveCfg = Debug|Win32 + {12A49562-BAB9-43A3-A21D-15B60BBB4C31}.Debug|Win32.Build.0 = Debug|Win32 + {12A49562-BAB9-43A3-A21D-15B60BBB4C31}.Release|Win32.ActiveCfg = Release|Win32 + {12A49562-BAB9-43A3-A21D-15B60BBB4C31}.Release|Win32.Build.0 = Release|Win32 + {18B1F35A-10F8-4287-9B37-2D10501B0B38}.Debug|Win32.ActiveCfg = Debug|Win32 + {18B1F35A-10F8-4287-9B37-2D10501B0B38}.Debug|Win32.Build.0 = Debug|Win32 + {18B1F35A-10F8-4287-9B37-2D10501B0B38}.Release|Win32.ActiveCfg = Release|Win32 + {18B1F35A-10F8-4287-9B37-2D10501B0B38}.Release|Win32.Build.0 = Release|Win32 + {C98AF157-352E-4737-BD30-A24E2647F5AE}.Debug|Win32.ActiveCfg = Debug|Win32 + {C98AF157-352E-4737-BD30-A24E2647F5AE}.Debug|Win32.Build.0 = Debug|Win32 + {C98AF157-352E-4737-BD30-A24E2647F5AE}.Release|Win32.ActiveCfg = Release|Win32 + {C98AF157-352E-4737-BD30-A24E2647F5AE}.Release|Win32.Build.0 = Release|Win32 + {746F3632-5BB2-4570-9453-31D6D58A7D8E}.Debug|Win32.ActiveCfg = Debug|Win32 + {746F3632-5BB2-4570-9453-31D6D58A7D8E}.Debug|Win32.Build.0 = Debug|Win32 + {746F3632-5BB2-4570-9453-31D6D58A7D8E}.Release|Win32.ActiveCfg = Release|Win32 + {746F3632-5BB2-4570-9453-31D6D58A7D8E}.Release|Win32.Build.0 = Release|Win32 + {72782932-37CC-46AE-8C7F-9A7B1A6EE108}.Debug|Win32.ActiveCfg = Debug|Win32 + {72782932-37CC-46AE-8C7F-9A7B1A6EE108}.Debug|Win32.Build.0 = Debug|Win32 + {72782932-37CC-46AE-8C7F-9A7B1A6EE108}.Release|Win32.ActiveCfg = Release|Win32 + {72782932-37CC-46AE-8C7F-9A7B1A6EE108}.Release|Win32.Build.0 = Release|Win32 + {EE157390-1E85-416C-946E-620E32C9AD33}.Debug|Win32.ActiveCfg = Debug|Win32 + {EE157390-1E85-416C-946E-620E32C9AD33}.Debug|Win32.Build.0 = Debug|Win32 + {EE157390-1E85-416C-946E-620E32C9AD33}.Release|Win32.ActiveCfg = Release|Win32 + {EE157390-1E85-416C-946E-620E32C9AD33}.Release|Win32.Build.0 = Release|Win32 + {57FAF32E-49FD-491F-895D-132D0D5EFE0A}.Debug|Win32.ActiveCfg = Debug|Win32 + {57FAF32E-49FD-491F-895D-132D0D5EFE0A}.Debug|Win32.Build.0 = Debug|Win32 + {57FAF32E-49FD-491F-895D-132D0D5EFE0A}.Release|Win32.ActiveCfg = Release|Win32 + {57FAF32E-49FD-491F-895D-132D0D5EFE0A}.Release|Win32.Build.0 = Release|Win32 + {A9EDAC04-6A5F-4BA7-BC0D-CCE7B255B6EA}.Debug|Win32.ActiveCfg = Debug|Win32 + {A9EDAC04-6A5F-4BA7-BC0D-CCE7B255B6EA}.Debug|Win32.Build.0 = Debug|Win32 + {A9EDAC04-6A5F-4BA7-BC0D-CCE7B255B6EA}.Release|Win32.ActiveCfg = Release|Win32 + {A9EDAC04-6A5F-4BA7-BC0D-CCE7B255B6EA}.Release|Win32.Build.0 = Release|Win32 + {843425BE-9A9A-44F4-A4E3-4B57D6ABD53C}.Debug|Win32.ActiveCfg = Debug|Win32 + {843425BE-9A9A-44F4-A4E3-4B57D6ABD53C}.Debug|Win32.Build.0 = Debug|Win32 + {843425BE-9A9A-44F4-A4E3-4B57D6ABD53C}.Release|Win32.ActiveCfg = Release|Win32 + {843425BE-9A9A-44F4-A4E3-4B57D6ABD53C}.Release|Win32.Build.0 = Release|Win32 + {92BFA534-C419-4EB2-AAA3-510653F38F08}.Debug|Win32.ActiveCfg = Debug|Win32 + {92BFA534-C419-4EB2-AAA3-510653F38F08}.Debug|Win32.Build.0 = Debug|Win32 + {92BFA534-C419-4EB2-AAA3-510653F38F08}.Release|Win32.ActiveCfg = Release|Win32 + {92BFA534-C419-4EB2-AAA3-510653F38F08}.Release|Win32.Build.0 = Release|Win32 + {B495B6D9-AF84-479D-B30A-313C16EF8BFD}.Debug|Win32.ActiveCfg = Debug|Win32 + {B495B6D9-AF84-479D-B30A-313C16EF8BFD}.Debug|Win32.Build.0 = Debug|Win32 + {B495B6D9-AF84-479D-B30A-313C16EF8BFD}.Release|Win32.ActiveCfg = Release|Win32 + {B495B6D9-AF84-479D-B30A-313C16EF8BFD}.Release|Win32.Build.0 = Release|Win32 + {79EF9F1D-E211-4ED1-91D2-FC935AB3A872}.Debug|Win32.ActiveCfg = Debug|Win32 + {79EF9F1D-E211-4ED1-91D2-FC935AB3A872}.Debug|Win32.Build.0 = Debug|Win32 + {79EF9F1D-E211-4ED1-91D2-FC935AB3A872}.Release|Win32.ActiveCfg = Release|Win32 + {79EF9F1D-E211-4ED1-91D2-FC935AB3A872}.Release|Win32.Build.0 = Release|Win32 + {429C907B-97D1-4B2D-9B0E-A14A5BFDAD15}.Debug|Win32.ActiveCfg = Debug|Win32 + {429C907B-97D1-4B2D-9B0E-A14A5BFDAD15}.Debug|Win32.Build.0 = Debug|Win32 + {429C907B-97D1-4B2D-9B0E-A14A5BFDAD15}.Release|Win32.ActiveCfg = Release|Win32 + {429C907B-97D1-4B2D-9B0E-A14A5BFDAD15}.Release|Win32.Build.0 = Release|Win32 + {DCF01B1C-5268-44F3-9130-D647FABFB663}.Debug|Win32.ActiveCfg = Debug|Win32 + {DCF01B1C-5268-44F3-9130-D647FABFB663}.Debug|Win32.Build.0 = Debug|Win32 + {DCF01B1C-5268-44F3-9130-D647FABFB663}.Release|Win32.ActiveCfg = Release|Win32 + {DCF01B1C-5268-44F3-9130-D647FABFB663}.Release|Win32.Build.0 = Release|Win32 + {3CA97077-6210-4362-998A-D15A35EEAA08}.Debug|Win32.ActiveCfg = Debug|Win32 + {3CA97077-6210-4362-998A-D15A35EEAA08}.Debug|Win32.Build.0 = Debug|Win32 + {3CA97077-6210-4362-998A-D15A35EEAA08}.Release|Win32.ActiveCfg = Release|Win32 + {3CA97077-6210-4362-998A-D15A35EEAA08}.Release|Win32.Build.0 = Release|Win32 + {729EF28E-38C9-40DE-A138-87785F021411}.Debug|Win32.ActiveCfg = Debug|Win32 + {729EF28E-38C9-40DE-A138-87785F021411}.Release|Win32.ActiveCfg = Release|Win32 + {504B3154-7A4F-459D-9877-B951021C3F1F}.Debug|Win32.ActiveCfg = Debug|Win32 + {504B3154-7A4F-459D-9877-B951021C3F1F}.Debug|Win32.Build.0 = Debug|Win32 + {504B3154-7A4F-459D-9877-B951021C3F1F}.Release|Win32.ActiveCfg = Release|Win32 + {504B3154-7A4F-459D-9877-B951021C3F1F}.Release|Win32.Build.0 = Release|Win32 + {17A33F3F-BAF5-403F-8EF4-FECDA7D9A335}.Debug|Win32.ActiveCfg = Debug|Win32 + {17A33F3F-BAF5-403F-8EF4-FECDA7D9A335}.Debug|Win32.Build.0 = Debug|Win32 + {17A33F3F-BAF5-403F-8EF4-FECDA7D9A335}.Release|Win32.ActiveCfg = Release|Win32 + {17A33F3F-BAF5-403F-8EF4-FECDA7D9A335}.Release|Win32.Build.0 = Release|Win32 + {DEB01ACB-D65F-4A62-AED9-58C1054499E9}.Debug|Win32.ActiveCfg = Debug|Win32 + {DEB01ACB-D65F-4A62-AED9-58C1054499E9}.Debug|Win32.Build.0 = Debug|Win32 + {DEB01ACB-D65F-4A62-AED9-58C1054499E9}.Release|Win32.ActiveCfg = Release|Win32 + {DEB01ACB-D65F-4A62-AED9-58C1054499E9}.Release|Win32.Build.0 = Release|Win32 + {01D63BF5-7798-4746-852A-4B45229BB735}.Debug|Win32.ActiveCfg = Debug|Win32 + {01D63BF5-7798-4746-852A-4B45229BB735}.Release|Win32.ActiveCfg = Release|Win32 + {4714EF49-BFD5-4B22-95F7-95A07F1EAC25}.Debug|Win32.ActiveCfg = Debug|Win32 + {4714EF49-BFD5-4B22-95F7-95A07F1EAC25}.Release|Win32.ActiveCfg = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {13DEECA0-BDD4-4744-A1A2-8EB0A44DF3D2} = {5377DC3A-DB96-4819-8AAF-2A75F3A69119} + {B5A00BFA-6083-4FAE-A097-71642D6473B5} = {5377DC3A-DB96-4819-8AAF-2A75F3A69119} + {1C320193-46A6-4B34-9C56-8AB584FC1B56} = {5377DC3A-DB96-4819-8AAF-2A75F3A69119} + {12A49562-BAB9-43A3-A21D-15B60BBB4C31} = {5377DC3A-DB96-4819-8AAF-2A75F3A69119} + {18B1F35A-10F8-4287-9B37-2D10501B0B38} = {5377DC3A-DB96-4819-8AAF-2A75F3A69119} + {72782932-37CC-46AE-8C7F-9A7B1A6EE108} = {5377DC3A-DB96-4819-8AAF-2A75F3A69119} + {A9EDAC04-6A5F-4BA7-BC0D-CCE7B255B6EA} = {5377DC3A-DB96-4819-8AAF-2A75F3A69119} + {843425BE-9A9A-44F4-A4E3-4B57D6ABD53C} = {5377DC3A-DB96-4819-8AAF-2A75F3A69119} + {504B3154-7A4F-459D-9877-B951021C3F1F} = {5377DC3A-DB96-4819-8AAF-2A75F3A69119} + {746F3632-5BB2-4570-9453-31D6D58A7D8E} = {493A1DB9-6E7C-48C7-93B5-F75C3C25B9DF} + {DEB01ACB-D65F-4A62-AED9-58C1054499E9} = {493A1DB9-6E7C-48C7-93B5-F75C3C25B9DF} + {592CF22D-3F8F-4A77-A174-130D77B7623B} = {8E282AE2-038C-49FE-AC67-BC9615AFD800} + {C98AF157-352E-4737-BD30-A24E2647F5AE} = {8E282AE2-038C-49FE-AC67-BC9615AFD800} + {EE157390-1E85-416C-946E-620E32C9AD33} = {8E282AE2-038C-49FE-AC67-BC9615AFD800} + {57FAF32E-49FD-491F-895D-132D0D5EFE0A} = {8E282AE2-038C-49FE-AC67-BC9615AFD800} + {92BFA534-C419-4EB2-AAA3-510653F38F08} = {09BABD45-8F30-4F99-B8B8-8DD78F6804DB} + {B495B6D9-AF84-479D-B30A-313C16EF8BFD} = {09BABD45-8F30-4F99-B8B8-8DD78F6804DB} + {729EF28E-38C9-40DE-A138-87785F021411} = {09BABD45-8F30-4F99-B8B8-8DD78F6804DB} + {79EF9F1D-E211-4ED1-91D2-FC935AB3A872} = {AC4356E8-48A1-4D2D-AFB1-11CF30B974CD} + {429C907B-97D1-4B2D-9B0E-A14A5BFDAD15} = {AC4356E8-48A1-4D2D-AFB1-11CF30B974CD} + {DCF01B1C-5268-44F3-9130-D647FABFB663} = {AC4356E8-48A1-4D2D-AFB1-11CF30B974CD} + {3CA97077-6210-4362-998A-D15A35EEAA08} = {AC4356E8-48A1-4D2D-AFB1-11CF30B974CD} + {17A33F3F-BAF5-403F-8EF4-FECDA7D9A335} = {AC4356E8-48A1-4D2D-AFB1-11CF30B974CD} + {01D63BF5-7798-4746-852A-4B45229BB735} = {62083CC3-13BF-49EA-BFE8-4C9337C0D82C} + {4714EF49-BFD5-4B22-95F7-95A07F1EAC25} = {62083CC3-13BF-49EA-BFE8-4C9337C0D82C} + EndGlobalSection +EndGlobal