Contributor(s):
Kamil Shakirov <kamils80@gmail.com>
- Anthony Masse <amasse.telisma@gmail.com>
+ Anthony Masse <amasse.atwork@gmail.com>
Vlad Socaciu <Curatica@gmail.com>
Garmt <garmt.noname@gmail.com>
- Patrick <pnunes29@gmail.com>
- Bayram <bayramboyraz@gmail.com>
- Mahmoud Hassan <firstmahmoud2002@gmail.com>
\ No newline at end of file
+ Patrick Nunes <pnunes29@gmail.com>
+ Bayram Boyraz <bayramboyraz@gmail.com>
+ Mahmoud Hassan <firstmahmoud2002@gmail.com>
+ Michael Jerris <mike@jerris.com>
+ Carlos Pina Soares
+ Chaitanya Chokkareddy <chaitanya.chokkareddy@gmail.com>
+ Tomas Valenta <tomas.valenta@speechtech.cz>
BUILD REQUIREMENTS
==================
-UniMRCP depends on a number of third party tools and libraries,
-which must be installed prior to UniMRCP build.
+UniMRCP depends on a number of third party tools and libraries,
+which are required and must be installed first.
-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)
+The easiest and recommended way is to install an appropriate
+dependency package from the download area, which contains APR,
+APR-Util and Sofia-SIP libraries prepackaged for UniMRCP use.
+
+http://code.google.com/p/unimrcp/downloads/
+
+Alternatively, the original packages of APR, APR-Util and
+Sofia-SIP libraries and patches for them can be downloaded from
+
+http://www.unimrcp.org/dependencies/
+
+References:
+
+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
+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
===================
Installed directory layout
bin - binaries (unimrcpserver, unimrcpclient)
conf - configuration files
+data - data files
include - header files
-libs - shared (convenient) libraries
-plugins - run-time loadable modules
+lib - shared (convenient) libraries
+log - log files
+plugin - run-time loadable modules
There are a couple of options to "./configure".
To specify where to look for the APR and APR-util libraries
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
+data - data files
+log - log files
+plugin - run-time loadable modules
def-data:
test -d $(datadir) || $(mkinstalldirs) $(datadir)
- for datafile in `find data -name *.pcm -o -name *.xml` ; do \
+ for datafile in `find data -name *.pcm -o -name *.xml -o -name *.jsgf -o -name *.txt` ; do \
filename=`echo $$datafile | sed -e 's|^.*/||'`; \
$(INSTALL) -m 644 data/$$filename $(datadir); \
done
>
<Tool
Name="VCPreBuildEventTool"
- CommandLine="xcopy "$(AprDir)\$(ConfigurationName)\libapr-1.dll" "$(SolutionDir)$(ConfigurationName)\bin\" /Y
xcopy "$(AprUtilDir)\$(ConfigurationName)\libaprutil-1.dll" "$(SolutionDir)$(ConfigurationName)\bin\" /Y
xcopy "$(AprIconvDir)\$(ConfigurationName)\libapriconv-1.dll" "$(SolutionDir)$(ConfigurationName)\bin\" /Y
xcopy "$(SofiaDir)\win32\libsofia-sip-ua\$(ConfigurationName)\libsofia_sip_ua.dll" "$(SolutionDir)$(ConfigurationName)\bin\" /Y
xcopy "$(SofiaDir)\win32\pthread\pthreadVC2.dll" "$(SolutionDir)$(ConfigurationName)\bin\" /Y

if not exist "$(SolutionDir)$(ConfigurationName)\conf" xcopy "$(SolutionDir)conf\*.xml" "$(SolutionDir)$(ConfigurationName)\conf\" /Y
if not exist "$(SolutionDir)$(ConfigurationName)\data" xcopy "$(SolutionDir)data\*" "$(SolutionDir)$(ConfigurationName)\data\" /Y
if not exist "$(SolutionDir)$(ConfigurationName)\log" mkdir "$(SolutionDir)$(ConfigurationName)\log\"
"
+ CommandLine="xcopy "$(AprDir)\$(ConfigurationName)\libapr-1.dll" "$(SolutionDir)$(ConfigurationName)\bin\" /Y
xcopy "$(AprUtilDir)\$(ConfigurationName)\libaprutil-1.dll" "$(SolutionDir)$(ConfigurationName)\bin\" /Y

if exist "$(AprIconvDir)\$(ConfigurationName)\libapriconv-1.dll" (
 xcopy "$(AprIconvDir)\$(ConfigurationName)\libapriconv-1.dll" "$(SolutionDir)$(ConfigurationName)\bin\" /Y
 if exist "$(AprIconvDir)\$(ConfigurationName)\iconv" (
 if not exist "$(SolutionDir)$(ConfigurationName)\bin\iconv" mkdir "$(SolutionDir)$(ConfigurationName)\bin\iconv"
 xcopy "$(AprIconvDir)\$(ConfigurationName)\iconv\*.so" "$(SolutionDir)$(ConfigurationName)\bin\iconv\" /Y
 )
)

xcopy "$(SofiaDir)\win32\libsofia-sip-ua\$(ConfigurationName)\libsofia_sip_ua.dll" "$(SolutionDir)$(ConfigurationName)\bin\" /Y
xcopy "$(SofiaDir)\win32\pthread\pthreadVC2.dll" "$(SolutionDir)$(ConfigurationName)\bin\" /Y

if not exist "$(SolutionDir)$(ConfigurationName)\conf" xcopy "$(SolutionDir)conf\*.xml" "$(SolutionDir)$(ConfigurationName)\conf\" /Y
if not exist "$(SolutionDir)$(ConfigurationName)\data" xcopy "$(SolutionDir)data\*" "$(SolutionDir)$(ConfigurationName)\data\" /Y
if not exist "$(SolutionDir)$(ConfigurationName)\log" mkdir "$(SolutionDir)$(ConfigurationName)\log\"
"
ExcludedFromBuild="false"
/>
<Tool
>
<Tool
Name="VCPreBuildEventTool"
- CommandLine="xcopy "$(AprDir)\$(ConfigurationName)\libapr-1.dll" "$(SolutionDir)$(ConfigurationName)\bin\" /Y
xcopy "$(AprUtilDir)\$(ConfigurationName)\libaprutil-1.dll" "$(SolutionDir)$(ConfigurationName)\bin\" /Y
xcopy "$(AprIconvDir)\$(ConfigurationName)\libapriconv-1.dll" "$(SolutionDir)$(ConfigurationName)\bin\" /Y
xcopy "$(SofiaDir)\win32\libsofia-sip-ua\$(ConfigurationName)\libsofia_sip_ua.dll" "$(SolutionDir)$(ConfigurationName)\bin\" /Y
xcopy "$(SofiaDir)\win32\pthread\pthreadVC2.dll" "$(SolutionDir)$(ConfigurationName)\bin\" /Y

if not exist "$(SolutionDir)$(ConfigurationName)\conf" xcopy "$(SolutionDir)conf\*.xml" "$(SolutionDir)$(ConfigurationName)\conf\" /Y
if not exist "$(SolutionDir)$(ConfigurationName)\data" xcopy "$(SolutionDir)data\*" "$(SolutionDir)$(ConfigurationName)\data\" /Y
if not exist "$(SolutionDir)$(ConfigurationName)\log" mkdir "$(SolutionDir)$(ConfigurationName)\log\"
"
+ CommandLine="xcopy "$(AprDir)\$(ConfigurationName)\libapr-1.dll" "$(SolutionDir)$(ConfigurationName)\bin\" /Y
xcopy "$(AprUtilDir)\$(ConfigurationName)\libaprutil-1.dll" "$(SolutionDir)$(ConfigurationName)\bin\" /Y

if exist "$(AprIconvDir)\$(ConfigurationName)\libapriconv-1.dll" (
 xcopy "$(AprIconvDir)\$(ConfigurationName)\libapriconv-1.dll" "$(SolutionDir)$(ConfigurationName)\bin\" /Y
 if exist "$(AprIconvDir)\$(ConfigurationName)\iconv" (
 if not exist "$(SolutionDir)$(ConfigurationName)\bin\iconv" mkdir "$(SolutionDir)$(ConfigurationName)\bin\iconv"
 xcopy "$(AprIconvDir)\$(ConfigurationName)\iconv\*.so" "$(SolutionDir)$(ConfigurationName)\bin\iconv\" /Y
 )
)

xcopy "$(SofiaDir)\win32\libsofia-sip-ua\$(ConfigurationName)\libsofia_sip_ua.dll" "$(SolutionDir)$(ConfigurationName)\bin\" /Y
xcopy "$(SofiaDir)\win32\pthread\pthreadVC2.dll" "$(SolutionDir)$(ConfigurationName)\bin\" /Y

if not exist "$(SolutionDir)$(ConfigurationName)\conf" xcopy "$(SolutionDir)conf\*.xml" "$(SolutionDir)$(ConfigurationName)\conf\" /Y
if not exist "$(SolutionDir)$(ConfigurationName)\data" xcopy "$(SolutionDir)data\*" "$(SolutionDir)$(ConfigurationName)\data\" /Y
if not exist "$(SolutionDir)$(ConfigurationName)\log" mkdir "$(SolutionDir)$(ConfigurationName)\log\"
"
/>
<Tool
Name="VCCustomBuildTool"
* Minor API changes that do not cause binary compatibility problems.
* Reset to 0 when upgrading UNI_MAJOR_VERSION
*/
-#define UNI_MINOR_VERSION 6
+#define UNI_MINOR_VERSION 8
/** patch level
* The Patch Level never includes API changes, simply bug fixes.
/>
<UserMacro
Name="UniMRCPServerLibs"
- Value="libunimrcpserver.lib mrcpserver.lib mrcpsignaling.lib mrcp.lib aprtoolkit.lib libaprutil-1.lib libapr-1.lib libsofia_sip_ua.lib ws2_32.lib winmm.lib"
+ Value="libunimrcpserver.lib mrcpserver.lib mrcpsignaling.lib mrcpengine.lib mrcp.lib aprtoolkit.lib libaprutil-1.lib libapr-1.lib libsofia_sip_ua.lib ws2_32.lib winmm.lib"
/>
</VisualStudioPropertySheet>
--- /dev/null
+<!--
+UniMRCP Client Scenarios.
+There are predefined, named scenarios, which you can customize and use.
+Currently available scenarios are
+1. class="Synthesizer"
+2. class="Recognizer"
+3. class="Recorder"
+4. class="DtmfRecofnizer"
+
+More than one scenario of the same type (class) can be defined.
+The name (not class) of the scenario is used to run it from console.
+For instance,
+> run synth
+-->
+
+<umcscenarios>
+ <scenario name="synth" class="Synthesizer" profile="MRCPv2-Default">
+ <resource-discovery enable="0"/>
+ <speak enable="1" content-type="application/synthesis+ssml" content-location="speak.xml"/>
+ <!-- <speak enable="1" content-type="text/plain" content-location="speak.txt"/> -->
+
+ <termination enable="1">
+ <capabilities>
+ <codec name="LPCM" rates="8000 16000"/>
+ <!-- <codec name="PCMU" rates="8000 16000"/> -->
+ </capabilities>
+ </termination>
+ <!--
+ <rtp-termination enable="1" ip="127.0.0.1" port="5678">
+ <codecs="PCMU PCMA L16/96/8000 PCMU/97/16000 telephone-event/101/8000"/>
+ </rtp-termination>
+ -->
+ </scenario>
+
+ <scenario name="recog" class="Recognizer" profile="MRCPv2-Default">
+ <resource-discovery enable="0"/>
+ <define-grammar enable="1" content-type="application/srgs+xml" content-location="grammar.xml"/>
+ <!-- <define-grammar enable="1" content-type="application/grammar+xml" content-location="grammar.xml"/> -->
+ <!-- <define-grammar enable="1" content-type="application/x-jsgf" content-location="grammar.jsgf"/> -->
+ <recognize enable="1"/>
+ <!-- <recognize enable="1" audio-source="one-8kHz.pcm"/> -->
+ <!-- <recognize enable="1" content-type="application/srgs+xml" content-location="grammar.xml"/> -->
+
+ <termination enable="1">
+ <capabilities>
+ <codec name="LPCM" rates="8000 16000"/>
+ <!-- <codec name="PCMU" rates="8000 16000"/> -->
+ </capabilities>
+ </termination>
+ <!--
+ <rtp-termination enable="1" ip="127.0.0.1" port="5678">
+ <codecs="PCMU PCMA L16/96/8000 PCMU/97/16000 telephone-event/101/8000"/>
+ </rtp-termination>
+ -->
+ </scenario>
+
+ <scenario name="rec" class="Recorder" profile="MRCPv2-Default">
+ <resource-discovery enable="0"/>
+ <record enable="1"/>
+
+ <termination enable="1">
+ <capabilities>
+ <codec name="LPCM" rates="8000 16000"/>
+ <!-- <codec name="PCMU" rates="8000 16000"/> -->
+ </capabilities>
+ </termination>
+ <!--
+ <rtp-termination enable="1" ip="127.0.0.1" port="5678">
+ <codecs="PCMU PCMA L16/96/8000 PCMU/97/16000 telephone-event/101/8000"/>
+ </rtp-termination>
+ -->
+ </scenario>
+
+ <scenario name="dtmf" class="DtmfRecognizer" profile="MRCPv2-Default">
+ <resource-discovery enable="0"/>
+ <recognize enable="1" content-type="text/uri-list" grammar="builtin:dtmf/digits" digits="1234"/> -->
+
+ <termination enable="1">
+ <capabilities>
+ <codec name="LPCM" rates="8000 16000"/>
+ <!-- <codec name="PCMU" rates="8000 16000"/> -->
+ </capabilities>
+ </termination>
+ <!--
+ <rtp-termination enable="1" ip="127.0.0.1" port="5678">
+ <codecs="PCMU PCMA L16/96/8000 PCMU/97/16000 telephone-event/101/8000"/>
+ </rtp-termination>
+ -->
+ </scenario>
+
+ <!-- It'd be great to have scriptable custom scenarios as well.
+ <scenario name="custom" class="UmcXml">
+ </scenario>
+ -->
+
+</umcscenarios>
<!-- UniMRCP client document -->
<unimrcpclient>
+ <!-- MRCP resources -->
+ <resources>
+ <resource class="speechsynth" enable="1"/>
+ <resource class="speechrecog" enable="1"/>
+ <resource class="recorder" enable="1"/>
+ </resources>
+
<!-- Client settings (signaling, media, ...) -->
<settings>
<!-- SIP, RTSP signaling agents -->
<!-- <param name="playout-delay" value="50"/> -->
<!-- <param name="max-playout-delay" value="200"/> -->
<!-- <param name="ptime" value="20"/> -->
- <param name="codecs" value="PCMU PCMA L16/96/8000"/>
+ <param name="codecs" value="PCMU PCMA L16/96/8000 PCMU/97/16000 telephone-event/101/8000"/>
+ <!-- <param name="codecs" value="PCMU PCMA L16/96/8000 PCMU/97/16000 PCMA/98/16000 L16/99/16000"/> -->
+
+ <!-- enable/disable rtcp support -->
+ <param name="rtcp" value="0"/>
+ <!-- rtcp bye policies (rtcp must be enabled first)
+ 0 - disable rtcp bye
+ 1 - send rtcp bye at the end of session
+ 2 - send rtcp bye also at the end of each talkspurt (input)
+ -->
+ <param name="rtcp-bye" value="1"/>
+ <!-- rtcp transmission interval in msec (set 0 to disable) -->
+ <param name="rtcp-tx-interval" value="5000"/>
+ <!-- period (timeout) to check for new rtcp messages in msec (set 0 to disable) -->
+ <param name="rtcp-rx-resolution" value="1000"/>
</rtp>
</media>
</settings>
<!-- unimrcpserver document -->
<unimrcpserver>
+ <!-- MRCP resources -->
+ <resources>
+ <resource class="speechsynth" enable="1"/>
+ <resource class="speechrecog" enable="1"/>
+ <resource class="recorder" enable="1"/>
+ </resources>
+
<!-- Server settings (signaling, media, ...) -->
<settings>
<!-- SIP, RTSP signaling agents -->
<!-- <param name="playout-delay" value="50"/> -->
<!-- <param name="max-playout-delay" value="200"/> -->
<!-- <param name="ptime" value="20"/> -->
- <param name="codecs" value="PCMU PCMA L16/96/8000"/>
+ <param name="codecs" value="PCMU PCMA L16/96/8000 telephone-event/101/8000"/>
+ <!-- <param name="codecs" value="PCMU PCMA L16/96/8000 PCMU/97/16000 PCMA/98/16000 L16/99/16000"/> -->
<!-- <param name="own-preference" value="1"/> -->
+
+ <!-- enable/disable rtcp support -->
+ <param name="rtcp" value="0"/>
+ <!-- rtcp bye policies (rtcp must be enabled first)
+ 0 - disable rtcp bye
+ 1 - send rtcp bye at the end of session
+ 2 - send rtcp bye also at the end of each talkspurt (input)
+ -->
+ <param name="rtcp-bye" value="1"/>
+ <!-- rtcp transmission interval in msec -->
+ <param name="rtcp-tx-interval" value="5000"/>
+ <!-- period (timeout) to check for new rtcp messages in msec (set 0 to disable) -->
+ <param name="rtcp-rx-resolution" value="1000"/>
</rtp>
</media>
<engine name="Flite-1" class="mrcpflite" enable="0"/>
<engine name="Demo-Synth-1" class="demosynth" enable="1"/>
<engine name="Demo-Recog-1" class="demorecog" enable="1"/>
+ <engine name="Recorder-1" class="mrcprecorder" enable="1"/>
</plugin>
</settings>
AC_PREREQ(2.57)
-AC_INIT([unimrcp],[0.6.0])
+AC_INIT([unimrcp],[0.8.0])
AC_CONFIG_AUX_DIR([build])
AC_CONFIG_MACRO_DIR([build/acmacros])
# 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"
+plugin_version_hdr="libs/mrcp-engine/include/mrcp_engine_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`"
AM_CONDITIONAL([DEMORECOG_PLUGIN],[test "${enable_demorecog_plugin}" = "yes"])
+#Enable recorder plugin
+AC_ARG_ENABLE(recorder-plugin,
+ [AC_HELP_STRING([--disable-recorder-plugin ],[exclude recorder plugin from build])],
+ [enable_recorder_plugin="$enableval"],
+ [enable_recorder_plugin="yes"])
+
+AM_CONDITIONAL([RECORDER_PLUGIN],[test "${enable_recorder_plugin}" = "yes"])
+
+
#Enable Cepstral Swift plugin
AC_ARG_ENABLE(cepstral-plugin,
[AC_HELP_STRING([--disable-cepstral-plugin ],[exclude cepstral plugin from build])],
plugins/mrcp-cepstral/Makefile
plugins/mrcp-pocketsphinx/Makefile
plugins/mrcp-flite/Makefile
+ plugins/mrcp-recorder/Makefile
plugins/demo-synth/Makefile
plugins/demo-recog/Makefile
platforms/Makefile
platforms/libunimrcp-client/Makefile
platforms/unimrcp-server/Makefile
platforms/unimrcp-client/Makefile
+ platforms/libasr-client/Makefile
+ platforms/asr-client/Makefile
+ platforms/umc/Makefile
tests/Makefile
tests/apttest/Makefile
tests/mpftest/Makefile
--- /dev/null
+#JSGF V1.0;\r
+grammar digits;\r
+public <numbers> = (one | two | three);\r
--- /dev/null
+Hello World.
\ No newline at end of file
APT_BEGIN_EXTERN_C
+/** Default size (number of elements) of cyclic queue */
#define CYCLIC_QUEUE_DEFAULT_SIZE 100
/** Opaque cyclic queue declaration */
/**
* Destroy the singleton instance of the logger.
*/
-APT_DECLARE(apt_bool_t) apt_log_instance_destroy();
+APT_DECLARE(apt_bool_t) apt_log_instance_destroy(void);
/**
* Get the singleton instance of the logger.
*/
-APT_DECLARE(apt_logger_t*) apt_log_instance_get();
+APT_DECLARE(apt_logger_t*) apt_log_instance_get(void);
/**
* Set the singleton instance of the logger.
/**
* Close the log file.
*/
-APT_DECLARE(apt_bool_t) apt_log_file_close();
+APT_DECLARE(apt_bool_t) apt_log_file_close(void);
/**
* Set the logging output.
*/
apt_bool_t apt_ip_get(char **addr, apr_pool_t *pool);
+
+/**
+ * Get current NTP time
+ * @param sec the seconds of the NTP time to return
+ * @param frac the fractions of the NTP time to return
+ */
+void apt_ntp_time_get(apr_uint32_t *sec, apr_uint32_t *frac);
+
APT_END_EXTERN_C
#endif /*__APT_NET_H__*/
/** Load NLSML document */
APT_DECLARE(apr_xml_doc*) nlsml_doc_load(const apt_str_t *data, apr_pool_t *pool);
-/** Get the first <interpretation> element */
+/** Get the first interpretation element */
APT_DECLARE(apr_xml_elem*) nlsml_first_interpret_get(const apr_xml_doc *doc);
-/** Get the next <interpretation> element */
+/** Get the next interpretation element */
APT_DECLARE(apr_xml_elem*) nlsml_next_interpret_get(const apr_xml_elem *interpret);
-/** Get <instance> and <input> elements of <interpretation> element */
+/** Get instance and input elements of interpretation element */
APT_DECLARE(apt_bool_t) nlsml_interpret_results_get(const apr_xml_elem *interpret, apr_xml_elem **instance, apr_xml_elem **input);
-/** Get specified atrribute of <input> */
+/** Get specified atrribute of input element */
APT_DECLARE(const char *) nlsml_input_attrib_get(const apr_xml_elem *input, const char *attrib, apt_bool_t recursive);
/**
* Create APR pool
*/
-APT_DECLARE(apr_pool_t*) apt_pool_create();
+APT_DECLARE(apr_pool_t*) apt_pool_create(void);
/**
* Create APR subpool pool
APT_DECLARE(apt_bool_t) apt_task_destroy(apt_task_t *task);
/**
- * Add slave task.
- * @param task the task to add slave task to
+ * Add child task.
+ * @param task the task to add child 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);
*/
APT_DECLARE(const char*) apt_task_name_get(apt_task_t *task);
+/**
+ * Enable/disable auto ready mode.
+ * @param task the task to set mode for
+ * @param auto_ready the enabled/disabled auto ready mode
+ */
+APT_DECLARE(void) apt_task_auto_ready_set(apt_task_t *task, apt_bool_t auto_ready);
+
+/**
+ * Explicitly indicate task is ready to process messages.
+ * @param task the task
+ */
+APT_DECLARE(apt_bool_t) apt_task_ready(apt_task_t *task);
+
/**
* Hold task execution.
* @param msec the time to hold
/**
* Create test framework.
*/
-APT_DECLARE(apt_test_framework_t*) apt_test_framework_create();
+APT_DECLARE(apt_test_framework_t*) apt_test_framework_create(void);
/**
* Destroy test framework.
#include <apr_queue.h>
#include "apt_consumer_task.h"
+#include "apt_log.h"
struct apt_consumer_task_t {
void *obj;
}
while(running) {
+ apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Wait for Task Messages [%s]",apt_task_name_get(task));
rv = apr_queue_pop(consumer_task->msg_queue,&msg);
if(rv == APR_SUCCESS) {
if(msg) {
log_entry[offset++] = '\n';
log_entry[offset] = '\0';
if((apt_logger->mode & APT_LOG_OUTPUT_CONSOLE) == APT_LOG_OUTPUT_CONSOLE) {
- printf(log_entry);
+ fwrite(log_entry,offset,1,stdout);
}
if((apt_logger->mode & APT_LOG_OUTPUT_FILE) == APT_LOG_OUTPUT_FILE && apt_logger->file_data) {
}
return TRUE;
}
+
+/** Seconds from Jan 1 1900 to Jan 1 1970 */
+#define NTP_TIME_OFFSET 2208988800UL
+
+/** Get current NTP time */
+void apt_ntp_time_get(apr_uint32_t *sec, apr_uint32_t *frac)
+{
+ apr_uint32_t t;
+ apr_uint32_t usec;
+
+ apr_time_t now = apr_time_now();
+ *sec = (apr_uint32_t)apr_time_sec(now) + NTP_TIME_OFFSET;
+
+ usec = (apr_uint32_t) apr_time_usec(now);
+ t = (usec * 1825) >> 5;
+ *frac = ((usec << 12) + (usec << 8) - t);
+}
vtable->destroy = apt_net_client_task_on_destroy;
vtable->signal_msg = apt_net_client_task_msg_signal;
}
+ apt_task_auto_ready_set(task->base,FALSE);
task->msg_queue = apt_cyclic_queue_create(CYCLIC_QUEUE_DEFAULT_SIZE);
apr_thread_mutex_create(&task->guard,APR_THREAD_MUTEX_UNNESTED,pool);
return FALSE;
}
+ /* explicitly indicate task is ready to process messages */
+ apt_task_ready(task->base);
+
while(running) {
status = apt_pollset_poll(task->pollset, -1, &num, &ret_pfd);
if(status != APR_SUCCESS) {
vtable->destroy = apt_net_server_task_on_destroy;
vtable->signal_msg = apt_net_server_task_msg_signal;
}
+ apt_task_auto_ready_set(task->base,FALSE);
task->msg_queue = apt_cyclic_queue_create(CYCLIC_QUEUE_DEFAULT_SIZE);
apr_thread_mutex_create(&task->guard,APR_THREAD_MUTEX_UNNESTED,pool);
return FALSE;
}
+ /* explicitly indicate task is ready to process messages */
+ apt_task_ready(task->base);
+
while(running) {
status = apt_pollset_poll(task->pollset, -1, &num, &ret_pfd);
if(status != APR_SUCCESS) {
}
arr = apr_array_copy(pool,src_arr);
for(i=0; i<arr->nelts; i++) {
- pair = (apt_pair_t*)arr->elts + i;
- src_pair = (const apt_pair_t*)src_arr->elts + i;
+ pair = &APR_ARRAY_IDX(arr,i,apt_pair_t);
+ src_pair = &APR_ARRAY_IDX(src_arr,i,const apt_pair_t);
apt_pair_copy(pair,src_pair,pool);
}
return arr;
int i;
apt_pair_t *pair;
for(i=0; i<arr->nelts; i++) {
- pair = (apt_pair_t*)arr->elts + i;
+ pair = &APR_ARRAY_IDX(arr,i,apt_pair_t);
if(apt_string_compare(&pair->name,name) == TRUE) {
return pair;
}
#include "apt_pool.h"
-//#define OWN_ALLOCATOR_PER_POOL
+#define OWN_ALLOCATOR_PER_POOL
APT_DECLARE(apr_pool_t*) apt_pool_create()
{
apr_allocator_owner_set(allocator,pool);
apr_thread_mutex_create(&mutex,APR_THREAD_MUTEX_NESTED,pool);
apr_allocator_mutex_set(allocator,mutex);
+ apr_pool_mutex_set(pool,mutex);
}
}
#else
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 */
+ apt_bool_t auto_ready; /* if TRUE, task is implicitly ready to process messages */
const char *name; /* name of the task */
};
task->child_tasks = apt_list_create(pool);
task->pending_start = 0;
task->pending_term = 0;
+ task->auto_ready = TRUE;
task->name = "Task";
return task;
}
return TRUE;
}
+APT_DECLARE(void) apt_task_auto_ready_set(apt_task_t *task, apt_bool_t auto_ready)
+{
+ task->auto_ready = auto_ready;
+}
+
+APT_DECLARE(apt_bool_t) apt_task_ready(apt_task_t *task)
+{
+ if(task->auto_ready == TRUE) {
+ return FALSE;
+ }
+
+ /* start child tasks (if any) */
+ apt_task_child_start(task);
+ return TRUE;
+}
+
+
static void* APR_THREAD_FUNC apt_task_run(apr_thread_t *thread_handle, void *data)
{
apt_task_t *task = data;
task->state = TASK_STATE_RUNNING;
apr_thread_mutex_unlock(task->data_guard);
- /* start child tasks (if any) */
- apt_task_child_start(task);
+ if(task->auto_ready == TRUE) {
+ /* start child tasks (if any) */
+ apt_task_child_start(task);
+ }
/* run task */
if(task->vtable.run) {
include/mpf_codec_descriptor.h \
include/mpf_codec_manager.h \
include/mpf_context.h \
+ include/mpf_dtmf_detector.h \
+ include/mpf_dtmf_generator.h \
include/mpf_engine.h \
include/mpf_frame.h \
include/mpf_frame_buffer.h \
include/mpf_message.h \
+ include/mpf_mixer.h \
+ include/mpf_multiplier.h \
+ include/mpf_named_event.h \
include/mpf_object.h \
include/mpf_stream.h \
- include/mpf_stream_mode.h \
+ include/mpf_stream_descriptor.h \
include/mpf_termination.h \
+ include/mpf_termination_factory.h \
include/mpf_rtp_termination_factory.h \
include/mpf_file_termination_factory.h \
- include/mpf_timer.h \
+ include/mpf_scheduler.h \
+ include/mpf_timer_manager.h \
include/mpf_types.h \
include/mpf_encoder.h \
include/mpf_decoder.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
+ include/mpf_rtp_pt.h \
+ include/mpf_rtcp_packet.h \
+ include/mpf_resampler.h
libmpf_la_SOURCES = codecs/g711/g711.c \
src/mpf_activity_detector.c \
src/mpf_codec_linear.c \
src/mpf_codec_manager.c \
src/mpf_context.c \
+ src/mpf_dtmf_detector.c \
+ src/mpf_dtmf_generator.c \
src/mpf_engine.c \
+ src/mpf_mixer.c \
+ src/mpf_multiplier.c \
+ src/mpf_named_event.c \
src/mpf_termination.c \
+ src/mpf_termination_factory.c \
src/mpf_rtp_termination_factory.c \
src/mpf_file_termination_factory.c \
src/mpf_frame_buffer.c \
- src/mpf_timer.c \
+ src/mpf_scheduler.c \
+ src/mpf_timer_manager.c \
src/mpf_encoder.c \
src/mpf_decoder.c \
src/mpf_jitter_buffer.c \
src/mpf_rtp_stream.c \
- src/mpf_rtp_attribs.c
+ src/mpf_rtp_attribs.c \
+ src/mpf_resampler.c \
+ src/mpf_stream.c
*/
#include "mpf_frame.h"
-#include "mpf_codec.h"
+#include "mpf_codec_descriptor.h"
APT_BEGIN_EXTERN_C
/** 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);
+/** Set timeout required to trigger speech (transition from inactive to active state) */
+MPF_DECLARE(void) mpf_activity_detector_speech_timeout_set(mpf_activity_detector_t *detector, apr_size_t speech_timeout);
+
+/** Set timeout required to trigger silence (transition from active to inactive state) */
+MPF_DECLARE(void) mpf_activity_detector_silence_timeout_set(mpf_activity_detector_t *detector, apr_size_t silence_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);
*/
#include <stdio.h>
-#include "mpf_stream_mode.h"
-#include "mpf_codec_descriptor.h"
+#include "mpf_stream_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
+/** FILE_READER defined as a stream source */
+#define FILE_READER STREAM_DIRECTION_RECEIVE
+/** FILE_WRITER defined as a stream sink */
+#define FILE_WRITER STREAM_DIRECTION_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;
+ /** Indicate descriptor type (reader and/or writer) */
+ mpf_stream_direction_e mask;
/** Codec descriptor to use for audio file read/write */
- mpf_codec_descriptor_t codec_descriptor;
+ mpf_codec_descriptor_t *codec_descriptor;
/** File handle to read audio stream */
- FILE *read_handle;
+ FILE *read_handle;
/** File handle to write audio stream */
- FILE *write_handle;
+ FILE *write_handle;
/** Max size of file */
- apr_size_t max_write_size;
+ apr_size_t max_write_size;
};
APT_END_EXTERN_C
* Create bridge of audio streams.
* @param source the source audio stream
* @param sink the sink audio stream
+ * @param codec_manager the codec manager
* @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);
+MPF_DECLARE(mpf_object_t*) mpf_bridge_create(
+ mpf_audio_stream_t *source,
+ mpf_audio_stream_t *sink,
+ const mpf_codec_manager_t *codec_manager,
+ apr_pool_t *pool);
APT_END_EXTERN_C
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 */
codec->vtable = vtable;
codec->attribs = attribs;
codec->static_descriptor = descriptor;
- codec->descriptor = NULL;
return codec;
}
codec->vtable = src_codec->vtable;
codec->attribs = src_codec->attribs;
codec->static_descriptor = src_codec->static_descriptor;
- codec->descriptor = src_codec->descriptor;
return 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;
+ if(codec->vtable->open) {
+ rv = codec->vtable->open(codec);
}
return rv;
}
MPF_SAMPLE_RATE_8000 = 0x01,
MPF_SAMPLE_RATE_16000 = 0x02,
MPF_SAMPLE_RATE_32000 = 0x04,
- MPF_SAMPLE_RATE_48000 = 0x08
+ MPF_SAMPLE_RATE_48000 = 0x08,
+
+ MPF_SAMPLE_RATE_SUPPORTED = MPF_SAMPLE_RATE_8000 | MPF_SAMPLE_RATE_16000 |
+ MPF_SAMPLE_RATE_32000 | MPF_SAMPLE_RATE_48000
} mpf_sample_rates_e;
/** Codec descriptor declaration */
typedef struct mpf_codec_descriptor_t mpf_codec_descriptor_t;
+/** Codec attributes declaration */
+typedef struct mpf_codec_attribs_t mpf_codec_attribs_t;
/** Codec list declaration */
typedef struct mpf_codec_list_t mpf_codec_list_t;
+/** Codec capabilities declaration */
+typedef struct mpf_codec_capabilities_t mpf_codec_capabilities_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 */
/** Channel count */
apr_byte_t channel_count;
/** Codec dependent additional format */
- const char *format;
+ apt_str_t format;
/** Enabled/disabled state */
apt_bool_t enabled;
};
/** List of codec descriptors */
struct mpf_codec_list_t {
- /** Dynamic array of mpf_codec_descriptor_t */
+ /** Dynamic array of codec descriptors (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;
+ /** Preffered primary (audio/video codec) descriptor from descriptor_arr */
+ mpf_codec_descriptor_t *primary_descriptor;
+ /** Preffered named event (telephone-event) descriptor from descriptor_arr */
+ mpf_codec_descriptor_t *event_descriptor;
};
/** Codec attributes */
/** Codec name */
apt_str_t name;
/** Bits per sample */
- apr_byte_t bits_per_samples;
+ apr_byte_t bits_per_sample;
/** Supported sampling rates (mpf_sample_rates_e) */
int sample_rates;
};
+/** List of codec attributes (capabilities) */
+struct mpf_codec_capabilities_t {
+ /** Dynamic array of codec attributes (mpf_codec_attrribs_t) */
+ apr_array_header_t *attrib_arr;
+ /** Allow/support named events */
+ apt_bool_t allow_named_events;
+};
+
+/** Codec frame */
+struct mpf_codec_frame_t {
+ /** Raw buffer, which may contain encoded or decoded data */
+ void *buffer;
+ /** Buffer size */
+ apr_size_t size;
+};
+
/** Initialize codec descriptor */
static APR_INLINE void mpf_codec_descriptor_init(mpf_codec_descriptor_t *descriptor)
apt_string_reset(&descriptor->name);
descriptor->sampling_rate = 0;
descriptor->channel_count = 0;
- descriptor->format = NULL;
+ apt_string_reset(&descriptor->format);
descriptor->enabled = TRUE;
}
+/** Initialize codec descriptor */
+static APR_INLINE mpf_codec_descriptor_t* mpf_codec_descriptor_create(apr_pool_t *pool)
+{
+ mpf_codec_descriptor_t *descriptor = (mpf_codec_descriptor_t*) apr_palloc(pool,sizeof(mpf_codec_descriptor_t));
+ mpf_codec_descriptor_init(descriptor);
+ return descriptor;
+}
+
/** 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 *
+ return descriptor->channel_count * attribs->bits_per_sample * CODEC_FRAME_TIME_BASE *
descriptor->sampling_rate / 1000 / 8; /* 1000 - msec per sec, 8 - bits per byte */
}
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;
+ codec_list->primary_descriptor = NULL;
+ codec_list->event_descriptor = 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));
+ codec_list->primary_descriptor = NULL;
+ codec_list->event_descriptor = NULL;
}
/** Copy list of codec descriptors */
/** 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_t *descriptor = (mpf_codec_descriptor_t*)apr_array_push(codec_list->descriptor_arr);
mpf_codec_descriptor_init(descriptor);
return descriptor;
}
}
/** 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)
+static APR_INLINE mpf_codec_descriptor_t* mpf_codec_list_descriptor_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;
+ return &APR_ARRAY_IDX(codec_list->descriptor_arr,id,mpf_codec_descriptor_t);
}
+/** Create linear PCM descriptor */
+MPF_DECLARE(mpf_codec_descriptor_t*) mpf_codec_lpcm_descriptor_create(apr_uint16_t sampling_rate, apr_byte_t channel_count, apr_pool_t *pool);
+
+/** Create codec descriptor by capabilities */
+MPF_DECLARE(mpf_codec_descriptor_t*) mpf_codec_descriptor_create_by_capabilities(const mpf_codec_capabilities_t *capabilities, const mpf_codec_descriptor_t *peer, apr_pool_t *pool);
+
/** 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);
+MPF_DECLARE(apt_bool_t) mpf_codec_descriptors_match(const mpf_codec_descriptor_t *descriptor1, const mpf_codec_descriptor_t *descriptor2);
+
+/** Match specified codec descriptor and the default lpcm one */
+MPF_DECLARE(apt_bool_t) mpf_codec_lpcm_descriptor_match(const mpf_codec_descriptor_t *descriptor);
+
+/** Match codec descriptor by attribs specified */
+MPF_DECLARE(apt_bool_t) mpf_codec_descriptor_match_by_attribs(mpf_codec_descriptor_t *descriptor, const mpf_codec_descriptor_t *static_descriptor, const mpf_codec_attribs_t *attribs);
+
+
+
+/** Initialize codec capabilities */
+static APR_INLINE void mpf_codec_capabilities_init(mpf_codec_capabilities_t *capabilities, apr_size_t initial_count, apr_pool_t *pool)
+{
+ capabilities->attrib_arr = apr_array_make(pool,(int)initial_count, sizeof(mpf_codec_attribs_t));
+ capabilities->allow_named_events = TRUE;
+}
+
+/** Clone codec capabilities */
+static APR_INLINE void mpf_codec_capabilities_clone(mpf_codec_capabilities_t *capabilities, const mpf_codec_capabilities_t *src_capabilities, apr_pool_t *pool)
+{
+ capabilities->attrib_arr = apr_array_copy(pool,src_capabilities->attrib_arr);
+ capabilities->allow_named_events = src_capabilities->allow_named_events;
+}
+
+/** Merge codec capabilities */
+static APR_INLINE apt_bool_t mpf_codec_capabilities_merge(mpf_codec_capabilities_t *capabilities, const mpf_codec_capabilities_t *src_capabilities, apr_pool_t *pool)
+{
+ if(capabilities->allow_named_events == FALSE && src_capabilities->allow_named_events == TRUE) {
+ capabilities->allow_named_events = src_capabilities->allow_named_events;
+ }
+ capabilities->attrib_arr = apr_array_append(pool,capabilities->attrib_arr,src_capabilities->attrib_arr);
+ return TRUE;
+}
+
+/** Add codec capabilities */
+static APR_INLINE apt_bool_t mpf_codec_capabilities_add(mpf_codec_capabilities_t *capabilities, int sample_rates, const char *codec_name)
+{
+ mpf_codec_attribs_t *attribs = (mpf_codec_attribs_t*)apr_array_push(capabilities->attrib_arr);
+ apt_string_set(&attribs->name,codec_name);
+ attribs->sample_rates = sample_rates;
+ attribs->bits_per_sample = 0;
+ return TRUE;
+}
+
+/** Add default (liear PCM) capabilities */
+MPF_DECLARE(apt_bool_t) mpf_codec_default_capabilities_add(mpf_codec_capabilities_t *capabilities);
+
+/** Validate codec capabilities */
+static APR_INLINE apt_bool_t mpf_codec_capabilities_validate(mpf_codec_capabilities_t *capabilities)
+{
+ if(apr_is_empty_array(capabilities->attrib_arr) == TRUE) {
+ mpf_codec_default_capabilities_add(capabilities);
+ }
+ return TRUE;
+}
+
+
+
+/** Find matched descriptor in codec list */
+MPF_DECLARE(mpf_codec_descriptor_t*) mpf_codec_list_descriptor_find(const mpf_codec_list_t *codec_list, const mpf_codec_descriptor_t *descriptor);
+
+/** Modify codec list according to capabilities specified */
+MPF_DECLARE(apt_bool_t) mpf_codec_list_modify(mpf_codec_list_t *codec_list, const mpf_codec_capabilities_t *capabilities);
+
/** 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);
+MPF_DECLARE(apt_bool_t) mpf_codec_lists_intersect(mpf_codec_list_t *codec_list1, mpf_codec_list_t *codec_list2);
+
+
+/** Get sampling rate mask (mpf_sample_rate_e) by integer value */
+MPF_DECLARE(int) mpf_sample_rate_mask_get(apr_uint16_t sampling_rate);
APT_END_EXTERN_C
/** 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) default codec */
-MPF_DECLARE(mpf_codec_t*) mpf_codec_manager_default_codec_get(const mpf_codec_manager_t *codec_manager, 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);
* @brief MPF Context
*/
-#include "mpf_object.h"
-#include "apt_obj_list.h"
+#include "mpf_types.h"
APT_BEGIN_EXTERN_C
-/** Definition of table item used in context */
-typedef void* table_item_t;
+/** Opaque factory of media contexts */
+typedef struct mpf_context_factory_t mpf_context_factory_t;
+
+/**
+ * Create factory of media contexts.
+ */
+MPF_DECLARE(mpf_context_factory_t*) mpf_context_factory_create(apr_pool_t *pool);
+
+/**
+ * Destroy factory of media contexts.
+ */
+MPF_DECLARE(void) mpf_context_factory_destroy(mpf_context_factory_t *factory);
-/** 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;
+/**
+ * Process factory of media contexts.
+ */
+MPF_DECLARE(apt_bool_t) mpf_context_factory_process(mpf_context_factory_t *factory);
- /** 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;
-};
+/**
+ * Create MPF context.
+ * @param factory the factory context belongs to
+ * @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(
+ mpf_context_factory_t *factory,
+ 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);
/**
* Add termination to context.
*/
MPF_DECLARE(apt_bool_t) mpf_context_termination_subtract(mpf_context_t *context, mpf_termination_t *termination);
+/**
+ * Add association between specified terminations.
+ * @param context the context to add association in the scope of
+ * @param termination1 the first termination to associate
+ * @param termination2 the second termination to associate
+ */
+MPF_DECLARE(apt_bool_t) mpf_context_association_add(mpf_context_t *context, mpf_termination_t *termination1, mpf_termination_t *termination2);
+
+/**
+ * Remove association between specified terminations.
+ * @param context the context to remove association in the scope of
+ * @param termination1 the first termination
+ * @param termination2 the second termination
+ */
+MPF_DECLARE(apt_bool_t) mpf_context_association_remove(mpf_context_t *context, mpf_termination_t *termination1, mpf_termination_t *termination2);
+
+/**
+ * Reset assigned associations and destroy applied topology.
+ * @param context the context to reset associations for
+ */
+MPF_DECLARE(apt_bool_t) mpf_context_associations_reset(mpf_context_t *context);
+
/**
* Apply topology.
- * @param context the context which holds the termination
- * @param termination the termination to apply toplogy for
+ * @param context the context to apply topology for
*/
-MPF_DECLARE(apt_bool_t) mpf_context_topology_apply(mpf_context_t *context, mpf_termination_t *termination);
+MPF_DECLARE(apt_bool_t) mpf_context_topology_apply(mpf_context_t *context);
/**
* Destroy topology.
- * @param context the context which holds the termination
- * @param termination the termination to destroy toplogy for
+ * @param context the context to destroy topology for
*/
-MPF_DECLARE(apt_bool_t) mpf_context_topology_destroy(mpf_context_t *context, mpf_termination_t *termination);
+MPF_DECLARE(apt_bool_t) mpf_context_topology_destroy(mpf_context_t *context);
/**
* Process context.
- * @param context the context
+ * @param context the context to process
*/
MPF_DECLARE(apt_bool_t) mpf_context_process(mpf_context_t *context);
/**
* Create audio stream decoder.
* @param source the source to get encoded stream from
+ * @param codec the codec to use for decode
* @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);
+MPF_DECLARE(mpf_audio_stream_t*) mpf_decoder_create(mpf_audio_stream_t *source, mpf_codec_t *codec, apr_pool_t *pool);
APT_END_EXTERN_C
--- /dev/null
+/*
+ * Copyright 2009 Tomas Valenta, 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_DTMF_DETECTOR_H__
+#define __MPF_DTMF_DETECTOR_H__
+
+/*
+ * @file mpf_dtmf_detector.h
+ * @brief DTMF detector
+ *
+ * Detector of DTMF tones sent both out-of-band (RFC4733) and in-band (audio).
+ */
+
+#include "apr.h"
+#include "apr_pools.h"
+#include "apt.h"
+#include "mpf_frame.h"
+#include "mpf_stream.h"
+
+APT_BEGIN_EXTERN_C
+
+/** DTMF detector band */
+typedef enum mpf_dtmf_detector_band_e {
+ /** Detect tones in-band */
+ MPF_DTMF_DETECTOR_INBAND = 0x1,
+ /** Detect named events out-of-band */
+ MPF_DTMF_DETECTOR_OUTBAND = 0x2,
+ /** Detect both in-band and out-of-band digits */
+ MPF_DTMF_DETECTOR_BOTH = MPF_DTMF_DETECTOR_INBAND | MPF_DTMF_DETECTOR_OUTBAND
+} mpf_dtmf_detector_band_e;
+
+/** Opaque MPF DTMF detector structure definition */
+typedef struct mpf_dtmf_detector_t mpf_dtmf_detector_t;
+
+
+/**
+ * Create MPF DTMF detector (advanced).
+ * @param stream A stream to get digits from.
+ * @param band One of:
+ * - MPF_DTMF_DETECTOR_INBAND: detect audible tones only
+ * - MPF_DTMF_DETECTOR_OUTBAND: detect out-of-band named-events only
+ * - MPF_DTMF_DETECTOR_BOTH: detect digits in both bands if supported by
+ * stream. When out-of-band digit arrives, in-band detection is turned off.
+ * @param pool Memory pool to allocate DTMF detector from.
+ * @return The object or NULL on error.
+ * @see mpf_dtmf_detector_create
+ */
+MPF_DECLARE(struct mpf_dtmf_detector_t *) mpf_dtmf_detector_create_ex(
+ const struct mpf_audio_stream_t *stream,
+ enum mpf_dtmf_detector_band_e band,
+ struct apr_pool_t *pool);
+
+/**
+ * Create MPF DTMF detector (simple). Calls mpf_dtmf_detector_create_ex
+ * with band = MPF_DTMF_DETECTOR_BOTH if out-of-band supported by the stream,
+ * MPF_DTMF_DETECTOR_INBAND otherwise.
+ * @param stream A stream to get digits from.
+ * @param pool Memory pool to allocate DTMF detector from.
+ * @return The object or NULL on error.
+ * @see mpf_dtmf_detector_create_ex
+ */
+static APR_INLINE struct mpf_dtmf_detector_t *mpf_dtmf_detector_create(
+ const struct mpf_audio_stream_t *stream,
+ struct apr_pool_t *pool)
+{
+ return mpf_dtmf_detector_create_ex(stream,
+ stream->tx_event_descriptor ? MPF_DTMF_DETECTOR_BOTH : MPF_DTMF_DETECTOR_INBAND,
+ pool);
+}
+
+/**
+ * Get DTMF digit from buffer of digits detected so far and remove it.
+ * @param detector The detector.
+ * @return DTMF character [0-9*#A-D] or NUL if the buffer is empty.
+ */
+MPF_DECLARE(char) mpf_dtmf_detector_digit_get(struct mpf_dtmf_detector_t *detector);
+
+/**
+ * Retrieve how many digits was lost due to full buffer.
+ * @param detector The detector.
+ * @return Number of lost digits.
+ */
+MPF_DECLARE(apr_size_t) mpf_dtmf_detector_digits_lost(const struct mpf_dtmf_detector_t *detector);
+
+/**
+ * Empty the buffer and reset detection states.
+ * @param detector The detector.
+ */
+MPF_DECLARE(void) mpf_dtmf_detector_reset(struct mpf_dtmf_detector_t *detector);
+
+/**
+ * Detect DTMF digits in the frame.
+ * @param detector The detector.
+ * @param frame Frame object passed in stream_write().
+ */
+MPF_DECLARE(void) mpf_dtmf_detector_get_frame(
+ struct mpf_dtmf_detector_t *detector,
+ const struct mpf_frame_t *frame);
+
+/**
+ * Free all resources associated with the detector.
+ * @param detector The detector.
+ */
+MPF_DECLARE(void) mpf_dtmf_detector_destroy(struct mpf_dtmf_detector_t *detector);
+
+APT_END_EXTERN_C
+
+#endif /*__MPF_DTMF_DETECTOR_H__*/
--- /dev/null
+/*
+ * Copyright 2009 Tomas Valenta, 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_DTMF_GENERATOR_H__
+#define __MPF_DTMF_GENERATOR_H__
+
+/*
+ * @file mpf_named_event.h
+ * @brief DTMF generator
+ *
+ * Generator used to send DTMF tones. Capable to send digits
+ * either in-band as audible tones or out-of-band according
+ * to RFC4733.
+ */
+
+#include "apr_pools.h"
+#include "apt.h"
+#include "mpf_frame.h"
+#include "mpf_stream.h"
+
+APT_BEGIN_EXTERN_C
+
+/** DTMF generator band */
+typedef enum mpf_dtmf_generator_band_e {
+ /** Generate tones in-band */
+ MPF_DTMF_GENERATOR_INBAND = 0x1,
+ /** Generate named events out-of-band */
+ MPF_DTMF_GENERATOR_OUTBAND = 0x2,
+ /** Generate both tones and named events */
+ MPF_DTMF_GENERATOR_BOTH = MPF_DTMF_GENERATOR_INBAND | MPF_DTMF_GENERATOR_OUTBAND
+} mpf_dtmf_generator_band_e;
+
+/** Opaque MPF DTMF generator structure definition */
+typedef struct mpf_dtmf_generator_t mpf_dtmf_generator_t;
+
+
+/**
+ * Create MPF DTMF generator (advanced).
+ * @param stream A stream to transport digits via.
+ * @param band MPF_DTMF_GENERATOR_INBAND or MPF_DTMF_GENERATOR_OUTBAND
+ * @param tone_ms Tone duration in milliseconds.
+ * @param silence_ms Inter-digit silence in milliseconds.
+ * @param pool Memory pool to allocate DTMF generator from.
+ * @return The object or NULL on error.
+ * @see mpf_dtmf_generator_create
+ */
+MPF_DECLARE(struct mpf_dtmf_generator_t *) mpf_dtmf_generator_create_ex(
+ const struct mpf_audio_stream_t *stream,
+ enum mpf_dtmf_generator_band_e band,
+ apr_size_t tone_ms,
+ apr_size_t silence_ms,
+ struct apr_pool_t *pool);
+
+/**
+ * Create MPF DTMF generator (simple). Calls mpf_dtmf_generator_create_ex
+ * with band = MPF_DTMF_GENERATOR_OUTBAND if supported by the stream or
+ * MPF_DTMF_GENERATOR_INBAND otherwise, tone_ms = 70, silence_ms = 50.
+ * @param stream A stream to transport digits via.
+ * @param pool Memory pool to allocate DTMF generator from.
+ * @return The object or NULL on error.
+ * @see mpf_dtmf_generator_create_ex
+ */
+static APR_INLINE struct mpf_dtmf_generator_t *mpf_dtmf_generator_create(
+ const struct mpf_audio_stream_t *stream,
+ struct apr_pool_t *pool)
+{
+ return mpf_dtmf_generator_create_ex(stream,
+ stream->rx_event_descriptor ? MPF_DTMF_GENERATOR_OUTBAND : MPF_DTMF_GENERATOR_INBAND,
+ 70, 50, pool);
+}
+
+/**
+ * Add DTMF digits to the queue.
+ * @param generator The generator.
+ * @param digits DTMF character sequence [0-9*#A-D].
+ * @return TRUE if ok, FALSE if there are too many digits.
+ */
+MPF_DECLARE(apt_bool_t) mpf_dtmf_generator_enqueue(
+ struct mpf_dtmf_generator_t *generator,
+ const char *digits);
+
+/**
+ * Empty the queue and immediately stop generating.
+ * @param generator The generator.
+ */
+MPF_DECLARE(void) mpf_dtmf_generator_reset(struct mpf_dtmf_generator_t *generator);
+
+/**
+ * Check state of the generator.
+ * @param generator The generator.
+ * @return TRUE if generating a digit or there are digits waiting in queue.
+ * FALSE if the queue is empty or generating silence after the last digit.
+ */
+MPF_DECLARE(apt_bool_t) mpf_dtmf_generator_sending(const struct mpf_dtmf_generator_t *generator);
+
+/**
+ * Put frame into the stream.
+ * @param generator The generator.
+ * @param frame Frame object passed in stream_read().
+ * @return TRUE if frame with tone (both in-band and out-of-band) was generated,
+ * FALSE otherwise. In contrast to mpf_dtmf_generator_sending, returns FALSE even
+ * if generating inter-digit silence. In other words returns TRUE iff the frame
+ * object was filled with data. This method MUST be called for each frame for
+ * proper timing.
+ */
+MPF_DECLARE(apt_bool_t) mpf_dtmf_generator_put_frame(
+ struct mpf_dtmf_generator_t *generator,
+ struct mpf_frame_t *frame);
+
+/**
+ * Free all resources associated with the generator.
+ * @param generator The generator.
+ */
+MPF_DECLARE(void) mpf_dtmf_generator_destroy(struct mpf_dtmf_generator_t *generator);
+
+APT_END_EXTERN_C
+
+#endif /*__MPF_DTMF_GENERATOR_H__*/
/**
* Create audio stream encoder.
* @param sink the sink to write encoded stream to
+ * @param codec the codec to use for encode
* @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);
+MPF_DECLARE(mpf_audio_stream_t*) mpf_encoder_create(mpf_audio_stream_t *sink, mpf_codec_t *codec, apr_pool_t *pool);
APT_END_EXTERN_C
APT_BEGIN_EXTERN_C
+/** MPF task message definition */
+typedef apt_task_msg_t mpf_task_msg_t;
+
/**
* Create MPF engine.
+ * @param rate the rate (n times faster than real-time)
* @param pool the pool to allocate memory from
*/
-MPF_DECLARE(mpf_engine_t*) mpf_engine_create(apr_pool_t *pool);
+MPF_DECLARE(mpf_engine_t*) mpf_engine_create(unsigned long rate, apr_pool_t *pool);
/**
* Create MPF codec manager.
*/
MPF_DECLARE(apt_bool_t) mpf_engine_codec_manager_register(mpf_engine_t *engine, const mpf_codec_manager_t *codec_manager);
+/**
+ * Create MPF context.
+ * @param engine the engine to create context for
+ * @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_engine_context_create(
+ mpf_engine_t *engine,
+ 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_engine_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_engine_context_object_get(mpf_context_t *context);
+
/**
* Get task.
* @param engine the engine to get task from
*/
MPF_DECLARE(void) mpf_engine_task_msg_type_set(mpf_engine_t *engine, apt_task_msg_type_e type);
+/**
+ * Create task message(if not created) and add MPF termination message to it.
+ * @param engine the engine task message belongs to
+ * @param command_id the MPF command identifier
+ * @param context the context to add termination to
+ * @param termination the termination to add
+ * @param descriptor the termination dependent descriptor
+ * @param task_msg the task message to create and add constructed MPF message to
+ */
+MPF_DECLARE(apt_bool_t) mpf_engine_termination_message_add(
+ mpf_engine_t *engine,
+ mpf_command_type_e command_id,
+ mpf_context_t *context,
+ mpf_termination_t *termination,
+ void *descriptor,
+ mpf_task_msg_t **task_msg);
+
+/**
+ * Create task message(if not created) and add MPF association message to it.
+ * @param engine the engine task message belongs to
+ * @param command_id the MPF command identifier
+ * @param context the context to add association of terminations for
+ * @param termination the termination to associate
+ * @param assoc_termination the termination to associate
+ * @param task_msg the task message to create and add constructed MPF message to
+ */
+MPF_DECLARE(apt_bool_t) mpf_engine_assoc_message_add(
+ mpf_engine_t *engine,
+ mpf_command_type_e command_id,
+ mpf_context_t *context,
+ mpf_termination_t *termination,
+ mpf_termination_t *assoc_termination,
+ mpf_task_msg_t **task_msg);
+
+/**
+ * Create task message(if not created) and add MPF topology message to it.
+ * @param engine the engine task message belongs to
+ * @param command_id the MPF command identifier
+ * @param context the context to modify topology for
+ * @param task_msg the task message to create and add constructed MPF message to
+ */
+MPF_DECLARE(apt_bool_t) mpf_engine_topology_message_add(
+ mpf_engine_t *engine,
+ mpf_command_type_e command_id,
+ mpf_context_t *context,
+ mpf_task_msg_t **task_msg);
+
+/**
+ * Send MPF task message.
+ * @param engine the engine to send task message to
+ * @param task_msg the task message to send
+ */
+MPF_DECLARE(apt_bool_t) mpf_engine_message_send(mpf_engine_t *engine, mpf_task_msg_t **task_msg);
+
APT_END_EXTERN_C
* @brief MPF File Termination Factory
*/
-#include "mpf_types.h"
+#include "mpf_termination_factory.h"
APT_BEGIN_EXTERN_C
*/
#include "mpf_codec_descriptor.h"
+#include "mpf_named_event.h"
APT_BEGIN_EXTERN_C
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) */
+ MEDIA_FRAME_TYPE_EVENT = 0x4 /**< named event frame (RFC4733/RFC2833) */
} mpf_frame_type_e;
-/** Named event declaration */
-typedef struct mpf_named_event_frame_t mpf_named_event_frame_t;
+/** Media frame marker */
+typedef enum {
+ MPF_MARKER_NONE, /**< none */
+ MPF_MARKER_START_OF_EVENT, /**< start of event */
+ MPF_MARKER_END_OF_EVENT, /**< end of event */
+ MPF_MARKER_NEW_SEGMENT, /**< start of new segment (long-lasting events) */
+} mpf_frame_marker_e;
+
/** 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;
+ /** frame marker (start-of-event,end-of-event) mpf_frame_marker_e */
+ int marker;
/** codec frame */
mpf_codec_frame_t codec_frame;
/** named-event frame */
/** 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);
+mpf_jitter_buffer_t* mpf_jitter_buffer_create(mpf_jb_config_t *jb_config, mpf_codec_descriptor_t *descriptor, mpf_codec_t *codec, apr_pool_t *pool);
/** Destroy jitter buffer */
void mpf_jitter_buffer_destroy(mpf_jitter_buffer_t *jb);
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);
+jb_result_t mpf_jitter_buffer_write(mpf_jitter_buffer_t *jb, 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);
+jb_result_t mpf_jitter_buffer_event_write(mpf_jitter_buffer_t *jb, const mpf_named_event_frame_t *named_event, apr_uint32_t ts, apr_byte_t marker);
/** Read media frame from jitter buffer */
apt_bool_t mpf_jitter_buffer_read(mpf_jitter_buffer_t *jb, mpf_frame_t *media_frame);
+++ /dev/null
-/*
- * 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 <apr_network_io.h>
-#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__*/
APT_BEGIN_EXTERN_C
+/** Max number of messages grouped in a container */
+#define MAX_MPF_MESSAGE_COUNT 5
+
/** Enumeration of MPF message types */
typedef enum {
MPF_MESSAGE_TYPE_REQUEST, /**< request message */
} mpf_status_code_e;
-/** Enumeration of commands */
+/** Enumeration of MPF 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_ADD_TERMINATION, /**< add termination to context */
+ MPF_MODIFY_TERMINATION, /**< modify termination properties */
+ MPF_SUBTRACT_TERMINATION,/**< subtract termination from context */
+ MPF_ADD_ASSOCIATION, /**< add association between terminations */
+ MPF_REMOVE_ASSOCIATION, /**< remove association between terminations */
+ MPF_RESET_ASSOCIATIONS, /**< reset associations among terminations (also destroy topology) */
+ MPF_APPLY_TOPOLOGY, /**< apply topology based on assigned associations */
+ MPF_DESTROY_TOPOLOGY /**< destroy applied topology */
} mpf_command_type_e;
/** MPF message declaration */
typedef struct mpf_message_t mpf_message_t;
+/** MPF message container declaration */
+typedef struct mpf_message_container_t mpf_message_container_t;
/** MPF message definition */
struct mpf_message_t {
mpf_context_t *context;
/** Termination */
mpf_termination_t *termination;
+ /** Associated termination */
+ mpf_termination_t *assoc_termination;
/** Termination type dependent descriptor */
void *descriptor;
};
+/** MPF message container definition */
+struct mpf_message_container_t {
+ /** Number of actual messages */
+ apr_size_t count;
+ /** Array of messages */
+ mpf_message_t messages[MAX_MPF_MESSAGE_COUNT];
+};
+
APT_END_EXTERN_C
#endif /*__MPF_MESSAGE_H__*/
--- /dev/null
+/*
+ * 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_MIXER_H__
+#define __MPF_MIXER_H__
+
+/**
+ * @file mpf_mixer.h
+ * @brief MPF Stream Mixer (n-sources, 1-sink)
+ */
+
+#include "mpf_object.h"
+
+APT_BEGIN_EXTERN_C
+
+/**
+ * Create audio stream mixer.
+ * @param source_arr the array of audio sources
+ * @param source_count the number of audio sources
+ * @param sink the audio sink
+ * @param codec_manager the codec manager
+ * @param pool the pool to allocate memory from
+ */
+MPF_DECLARE(mpf_object_t*) mpf_mixer_create(
+ mpf_audio_stream_t **source_arr,
+ apr_size_t source_count,
+ mpf_audio_stream_t *sink,
+ const mpf_codec_manager_t *codec_manager,
+ apr_pool_t *pool);
+
+
+APT_END_EXTERN_C
+
+#endif /*__MPF_MIXER_H__*/
--- /dev/null
+/*
+ * 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_MULTIPLIER_H__
+#define __MPF_MULTIPLIER_H__
+
+/**
+ * @file mpf_multiplier.h
+ * @brief MPF Stream Multiplier (1-source, n-sinks)
+ */
+
+#include "mpf_object.h"
+
+APT_BEGIN_EXTERN_C
+
+/**
+ * Create audio stream multiplier.
+ * @param source the audio source
+ * @param sink_arr the array of audio sinks
+ * @param sink_count the number of audio sinks
+ * @param codec_manager the codec manager
+ * @param pool the pool to allocate memory from
+ */
+MPF_DECLARE(mpf_object_t*) mpf_multiplier_create(
+ mpf_audio_stream_t *source,
+ mpf_audio_stream_t **sink_arr,
+ apr_size_t sink_count,
+ const mpf_codec_manager_t *codec_manager,
+ apr_pool_t *pool);
+
+
+APT_END_EXTERN_C
+
+#endif /*__MPF_MULTIPLIER_H__*/
--- /dev/null
+/*
+ * 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_NAMED_EVENT_H__
+#define __MPF_NAMED_EVENT_H__
+
+/**
+ * @file mpf_named_event.h
+ * @brief MPF Named Events (RFC4733/RFC2833)
+ */
+
+#include "mpf_codec_descriptor.h"
+
+APT_BEGIN_EXTERN_C
+
+/** Named event declaration */
+typedef struct mpf_named_event_frame_t mpf_named_event_frame_t;
+
+
+/** Named event (RFC4733/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;
+};
+
+/** Create named event descriptor */
+MPF_DECLARE(mpf_codec_descriptor_t*) mpf_event_descriptor_create(apr_uint16_t sampling_rate, apr_pool_t *pool);
+
+/** Check whether the specified descriptor is named event one */
+MPF_DECLARE(apt_bool_t) mpf_event_descriptor_check(const mpf_codec_descriptor_t *descriptor);
+
+/** Convert DTMF character to event identifier */
+MPF_DECLARE(apr_uint32_t) mpf_dtmf_char_to_event_id(const char dtmf_char);
+
+/** Convert event identifier to DTMF character */
+MPF_DECLARE(char) mpf_event_id_to_dtmf_char(const apr_uint32_t event_id);
+
+
+APT_END_EXTERN_C
+
+#endif /*__MPF_NAMED_EVENT_H__*/
*/
#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 */
+/** Media processing objects base */
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);
+ /** Virtual process */
+ apt_bool_t (*process)(mpf_object_t *object);
+ /** Virtual trace of media path */
+ void (*trace)(mpf_object_t *object);
};
+/** Initialize object */
+static APR_INLINE void mpf_object_init(mpf_object_t *object)
+{
+ object->destroy = NULL;
+ object->process = NULL;
+ object->trace = NULL;
+}
+
+/** Destroy object */
+static APR_INLINE void mpf_object_destroy(mpf_object_t *object)
+{
+ if(object->destroy)
+ object->destroy(object);
+}
+
+/** Process object */
+static APR_INLINE void mpf_object_process(mpf_object_t *object)
+{
+ if(object->process)
+ object->process(object);
+}
+
+/** Trace media path */
+static APR_INLINE void mpf_object_trace(mpf_object_t *object)
+{
+ if(object->trace)
+ object->trace(object);
+}
+
APT_END_EXTERN_C
* limitations under the License.
*/
-#ifndef __MPF_TIMER_H__
-#define __MPF_TIMER_H__
+#ifndef __MPF_RESAMPLER_H__
+#define __MPF_RESAMPLER_H__
/**
- * @file mpf_timer.h
- * @brief MPF High Resolution Timer
+ * @file mpf_resampler.h
+ * @brief MPF Stream Resampler
*/
-#include "mpf.h"
+#include "mpf_stream.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);
+/**
+ * Create audio stream resampler.
+ * @param source the source stream to resample
+ * @param sink the sink stream to resample to
+ * @param pool the pool to allocate memory from
+ */
+MPF_DECLARE(mpf_audio_stream_t*) mpf_resampler_create(mpf_audio_stream_t *source, mpf_audio_stream_t *sink, apr_pool_t *pool);
APT_END_EXTERN_C
-#endif /*__MPF_TIMER_H__*/
+#endif /*__MPF_RESAMPLER_H__*/
--- /dev/null
+/*
+ * 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_RTCP_PACKET_H__
+#define __MPF_RTCP_PACKET_H__
+
+/**
+ * @file mpf_rtcp_packet.h
+ * @brief RTCP Packet Definition
+ */
+
+#include "mpf_rtp_stat.h"
+
+APT_BEGIN_EXTERN_C
+
+
+/** RTCP payload (packet) types */
+typedef enum {
+ RTCP_SR = 200,
+ RTCP_RR = 201,
+ RTCP_SDES = 202,
+ RTCP_BYE = 203,
+ RTCP_APP = 204
+} rtcp_type_e;
+
+/** RTCP SDES types */
+typedef enum {
+ RTCP_SDES_END = 0,
+ RTCP_SDES_CNAME = 1,
+ RTCP_SDES_NAME = 2,
+ RTCP_SDES_EMAIL = 3,
+ RTCP_SDES_PHONE = 4,
+ RTCP_SDES_LOC = 5,
+ RTCP_SDES_TOOL = 6,
+ RTCP_SDES_NOTE = 7,
+ RTCP_SDES_PRIV = 8
+} rtcp_sdes_type_e;
+
+/** RTCP header declaration */
+typedef struct rtcp_header_t rtcp_header_t;
+/** RTCP packet declaration */
+typedef struct rtcp_packet_t rtcp_packet_t;
+/** SDES item declaration*/
+typedef struct rtcp_sdes_item_t rtcp_sdes_item_t;
+
+
+/** RTCP header */
+struct rtcp_header_t {
+#if (APR_IS_BIGENDIAN == 1)
+ /** protocol version */
+ unsigned int version: 2;
+ /** padding flag */
+ unsigned int padding: 1;
+ /** varies by packet type */
+ unsigned int count: 5;
+ /** packet type */
+ unsigned int pt: 8;
+#else
+ /** varies by packet type */
+ unsigned int count: 5;
+ /** padding flag */
+ unsigned int padding: 1;
+ /** protocol version */
+ unsigned int version: 2;
+ /** packet type */
+ unsigned int pt: 8;
+#endif
+
+ /** packet length in words, w/o this word */
+ unsigned int length: 16;
+};
+
+/** SDES item */
+struct rtcp_sdes_item_t {
+ /** type of item (rtcp_sdes_type_t) */
+ apr_byte_t type;
+ /* length of item (in octets) */
+ apr_byte_t length;
+ /* text, not null-terminated */
+ char data[1];
+};
+
+/** RTCP packet */
+struct rtcp_packet_t {
+ /** common header */
+ rtcp_header_t header;
+ union {
+ /** sender report (SR) */
+ struct {
+ /** sr stat */
+ rtcp_sr_stat_t sr_stat;
+ /** variable-length list rr stats */
+ rtcp_rr_stat_t rr_stat[1];
+ } sr;
+
+ /** reception report (RR) */
+ struct {
+ /** receiver generating this report */
+ apr_uint32_t ssrc;
+ /** variable-length list rr stats */
+ rtcp_rr_stat_t rr_stat[1];
+ } rr;
+
+ /** source description (SDES) */
+ struct {
+ /** first SSRC/CSRC */
+ apr_uint32_t ssrc;
+ /** list of SDES items */
+ rtcp_sdes_item_t item[1];
+ } sdes;
+
+ /** BYE */
+ struct {
+ /** list of sources */
+ apr_uint32_t ssrc[1];
+ /* optional length of reason string (in octets) */
+ apr_byte_t length;
+ /* optional reason string, not null-terminated */
+ char data[1];
+ } bye;
+ } r;
+};
+
+/** Initialize RTCP header */
+static APR_INLINE void rtcp_header_init(rtcp_header_t *header, rtcp_type_e pt)
+{
+ header->version = RTP_VERSION;
+ header->padding = 0;
+ header->count = 0;
+ header->pt = pt;
+ header->length = 0;
+}
+
+static APR_INLINE void rtcp_header_length_set(rtcp_header_t *header, apr_size_t length)
+{
+ header->length = htons((apr_uint16_t)length / 4 - 1);
+}
+
+static APR_INLINE void rtcp_sr_hton(rtcp_sr_stat_t *sr_stat)
+{
+ sr_stat->ssrc = htonl(sr_stat->ssrc);
+ sr_stat->ntp_sec = htonl(sr_stat->ntp_sec);
+ sr_stat->ntp_frac = htonl(sr_stat->ntp_frac);
+ sr_stat->rtp_ts = htonl(sr_stat->rtp_ts);
+ sr_stat->sent_packets = htonl(sr_stat->sent_packets);
+ sr_stat->sent_octets = htonl(sr_stat->sent_octets);
+}
+
+static APR_INLINE void rtcp_sr_ntoh(rtcp_sr_stat_t *sr_stat)
+{
+ sr_stat->ssrc = ntohl(sr_stat->ssrc);
+ sr_stat->ntp_sec = ntohl(sr_stat->ntp_sec);
+ sr_stat->ntp_frac = ntohl(sr_stat->ntp_frac);
+ sr_stat->rtp_ts = ntohl(sr_stat->rtp_ts);
+ sr_stat->sent_packets = ntohl(sr_stat->sent_packets);
+ sr_stat->sent_octets = ntohl(sr_stat->sent_octets);
+}
+
+static APR_INLINE void rtcp_rr_hton(rtcp_rr_stat_t *rr_stat)
+{
+ rr_stat->ssrc = htonl(rr_stat->ssrc);
+ rr_stat->last_seq = htonl(rr_stat->last_seq);
+ rr_stat->jitter = htonl(rr_stat->jitter);
+
+#if (APR_IS_BIGENDIAN == 0)
+ rr_stat->lost = ((rr_stat->lost >> 16) & 0x000000ff) |
+ (rr_stat->lost & 0x0000ff00) |
+ ((rr_stat->lost << 16) & 0x00ff0000);
+#endif
+}
+
+static APR_INLINE void rtcp_rr_ntoh(rtcp_rr_stat_t *rr_stat)
+{
+ rr_stat->ssrc = ntohl(rr_stat->ssrc);
+ rr_stat->last_seq = ntohl(rr_stat->last_seq);
+ rr_stat->jitter = ntohl(rr_stat->jitter);
+
+#if (APR_IS_BIGENDIAN == 0)
+ rr_stat->lost = ((rr_stat->lost >> 16) & 0x000000ff) |
+ (rr_stat->lost & 0x0000ff00) |
+ ((rr_stat->lost << 16) & 0x00ff0000);
+#endif
+}
+
+APT_END_EXTERN_C
+
+#endif /*__MPF_RTCP_PACKET_H__*/
* @brief RTP Attributes (SDP)
*/
-#include "mpf_media_descriptor.h"
-#include "mpf_stream_mode.h"
+#include "mpf_rtp_descriptor.h"
APT_BEGIN_EXTERN_C
/** 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);
+/** Get string by RTP direction (send/receive) */
+MPF_DECLARE(const apt_str_t*) mpf_rtp_direction_str_get(mpf_stream_direction_e direction);
APT_END_EXTERN_C
/** Deviation threshold is used to trigger drift in timestamps */
#define DEVIATION_THRESHOLD 4000
-/** RTP receive history declaration */
+/** RTP receiver history declaration */
typedef struct rtp_rx_history_t rtp_rx_history_t;
-/** RTP receive periodic history declaration */
+/** RTP receiver 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 */
+/** History of RTP receiver */
struct rtp_rx_history_t {
- /** Updated on every seq num wrap around*/
- apr_uint32_t seq_cycles;
+ /** Updated on every seq num wrap around */
+ apr_uint32_t seq_cycles;
/** First seq num received */
- apr_uint16_t seq_num_base;
+ apr_uint16_t seq_num_base;
/** Max seq num received */
- apr_uint16_t seq_num_max;
+ apr_uint16_t seq_num_max;
/** Last timestamp received */
- apr_uint32_t ts_last;
+ apr_uint32_t ts_last;
/** Local time measured on last packet received */
- apr_time_t time_last;
+ apr_time_t time_last;
/** New ssrc, which is in probation */
- apr_uint32_t ssrc_new;
+ apr_uint32_t ssrc_new;
/** Period of ssrc probation */
- apr_byte_t ssrc_probation;
+ apr_byte_t ssrc_probation;
};
-/** Periodic history of RTP receive (initialized after every N packets) */
+/** Periodic history of RTP receiver (initialized after every N packets) */
struct rtp_rx_periodic_history_t {
/** Number of packets received */
- apr_uint32_t received_prior;
+ apr_uint32_t received_prior;
+ /** Number of packets expected */
+ apr_uint32_t expected_prior;
/** Number of packets discarded */
- apr_uint32_t discarded_prior;
+ apr_uint32_t discarded_prior;
/** Min jitter */
- apr_uint32_t jitter_min;
+ apr_uint32_t jitter_min;
/** Max jitter */
- apr_uint32_t jitter_max;
+ apr_uint32_t jitter_max;
};
-/** Reset RTP receive history */
+/** Reset RTP receiver 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 */
+/** Reset RTP receiver 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 */
+ /** RTCP statistics used in RR */
+ rtcp_rr_stat_t rr_stat;
+ /** RTP receiver statistics */
rtp_rx_stat_t stat;
/** RTP history */
rtp_rx_history_t 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;
apr_uint16_t last_seq_num;
/** Current timestamp (samples processed) */
apr_uint32_t timestamp;
+ /** Event timestamp base */
+ apr_uint32_t timestamp_base;
/** 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;
+ /** RTCP statistics used in SR */
+ rtcp_sr_stat_t sr_stat;
};
/** Initialize RTP receiver */
static APR_INLINE void rtp_receiver_init(rtp_receiver_t *receiver)
{
- receiver->event_pt = 0;
-
receiver->jb = NULL;
+ mpf_rtcp_rr_stat_reset(&receiver->rr_stat);
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->inactivity = 0;
transmitter->last_seq_num = 0;
transmitter->timestamp = 0;
+ transmitter->timestamp_base = 0;
transmitter->packet_data = NULL;
transmitter->packet_size = 0;
- mpf_rtp_tx_stat_reset(&transmitter->stat);
+ mpf_rtcp_sr_stat_reset(&transmitter->sr_stat);
}
APT_END_EXTERN_C
* @brief MPF RTP Stream Descriptor
*/
-#include "mpf_stream_mode.h"
-#include "mpf_media_descriptor.h"
-#include "mpf_codec_descriptor.h"
+#include <apr_network_io.h>
+#include "apt_string.h"
+#include "mpf_stream_descriptor.h"
APT_BEGIN_EXTERN_C
/** Jitter buffer configuration declaration */
typedef struct mpf_jb_config_t mpf_jb_config_t;
+/** MPF media state */
+typedef enum {
+ MPF_MEDIA_DISABLED, /**< disabled media */
+ MPF_MEDIA_ENABLED /**< enabled media */
+} mpf_media_state_e;
/** RTP media (local/remote) descriptor */
struct mpf_rtp_media_descriptor_t {
- /** Media descriptor base */
- mpf_media_descriptor_t base;
+ /** 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;
/** Stream mode (send/receive) */
- mpf_stream_mode_e mode;
+ mpf_stream_direction_e direction;
/** Packetization time */
apr_uint16_t ptime;
/** Codec list */
mpf_codec_list_t codec_list;
/** Media identifier */
apr_size_t mid;
+ /** Position, order in SDP message (0,1,...) */
+ apr_size_t id;
};
/** RTP stream descriptor */
struct mpf_rtp_stream_descriptor_t {
+ /** Stream capabilities */
+ mpf_stream_capabilities_t *capabilities;
/** Local media descriptor */
mpf_rtp_media_descriptor_t *local;
/** Remote media descriptor */
apr_byte_t adaptive;
};
+typedef enum {
+ RTCP_BYE_DISABLE, /**< disable RTCP BYE transmission */
+ RTCP_BYE_PER_SESSION, /**< transmit RTCP BYE at the end of session */
+ RTCP_BYE_PER_TALKSPURT, /**< transmit RTCP BYE at the end of each talkspurt (input) */
+} rtcp_bye_policy_e;
+
/** RTP config */
struct mpf_rtp_config_t {
/** Local IP address to bind to */
- apt_str_t ip;
+ apt_str_t ip;
/** External (NAT) IP address */
- apt_str_t ext_ip;
+ apt_str_t ext_ip;
/** Min RTP port */
- apr_port_t rtp_port_min;
+ apr_port_t rtp_port_min;
/** Max RTP port */
- apr_port_t rtp_port_max;
+ apr_port_t rtp_port_max;
/** Current RTP port */
- apr_port_t rtp_port_cur;
+ apr_port_t rtp_port_cur;
/** Packetization time */
- apr_uint16_t ptime;
+ 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;
+ mpf_codec_list_t codec_list;
+ /** Preference in offer/anwser: 1 - own(local) preference, 0 - remote preference */
+ apt_bool_t own_preferrence;
+ /** Enable/disable RTCP support */
+ apt_bool_t rtcp;
+ /** RTCP BYE policy */
+ rtcp_bye_policy_e rtcp_bye_policy;
+ /** RTCP report transmission interval */
+ apr_uint16_t rtcp_tx_interval;
+ /** RTCP rx resolution (timeout to check for a new RTCP message) */
+ apr_uint16_t rtcp_rx_resolution;
/** Jitter buffer config */
- mpf_jb_config_t jb_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->state = MPF_MEDIA_DISABLED;
+ apt_string_reset(&media->ip);
+ apt_string_reset(&media->ext_ip);
+ media->port = 0;
+ media->direction = STREAM_DIRECTION_NONE;
media->ptime = 0;
mpf_codec_list_reset(&media->codec_list);
media->mid = 0;
+ media->id = 0;
}
/** Initialize stream descriptor */
-static APR_INLINE void mpf_rtp_stream_descriptor_init(mpf_rtp_stream_descriptor_t *stream)
+static APR_INLINE void mpf_rtp_stream_descriptor_init(mpf_rtp_stream_descriptor_t *descriptor)
{
- stream->local = NULL;
- stream->remote = NULL;
+ descriptor->capabilities = NULL;
+ descriptor->local = NULL;
+ descriptor->remote = NULL;
}
/** Initialize RTP termination descriptor */
rtp_config->ptime = 0;
mpf_codec_list_init(&rtp_config->codec_list,0,pool);
rtp_config->own_preferrence = FALSE;
+ rtp_config->rtcp = FALSE;
+ rtp_config->rtcp_bye_policy = RTCP_BYE_DISABLE;
+ rtp_config->rtcp_tx_interval = 0;
+ rtp_config->rtcp_rx_resolution = 0;
mpf_jb_config_init(&rtp_config->jb_config);
+
return rtp_config;
}
APT_BEGIN_EXTERN_C
-/** Protocol version. */
+/** Protocol version */
#define RTP_VERSION 2
/** RTP header declaration */
};
/** RTP extension header */
-struct rtp_extension_header_t
-{
+struct rtp_extension_header_t {
/** profile */
apr_uint16_t profile;
/** length */
--- /dev/null
+/*
+ * 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_PT_H__
+#define __MPF_RTP_PT_H__
+
+/**
+ * @file mpf_rtp_pt.h
+ * @brief RTP Payload Types (RFC3551)
+ */
+
+#include "mpf.h"
+
+APT_BEGIN_EXTERN_C
+
+/** RTP payload types */
+typedef enum {
+ RTP_PT_PCMU = 0, /**< PCMU Audio 8kHz 1 */
+ RTP_PT_PCMA = 8, /**< PCMA Audio 8kHz 1 */
+
+ RTP_PT_CN = 13, /**< Comfort Noise Audio 8kHz 1 */
+
+ RTP_PT_DYNAMIC = 96, /**< Start of dynamic payload types */
+ RTP_PT_DYNAMIC_MAX = 127, /**< End of dynamic payload types */
+
+ RTP_PT_UNKNOWN = 128 /**< Unknown (invalid) payload type */
+} mpf_rtp_pt_e;
+
+APT_END_EXTERN_C
+
+#endif /*__MPF_RTP_PT_H__*/
/**
* @file mpf_rtp_stat.h
- * @brief RTP Statistics
+ * @brief RTP/RTCP 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 */
+/** RTP receiver statistics */
typedef struct rtp_rx_stat_t rtp_rx_stat_t;
+/** RTCP statistics used in Sender Report (SR) */
+typedef struct rtcp_sr_stat_t rtcp_sr_stat_t;
+/** RTCP statistics used in Receiver Report (RR) */
+typedef struct rtcp_rr_stat_t rtcp_rr_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 */
+/** RTP receiver statistics */
struct rtp_rx_stat_t {
/** number of valid RTP packets received */
- apr_uint32_t received_packets;
+ apr_uint32_t received_packets;
/** number of invalid RTP packets received */
- apr_uint32_t invalid_packets;
+ apr_uint32_t invalid_packets;
/** number of discarded in jitter buffer packets */
- apr_uint32_t discarded_packets;
+ apr_uint32_t discarded_packets;
/** number of ignored packets */
- apr_uint32_t ignored_packets;
+ apr_uint32_t ignored_packets;
/** number of lost in network packets */
- apr_uint32_t lost_packets;
+ apr_uint32_t lost_packets;
/** number of restarts */
- apr_byte_t restarts;
+ apr_byte_t restarts;
+};
- /** network jitter (rfc3550) */
- apr_uint32_t jitter;
+/** RTCP statistics used in Sender Report (SR) */
+struct rtcp_sr_stat_t {
+ /** sender source identifier */
+ apr_uint32_t ssrc;
+ /** NTP timestamp (seconds) */
+ apr_uint32_t ntp_sec;
+ /** NTP timestamp (fractions) */
+ apr_uint32_t ntp_frac;
+ /** RTP timestamp */
+ apr_uint32_t rtp_ts;
+ /* packets sent */
+ apr_uint32_t sent_packets;
+ /* octets (bytes) sent */
+ apr_uint32_t sent_octets;
+};
- /** source id of received RTP stream */
- apr_uint32_t ssrc;
+/** RTCP statistics used in Receiver Report (RR) */
+struct rtcp_rr_stat_t {
+ /** source identifier of RTP stream being received */
+ apr_uint32_t ssrc;
+ /** fraction lost since last SR/RR */
+ apr_uint32_t fraction:8;
+ /** cumulative number of packets lost (signed!) */
+ apr_int32_t lost:24;
+ /** extended last sequence number received */
+ apr_uint32_t last_seq;
+ /** interarrival jitter (RFC3550) */
+ apr_uint32_t jitter;
+ /** last SR packet from this source */
+ apr_uint32_t lsr;
+ /** delay since last SR packet */
+ apr_uint32_t dlsr;
};
-/** Reset RTP transmit statistics */
-static APR_INLINE void mpf_rtp_tx_stat_reset(rtp_tx_stat_t *tx_stat)
+
+/** Reset RTCP SR statistics */
+static APR_INLINE void mpf_rtcp_sr_stat_reset(rtcp_sr_stat_t *sr_stat)
+{
+ memset(sr_stat,0,sizeof(rtcp_sr_stat_t));
+}
+
+/** Reset RTCP RR statistics */
+static APR_INLINE void mpf_rtcp_rr_stat_reset(rtcp_rr_stat_t *rr_stat)
{
- memset(tx_stat,0,sizeof(rtp_tx_stat_t));
+ memset(rr_stat,0,sizeof(rtcp_rr_stat_t));
}
-/** Reset RTP receive statistics */
+/** Reset RTP receiver 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));
*/
MPF_DECLARE(mpf_audio_stream_t*) mpf_rtp_stream_create(mpf_termination_t *termination, mpf_rtp_config_t *config, apr_pool_t *pool);
+/**
+ * Add/enable RTP stream.
+ * @param stream RTP stream to add
+ */
+MPF_DECLARE(apt_bool_t) mpf_rtp_stream_add(mpf_audio_stream_t *stream);
+
+/**
+ * Subtract/disable RTP stream.
+ * @param stream RTP stream to subtract
+ */
+MPF_DECLARE(apt_bool_t) mpf_rtp_stream_remove(mpf_audio_stream_t *stream);
+
/**
* Modify RTP stream.
* @param stream RTP stream to modify
* @brief MPF RTP Termination Factory
*/
-#include <apr_network_io.h>
-#include "mpf_types.h"
+#include "mpf_termination_factory.h"
#include "mpf_rtp_descriptor.h"
APT_BEGIN_EXTERN_C
--- /dev/null
+/*
+ * 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_SCHEDULER_H__
+#define __MPF_SCHEDULER_H__
+
+/**
+ * @file mpf_scheduler.h
+ * @brief MPF Scheduler (High Resolution Clock for Media Processing and Timer)
+ */
+
+#include "mpf_types.h"
+
+APT_BEGIN_EXTERN_C
+
+/** Prototype of scheduler callback */
+typedef void (*mpf_scheduler_proc_f)(mpf_scheduler_t *scheduler, void *obj);
+
+/** Create scheduler */
+MPF_DECLARE(mpf_scheduler_t*) mpf_scheduler_create(unsigned long rate, apr_pool_t *pool);
+
+/** Destroy scheduler */
+MPF_DECLARE(void) mpf_scheduler_destroy(mpf_scheduler_t *scheduler);
+
+/** Set media processing clock */
+MPF_DECLARE(apt_bool_t) mpf_scheduler_media_clock_set(
+ mpf_scheduler_t *scheduler,
+ unsigned long resolution,
+ mpf_scheduler_proc_f proc,
+ void *obj);
+
+/** Set timer clock */
+MPF_DECLARE(apt_bool_t) mpf_scheduler_timer_clock_set(
+ mpf_scheduler_t *scheduler,
+ unsigned long resolution,
+ mpf_scheduler_proc_f proc,
+ void *obj);
+
+/** Start scheduler */
+MPF_DECLARE(apt_bool_t) mpf_scheduler_start(mpf_scheduler_t *scheduler);
+
+/** Stop scheduler */
+MPF_DECLARE(apt_bool_t) mpf_scheduler_stop(mpf_scheduler_t *scheduler);
+
+
+APT_END_EXTERN_C
+
+#endif /*__MPF_SCHEDULER_H__*/
*/
#include "mpf_types.h"
-#include "mpf_stream_mode.h"
#include "mpf_frame.h"
+#include "mpf_stream_descriptor.h"
#include "mpf_codec.h"
+#include "apt_text_stream.h"
APT_BEGIN_EXTERN_C
-/** Opaque audio stream virtual table declaration */
+/** Declaration of virtual table of audio stream */
typedef struct mpf_audio_stream_vtable_t mpf_audio_stream_vtable_t;
/** Audio stream */
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;
+
+ /** Stream capabilities */
+ const mpf_stream_capabilities_t *capabilities;
+
+ /** Stream direction send/receive (bitmask of mpf_stream_direction_e) */
+ mpf_stream_direction_e direction;
+ /** Rx codec descriptor */
+ mpf_codec_descriptor_t *rx_descriptor;
+ /** Rx event descriptor */
+ mpf_codec_descriptor_t *rx_event_descriptor;
+ /** Tx codec descriptor */
+ mpf_codec_descriptor_t *tx_descriptor;
+ /** Tx event descriptor */
+ mpf_codec_descriptor_t *tx_event_descriptor;
};
/** Video stream */
struct mpf_video_stream_t {
/** Back pointer */
mpf_termination_t *termination;
- /** Stream mode (send/receive) */
- mpf_stream_mode_e mode;
+ /** Stream direction send/receive (bitmask of mpf_stream_direction_e) */
+ mpf_stream_direction_e direction;
};
/** Table of audio stream virtual methods */
apt_bool_t (*destroy)(mpf_audio_stream_t *stream);
/** Virtual open receiver method */
- apt_bool_t (*open_rx)(mpf_audio_stream_t *stream);
+ apt_bool_t (*open_rx)(mpf_audio_stream_t *stream, mpf_codec_t *codec);
/** 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);
+ apt_bool_t (*open_tx)(mpf_audio_stream_t *stream, mpf_codec_t *codec);
/** 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);
-};
+ /** Virtual trace method */
+ void (*trace)(mpf_audio_stream_t *stream, mpf_stream_direction_e direction, apt_text_stream_t *output);
+};
/** 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;
-}
+MPF_DECLARE(mpf_audio_stream_t*) mpf_audio_stream_create(void *obj, const mpf_audio_stream_vtable_t *vtable, const mpf_stream_capabilities_t *capabilities, apr_pool_t *pool);
+
+/** Validate audio stream receiver */
+MPF_DECLARE(apt_bool_t) mpf_audio_stream_rx_validate(
+ mpf_audio_stream_t *stream,
+ const mpf_codec_descriptor_t *descriptor,
+ const mpf_codec_descriptor_t *event_descriptor,
+ apr_pool_t *pool);
+
+/** Validate audio stream transmitter */
+MPF_DECLARE(apt_bool_t) mpf_audio_stream_tx_validate(
+ mpf_audio_stream_t *stream,
+ const mpf_codec_descriptor_t *descriptor,
+ const mpf_codec_descriptor_t *event_descriptor,
+ apr_pool_t *pool);
/** Destroy audio stream */
static APR_INLINE apt_bool_t mpf_audio_stream_destroy(mpf_audio_stream_t *stream)
return TRUE;
}
-/** Open audio stream receive */
-static APR_INLINE apt_bool_t mpf_audio_stream_rx_open(mpf_audio_stream_t *stream)
+/** Open audio stream receiver */
+static APR_INLINE apt_bool_t mpf_audio_stream_rx_open(mpf_audio_stream_t *stream, mpf_codec_t *codec)
{
if(stream->vtable->open_rx)
- return stream->vtable->open_rx(stream);
+ return stream->vtable->open_rx(stream,codec);
return TRUE;
}
-/** Close audio stream receive */
+/** Close audio stream receiver */
static APR_INLINE apt_bool_t mpf_audio_stream_rx_close(mpf_audio_stream_t *stream)
{
if(stream->vtable->close_rx)
return TRUE;
}
-/** Open audio stream transmit */
-static APR_INLINE apt_bool_t mpf_audio_stream_tx_open(mpf_audio_stream_t *stream)
+/** Open audio stream transmitter */
+static APR_INLINE apt_bool_t mpf_audio_stream_tx_open(mpf_audio_stream_t *stream, mpf_codec_t *codec)
{
if(stream->vtable->open_tx)
- return stream->vtable->open_tx(stream);
+ return stream->vtable->open_tx(stream,codec);
return TRUE;
}
-/** Close audio stream transmit */
+/** Close audio stream transmitter */
static APR_INLINE apt_bool_t mpf_audio_stream_tx_close(mpf_audio_stream_t *stream)
{
if(stream->vtable->close_tx)
return TRUE;
}
+/** Trace media path */
+MPF_DECLARE(void) mpf_audio_stream_trace(mpf_audio_stream_t *stream, mpf_stream_direction_e direction, apt_text_stream_t *output);
+
APT_END_EXTERN_C
#endif /*__MPF_STREAM_H__*/
--- /dev/null
+/*
+ * 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_DESCRIPTOR_H__
+#define __MPF_STREAM_DESCRIPTOR_H__
+
+/**
+ * @file mpf_stream_descriptor.h
+ * @brief MPF Stream Descriptor
+ */
+
+#include "mpf_codec_descriptor.h"
+
+APT_BEGIN_EXTERN_C
+
+/** Stream capabilities declaration */
+typedef struct mpf_stream_capabilities_t mpf_stream_capabilities_t;
+
+/** Stream directions (none, send, receive, duplex) */
+typedef enum {
+ STREAM_DIRECTION_NONE = 0x0, /**< none */
+ STREAM_DIRECTION_SEND = 0x1, /**< send (sink) */
+ STREAM_DIRECTION_RECEIVE = 0x2, /**< receive (source) */
+
+ STREAM_DIRECTION_DUPLEX = STREAM_DIRECTION_SEND | STREAM_DIRECTION_RECEIVE /**< duplex */
+} mpf_stream_direction_e;
+
+
+/** Stream capabilities */
+struct mpf_stream_capabilities_t {
+ /** Supported directions either send, receive or bidirectional stream (bitmask of mpf_stream_direction_e) */
+ mpf_stream_direction_e direction;
+ /** Codec capabilities (supported codecs and named events) */
+ mpf_codec_capabilities_t codecs;
+};
+
+/** Create stream capabilities */
+MPF_DECLARE(mpf_stream_capabilities_t*) mpf_stream_capabilities_create(mpf_stream_direction_e directions, apr_pool_t *pool);
+
+/** Create source stream capabilities */
+static APR_INLINE mpf_stream_capabilities_t* mpf_source_stream_capabilities_create(apr_pool_t *pool)
+{
+ return mpf_stream_capabilities_create(STREAM_DIRECTION_RECEIVE,pool);
+}
+
+/** Create sink stream capabilities */
+static APR_INLINE mpf_stream_capabilities_t* mpf_sink_stream_capabilities_create(apr_pool_t *pool)
+{
+ return mpf_stream_capabilities_create(STREAM_DIRECTION_SEND,pool);
+}
+
+/** Clone stream capabilities */
+MPF_DECLARE(mpf_stream_capabilities_t*) mpf_stream_capabilities_clone(const mpf_stream_capabilities_t *src_capabilities, apr_pool_t *pool);
+
+/** Merge stream capabilities */
+MPF_DECLARE(apt_bool_t) mpf_stream_capabilities_merge(mpf_stream_capabilities_t *capabilities, const mpf_stream_capabilities_t *src_capabilities, apr_pool_t *pool);
+
+
+/** Get reverse direction */
+static APR_INLINE mpf_stream_direction_e mpf_stream_reverse_direction_get(mpf_stream_direction_e direction)
+{
+ mpf_stream_direction_e rev_direction = direction;
+ if(rev_direction == STREAM_DIRECTION_SEND) {
+ rev_direction = STREAM_DIRECTION_RECEIVE;
+ }
+ else if(rev_direction == STREAM_DIRECTION_RECEIVE) {
+ rev_direction = STREAM_DIRECTION_SEND;
+ }
+ return rev_direction;
+}
+
+
+APT_END_EXTERN_C
+
+#endif /*__MPF_STREAM_DESCRIPTOR_H__*/
+++ /dev/null
-/*
- * 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__*/
/** Prototype of termination event handler */
typedef apt_bool_t (*mpf_termination_event_handler_f)(mpf_termination_t *termination, int event_id, void *descriptor);
+/** 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 add method */
+ apt_bool_t (*add)(mpf_termination_t *termination, void *descriptor);
+ /** Virtual termination modify method */
+ apt_bool_t (*modify)(mpf_termination_t *termination, void *descriptor);
+ /** Virtual termination subtract method */
+ apt_bool_t (*subtract)(mpf_termination_t *termination);
+};
+
+
/** MPF Termination */
struct mpf_termination_t {
/** Pool to allocate memory from */
mpf_termination_event_handler_f event_handler;
/** Codec manager */
const mpf_codec_manager_t *codec_manager;
+ /** Timer manager */
+ mpf_timer_manager_t *timer_manager;
/** Termination factory entire termination created by */
mpf_termination_factory_t *termination_factory;
/** Table of virtual methods */
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
apr_pool_t *pool);
/**
- * Modify MPF termination.
- * @param termination the termination to modify
+ * Add MPF termination.
+ * @param termination the termination to add
* @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);
+MPF_DECLARE(apt_bool_t) mpf_termination_add(mpf_termination_t *termination, void *descriptor);
/**
- * 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
+ * Modify MPF termination.
+ * @param termination the termination to modify
+ * @param descriptor the termination specific descriptor
*/
-MPF_DECLARE(void*) mpf_termination_object_get(mpf_termination_t *termination);
-
+MPF_DECLARE(apt_bool_t) mpf_termination_modify(mpf_termination_t *termination, void *descriptor);
/**
- * 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
+ * Subtract MPF termination.
+ * @param termination the termination to subtract
*/
-MPF_DECLARE(mpf_termination_t*) mpf_termination_create(
- mpf_termination_factory_t *termination_factory,
- void *obj,
- apr_pool_t *pool);
+MPF_DECLARE(apt_bool_t) mpf_termination_subtract(mpf_termination_t *termination);
-/**
- * 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
--- /dev/null
+/*
+ * 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_FACTORY_H__
+#define __MPF_TERMINATION_FACTORY_H__
+
+/**
+ * @file mpf_termination_factory.h
+ * @brief MPF Termination Factory
+ */
+
+#include "mpf_types.h"
+
+APT_BEGIN_EXTERN_C
+
+/** 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);
+};
+
+
+
+/**
+ * Create MPF termination from 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);
+
+/**
+ * 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);
+
+/**
+ * Get audio stream.
+ * @param termination the termination to get audio stream from
+ */
+MPF_DECLARE(mpf_audio_stream_t*) mpf_termination_audio_stream_get(mpf_termination_t *termination);
+
+/**
+ * Get video stream.
+ * @param termination the termination to get video stream from
+ */
+MPF_DECLARE(mpf_video_stream_t*) mpf_termination_video_stream_get(mpf_termination_t *termination);
+
+
+APT_END_EXTERN_C
+
+#endif /*__MPF_TERMINATION_FACTORY_H__*/
--- /dev/null
+/*
+ * 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_MANAGER_H__
+#define __MPF_TIMER_MANAGER_H__
+
+/**
+ * @file mpf_timer_manager.h
+ * @brief MPF Timer Management
+ */
+
+#include "mpf_types.h"
+
+APT_BEGIN_EXTERN_C
+
+
+/** Prototype of timer callback */
+typedef void (*mpf_timer_proc_f)(mpf_timer_t *timer, void *obj);
+
+
+/** Create timer manager */
+MPF_DECLARE(mpf_timer_manager_t*) mpf_timer_manager_create(mpf_scheduler_t *scheduler, apr_pool_t *pool);
+
+/** Destroy timer manager */
+MPF_DECLARE(void) mpf_timer_manager_destroy(mpf_timer_manager_t *timer_manager);
+
+
+/** Create timer */
+MPF_DECLARE(mpf_timer_t*) mpf_timer_create(mpf_timer_manager_t *timer_manager, mpf_timer_proc_f proc, void *obj, apr_pool_t *pool);
+
+/** Set one-shot timer */
+MPF_DECLARE(apt_bool_t) mpf_timer_set(mpf_timer_t *timer, apr_uint32_t timeout);
+
+/** Kill timer */
+MPF_DECLARE(apt_bool_t) mpf_timer_kill(mpf_timer_t *timer);
+
+
+APT_END_EXTERN_C
+
+#endif /*__MPF_TIMER_MANAGER_H__*/
/** Opaque MPF engine declaration */
typedef struct mpf_engine_t mpf_engine_t;
+/** Opaque MPF scheduler declaration */
+typedef struct mpf_scheduler_t mpf_scheduler_t;
+
/** Opaque codec manager declaration */
typedef struct mpf_codec_manager_t mpf_codec_manager_t;
+/** Opaque MPF timer manager declaration */
+typedef struct mpf_timer_manager_t mpf_timer_manager_t;
+
+/** Opaque MPF timer declaration */
+typedef struct mpf_timer_t mpf_timer_t;
+
/** Opaque MPF context declaration */
typedef struct mpf_context_t mpf_context_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
+++ /dev/null
-/*
- * 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__*/
RelativePath=".\include\mpf_decoder.h"\r
>\r
</File>\r
+ <File\r
+ RelativePath=".\include\mpf_dtmf_detector.h"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath=".\include\mpf_dtmf_generator.h"\r
+ >\r
+ </File>\r
<File\r
RelativePath=".\include\mpf_encoder.h"\r
>\r
>\r
</File>\r
<File\r
- RelativePath=".\include\mpf_media_descriptor.h"\r
+ RelativePath=".\include\mpf_message.h"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath=".\include\mpf_mixer.h"\r
>\r
</File>\r
<File\r
- RelativePath=".\include\mpf_message.h"\r
+ RelativePath=".\include\mpf_multiplier.h"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath=".\include\mpf_named_event.h"\r
>\r
</File>\r
<File\r
RelativePath=".\include\mpf_object.h"\r
>\r
</File>\r
+ <File\r
+ RelativePath=".\include\mpf_resampler.h"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath=".\include\mpf_rtcp_packet.h"\r
+ >\r
+ </File>\r
<File\r
RelativePath=".\include\mpf_rtp_attribs.h"\r
>\r
RelativePath=".\include\mpf_rtp_header.h"\r
>\r
</File>\r
+ <File\r
+ RelativePath=".\include\mpf_rtp_pt.h"\r
+ >\r
+ </File>\r
<File\r
RelativePath=".\include\mpf_rtp_stat.h"\r
>\r
RelativePath=".\include\mpf_rtp_termination_factory.h"\r
>\r
</File>\r
+ <File\r
+ RelativePath=".\include\mpf_scheduler.h"\r
+ >\r
+ </File>\r
<File\r
RelativePath=".\include\mpf_stream.h"\r
>\r
</File>\r
<File\r
- RelativePath=".\include\mpf_stream_mode.h"\r
+ RelativePath=".\include\mpf_stream_descriptor.h"\r
>\r
</File>\r
<File\r
>\r
</File>\r
<File\r
- RelativePath=".\include\mpf_timer.h"\r
+ RelativePath=".\include\mpf_termination_factory.h"\r
>\r
</File>\r
<File\r
- RelativePath=".\include\mpf_types.h"\r
+ RelativePath=".\include\mpf_timer_manager.h"\r
>\r
</File>\r
<File\r
- RelativePath=".\include\mpf_user.h"\r
+ RelativePath=".\include\mpf_types.h"\r
>\r
</File>\r
</Filter>\r
RelativePath=".\src\mpf_decoder.c"\r
>\r
</File>\r
+ <File\r
+ RelativePath=".\src\mpf_dtmf_detector.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath=".\src\mpf_dtmf_generator.c"\r
+ >\r
+ </File>\r
<File\r
RelativePath=".\src\mpf_encoder.c"\r
>\r
RelativePath=".\src\mpf_jitter_buffer.c"\r
>\r
</File>\r
+ <File\r
+ RelativePath=".\src\mpf_mixer.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath=".\src\mpf_multiplier.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath=".\src\mpf_named_event.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath=".\src\mpf_resampler.c"\r
+ >\r
+ </File>\r
<File\r
RelativePath=".\src\mpf_rtp_attribs.c"\r
>\r
RelativePath=".\src\mpf_rtp_termination_factory.c"\r
>\r
</File>\r
+ <File\r
+ RelativePath=".\src\mpf_scheduler.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath=".\src\mpf_stream.c"\r
+ >\r
+ </File>\r
<File\r
RelativePath=".\src\mpf_termination.c"\r
>\r
</File>\r
<File\r
- RelativePath=".\src\mpf_timer.c"\r
+ RelativePath=".\src\mpf_termination_factory.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath=".\src\mpf_timer_manager.c"\r
>\r
</File>\r
</Filter>\r
/** Activity detector */
struct mpf_activity_detector_t {
/* voice activity (silence) level threshold */
- apr_size_t level_threshold;
+ apr_size_t level_threshold;
- /* period of activity/inactivity required to complete/raise an event */
- apr_size_t complete_timeout;
+ /* period of activity required to complete transition to active state */
+ apr_size_t speech_timeout;
+ /* period of inactivity required to complete transition to inactive state */
+ apr_size_t silence_timeout;
/* noinput timeout */
- apr_size_t noinput_timeout;
+ apr_size_t noinput_timeout;
/* current state */
- apt_bool_t state;
+ mpf_detector_state_e state;
/* duration spent in current state */
- apr_size_t duration;
+ apr_size_t duration;
};
/** Create activity detector */
{
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->speech_timeout = 300; /* 0.3 s */
+ detector->silence_timeout = 300; /* 0.3 s */
detector->noinput_timeout = 5000; /* 5 s */
detector->duration = 0;
detector->state = DETECTOR_STATE_INACTIVITY;
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)
+/** Set timeout required to trigger speech (transition from inactive to active state) */
+MPF_DECLARE(void) mpf_activity_detector_speech_timeout_set(mpf_activity_detector_t *detector, apr_size_t speech_timeout)
{
- detector->complete_timeout = complete_timeout;
+ detector->speech_timeout = speech_timeout;
+}
+
+/** Set timeout required to trigger silence (transition from active to inactive state) */
+MPF_DECLARE(void) mpf_activity_detector_silence_timeout_set(mpf_activity_detector_t *detector, apr_size_t silence_timeout)
+{
+ detector->silence_timeout = silence_timeout;
}
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) {
+ if(detector->duration >= detector->speech_timeout) {
/* finally detected activity */
det_event = MPF_DETECTOR_EVENT_ACTIVITY;
mpf_activity_detector_state_change(detector,DETECTOR_STATE_ACTIVITY);
}
else {
detector->duration += CODEC_FRAME_TIME_BASE;
- if(detector->duration >= detector->complete_timeout) {
+ if(detector->duration >= detector->silence_timeout) {
/* detected inactivity */
det_event = MPF_DETECTOR_EVENT_INACTIVITY;
mpf_activity_detector_state_change(detector,DETECTOR_STATE_INACTIVITY);
return TRUE;
}
-static apt_bool_t mpf_audio_file_reader_open(mpf_audio_stream_t *stream)
+static apt_bool_t mpf_audio_file_reader_open(mpf_audio_stream_t *stream, mpf_codec_t *codec)
{
return TRUE;
}
}
-static apt_bool_t mpf_audio_file_writer_open(mpf_audio_stream_t *stream)
+static apt_bool_t mpf_audio_file_writer_open(mpf_audio_stream_t *stream, mpf_codec_t *codec)
{
return TRUE;
}
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;
+ mpf_stream_capabilities_t *capabilities = mpf_stream_capabilities_create(STREAM_DIRECTION_DUPLEX,pool);
+ mpf_audio_stream_t *audio_stream = mpf_audio_stream_create(file_stream,&vtable,capabilities,pool);
+ if(!audio_stream) {
+ return NULL;
+ }
+ audio_stream->termination = termination;
+ file_stream->audio_stream = audio_stream;
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;
+
+ return audio_stream;
}
MPF_DECLARE(apt_bool_t) mpf_file_stream_modify(mpf_audio_stream_t *stream, mpf_audio_file_descriptor_t *descriptor)
}
file_stream->read_handle = descriptor->read_handle;
file_stream->eof = FALSE;
- stream->mode |= FILE_READER;
+ stream->direction |= FILE_READER;
- stream->rx_codec = mpf_codec_manager_codec_get(
- stream->termination->codec_manager,
- &descriptor->codec_descriptor,
- stream->termination->pool);
+ stream->rx_descriptor = descriptor->codec_descriptor;
}
if(descriptor->mask & FILE_WRITER) {
if(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->direction |= FILE_WRITER;
- stream->tx_codec = mpf_codec_manager_codec_get(
- stream->termination->codec_manager,
- &descriptor->codec_descriptor,
- stream->termination->pool);
+ stream->tx_descriptor = descriptor->codec_descriptor;
}
return TRUE;
}
*/
#include "mpf_bridge.h"
-#include "mpf_stream.h"
+#include "mpf_encoder.h"
+#include "mpf_decoder.h"
+#include "mpf_resampler.h"
+#include "mpf_codec_manager.h"
#include "apt_log.h"
+typedef struct mpf_bridge_t mpf_bridge_t;
+
+/** MPF bridge derived from MPF object */
+struct mpf_bridge_t {
+ /** MPF bridge base */
+ mpf_object_t base;
+ /** 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;
+};
+
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);
+ mpf_bridge_t *bridge = (mpf_bridge_t*) object;
+ bridge->frame.type = MEDIA_FRAME_TYPE_NONE;
+ bridge->frame.marker = MPF_MARKER_NONE;
+ bridge->source->vtable->read_frame(bridge->source,&bridge->frame);
- if((object->frame.type & MEDIA_FRAME_TYPE_AUDIO) == 0) {
- memset( object->frame.codec_frame.buffer,
+ if((bridge->frame.type & MEDIA_FRAME_TYPE_AUDIO) == 0) {
+ memset( bridge->frame.codec_frame.buffer,
0,
- object->frame.codec_frame.size);
+ bridge->frame.codec_frame.size);
}
- object->sink->vtable->write_frame(object->sink,&object->frame);
+ bridge->sink->vtable->write_frame(bridge->sink,&bridge->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);
+ mpf_bridge_t *bridge = (mpf_bridge_t*) object;
+ bridge->frame.type = MEDIA_FRAME_TYPE_NONE;
+ bridge->source->vtable->read_frame(bridge->source,&bridge->frame);
+ bridge->sink->vtable->write_frame(bridge->sink,&bridge->frame);
return TRUE;
}
+static void mpf_bridge_trace(mpf_object_t *object)
+{
+ mpf_bridge_t *bridge = (mpf_bridge_t*) object;
+ char buf[1024];
+ apr_size_t offset;
+
+ apt_text_stream_t output;
+ apt_text_stream_init(&output,buf,sizeof(buf)-1);
+
+ mpf_audio_stream_trace(bridge->source,STREAM_DIRECTION_RECEIVE,&output);
+
+ offset = output.pos - output.text.buf;
+ output.pos += apr_snprintf(output.pos, output.text.length - offset,
+ "->Bridge->");
+
+ mpf_audio_stream_trace(bridge->sink,STREAM_DIRECTION_SEND,&output);
+
+ *output.pos = '\0';
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,output.text.buf);
+}
+
static apt_bool_t mpf_bridge_destroy(mpf_object_t *object)
{
- mpf_object_t *bridge = object;
+ mpf_bridge_t *bridge = (mpf_bridge_t*) 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)
+static mpf_bridge_t* mpf_bridge_base_create(mpf_audio_stream_t *source, mpf_audio_stream_t *sink, apr_pool_t *pool)
{
- mpf_object_t *bridge;
+ mpf_bridge_t *bridge;
if(!source || !sink) {
return NULL;
}
- bridge = apr_palloc(pool,sizeof(mpf_object_t));
+ bridge = apr_palloc(pool,sizeof(mpf_bridge_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;
- }
+ mpf_object_init(&bridge->base);
+ bridge->base.destroy = mpf_bridge_destroy;
+ bridge->base.process = mpf_bridge_process;
+ bridge->base.trace = mpf_bridge_trace;
return bridge;
}
-MPF_DECLARE(mpf_object_t*) mpf_bridge_create(mpf_audio_stream_t *source, mpf_audio_stream_t *sink, apr_pool_t *pool)
+static mpf_object_t* mpf_linear_bridge_create(mpf_audio_stream_t *source, mpf_audio_stream_t *sink, const mpf_codec_manager_t *codec_manager, 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");
+ mpf_bridge_t *bridge;
+ apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Create Linear Audio Bridge");
bridge = mpf_bridge_base_create(source,sink,pool);
if(!bridge) {
return NULL;
}
- descriptor = source->rx_codec->descriptor;
+ descriptor = source->rx_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;
+
+ if(mpf_audio_stream_rx_open(source,NULL) == FALSE) {
+ return NULL;
+ }
+ if(mpf_audio_stream_tx_open(sink,NULL) == FALSE) {
+ mpf_audio_stream_rx_close(source);
+ return NULL;
+ }
+ return &bridge->base;
}
-MPF_DECLARE(mpf_object_t*) mpf_null_bridge_create(mpf_audio_stream_t *source, mpf_audio_stream_t *sink, apr_pool_t *pool)
+static mpf_object_t* mpf_null_bridge_create(mpf_audio_stream_t *source, mpf_audio_stream_t *sink, const mpf_codec_manager_t *codec_manager, 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");
+ mpf_bridge_t *bridge;
+ apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Create Null Audio Bridge");
bridge = mpf_bridge_base_create(source,sink,pool);
if(!bridge) {
return NULL;
}
- bridge->process = mpf_null_bridge_process;
+ bridge->base.process = mpf_null_bridge_process;
+
+ codec = mpf_codec_manager_codec_get(codec_manager,source->rx_descriptor,pool);
+ if(!codec) {
+ return NULL;
+ }
- codec = source->rx_codec;
- frame_size = mpf_codec_frame_size_calculate(codec->descriptor,codec->attribs);
+ frame_size = mpf_codec_frame_size_calculate(source->rx_descriptor,codec->attribs);
bridge->frame.codec_frame.size = frame_size;
bridge->frame.codec_frame.buffer = apr_palloc(pool,frame_size);
- return bridge;
+
+ if(mpf_audio_stream_rx_open(source,codec) == FALSE) {
+ return NULL;
+ }
+ if(mpf_audio_stream_tx_open(sink,codec) == FALSE) {
+ mpf_audio_stream_rx_close(source);
+ return NULL;
+ }
+ return &bridge->base;
+}
+
+MPF_DECLARE(mpf_object_t*) mpf_bridge_create(mpf_audio_stream_t *source, mpf_audio_stream_t *sink, const mpf_codec_manager_t *codec_manager, apr_pool_t *pool)
+{
+ if(!source || !sink) {
+ return NULL;
+ }
+
+ if(mpf_audio_stream_rx_validate(source,sink->tx_descriptor,sink->tx_event_descriptor,pool) == FALSE ||
+ mpf_audio_stream_tx_validate(sink,source->rx_descriptor,source->rx_event_descriptor,pool) == FALSE) {
+ return NULL;
+ }
+
+ if(mpf_codec_descriptors_match(source->rx_descriptor,sink->tx_descriptor) == TRUE) {
+ return mpf_null_bridge_create(source,sink,codec_manager,pool);
+ }
+
+ if(mpf_codec_lpcm_descriptor_match(source->rx_descriptor) == FALSE) {
+ mpf_codec_t *codec = mpf_codec_manager_codec_get(codec_manager,source->rx_descriptor,pool);
+ if(codec) {
+ /* set decoder before bridge */
+ mpf_audio_stream_t *decoder = mpf_decoder_create(source,codec,pool);
+ source = decoder;
+ }
+ }
+
+ if(mpf_codec_lpcm_descriptor_match(sink->tx_descriptor) == FALSE) {
+ mpf_codec_t *codec = mpf_codec_manager_codec_get(codec_manager,sink->tx_descriptor,pool);
+ if(codec) {
+ /* set encoder after bridge */
+ mpf_audio_stream_t *encoder = mpf_encoder_create(sink,codec,pool);
+ sink = encoder;
+ }
+ }
+
+ if(source->rx_descriptor->sampling_rate != sink->tx_descriptor->sampling_rate) {
+ /* set resampler before bridge */
+ mpf_audio_stream_t *resampler = mpf_resampler_create(source,sink,pool);
+ if(!resampler) {
+ return NULL;
+ }
+ source = resampler;
+ }
+
+ return mpf_linear_bridge_create(source,sink,codec_manager,pool);
}
*/
#include "mpf_codec_descriptor.h"
+#include "mpf_named_event.h"
+#include "mpf_rtp_pt.h"
+
+/* linear PCM (host horder) */
+#define LPCM_CODEC_NAME "LPCM"
+#define LPCM_CODEC_NAME_LENGTH (sizeof(LPCM_CODEC_NAME)-1)
+
+/* linear PCM atrributes */
+static const mpf_codec_attribs_t lpcm_attribs = {
+ {LPCM_CODEC_NAME, LPCM_CODEC_NAME_LENGTH}, /* codec name */
+ 16, /* bits per sample */
+ MPF_SAMPLE_RATE_8000 | MPF_SAMPLE_RATE_16000 |
+ MPF_SAMPLE_RATE_32000 | MPF_SAMPLE_RATE_48000 /* supported sampling rates */
+};
+
+/** Find matched attribs in codec capabilities by descriptor specified */
+static mpf_codec_attribs_t* mpf_codec_capabilities_attribs_find(const mpf_codec_capabilities_t *capabilities, const mpf_codec_descriptor_t *descriptor);
+
+
+/** Get sampling rate mask (mpf_sample_rate_e) by integer value */
+MPF_DECLARE(int) mpf_sample_rate_mask_get(apr_uint16_t sampling_rate)
+{
+ switch(sampling_rate) {
+ case 8000:
+ return MPF_SAMPLE_RATE_8000;
+ case 16000:
+ return MPF_SAMPLE_RATE_16000;
+ case 32000:
+ return MPF_SAMPLE_RATE_32000;
+ case 48000:
+ return MPF_SAMPLE_RATE_48000;
+ }
+ return MPF_SAMPLE_RATE_NONE;
+}
+
+static APR_INLINE apt_bool_t mpf_sampling_rate_check(apr_uint16_t sampling_rate, int mask)
+{
+ return (mpf_sample_rate_mask_get(sampling_rate) & mask) ? TRUE : FALSE;
+}
+
+MPF_DECLARE(mpf_codec_descriptor_t*) mpf_codec_lpcm_descriptor_create(apr_uint16_t sampling_rate, apr_byte_t channel_count, apr_pool_t *pool)
+{
+ mpf_codec_descriptor_t *descriptor = mpf_codec_descriptor_create(pool);
+ descriptor->payload_type = RTP_PT_UNKNOWN;
+ descriptor->name = lpcm_attribs.name;
+ descriptor->sampling_rate = sampling_rate;
+ descriptor->channel_count = channel_count;
+ return descriptor;
+}
+
+/** Create codec descriptor by capabilities */
+MPF_DECLARE(mpf_codec_descriptor_t*) mpf_codec_descriptor_create_by_capabilities(const mpf_codec_capabilities_t *capabilities, const mpf_codec_descriptor_t *peer, apr_pool_t *pool)
+{
+ mpf_codec_descriptor_t *descriptor;
+ mpf_codec_attribs_t *attribs = NULL;
+ if(capabilities && peer) {
+ attribs = mpf_codec_capabilities_attribs_find(capabilities,peer);
+ }
+
+ if(!attribs) {
+ return mpf_codec_lpcm_descriptor_create(8000,1,pool);
+ }
+
+ descriptor = mpf_codec_descriptor_create(pool);
+ *descriptor = *peer;
+ if(apt_string_compare(&peer->name,&attribs->name) == FALSE) {
+ descriptor->payload_type = RTP_PT_UNKNOWN;
+ descriptor->name = attribs->name;
+ }
+ return descriptor;
+}
+
/** 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)
+MPF_DECLARE(apt_bool_t) mpf_codec_descriptors_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 < RTP_PT_DYNAMIC && descriptor2->payload_type < RTP_PT_DYNAMIC) {
if(descriptor1->payload_type == descriptor2->payload_type) {
match = TRUE;
}
return match;
}
+/** Match specified codec descriptor and the default lpcm one */
+MPF_DECLARE(apt_bool_t) mpf_codec_lpcm_descriptor_match(const mpf_codec_descriptor_t *descriptor)
+{
+ return apt_string_compare(&descriptor->name,&lpcm_attribs.name);
+}
+
+/** Add default (liear PCM) capabilities */
+MPF_DECLARE(apt_bool_t) mpf_codec_default_capabilities_add(mpf_codec_capabilities_t *capabilities)
+{
+ return mpf_codec_capabilities_add(capabilities,MPF_SAMPLE_RATE_8000,lpcm_attribs.name.buf);
+}
+
+/** Match codec descriptors by attribs specified */
+MPF_DECLARE(apt_bool_t) mpf_codec_descriptor_match_by_attribs(mpf_codec_descriptor_t *descriptor, const mpf_codec_descriptor_t *static_descriptor, const mpf_codec_attribs_t *attribs)
+{
+ apt_bool_t match = FALSE;
+ if(descriptor->payload_type < RTP_PT_DYNAMIC) {
+ if(static_descriptor && static_descriptor->payload_type == descriptor->payload_type) {
+ descriptor->name = static_descriptor->name;
+ descriptor->sampling_rate = static_descriptor->sampling_rate;
+ descriptor->channel_count = static_descriptor->channel_count;
+ match = TRUE;
+ }
+ }
+ else {
+ if(apt_string_compare(&attribs->name,&descriptor->name) == TRUE) {
+ if(mpf_sampling_rate_check(descriptor->sampling_rate,attribs->sample_rates) == TRUE) {
+ match = TRUE;
+ }
+ }
+ }
+ return match;
+}
+
+/** Find matched descriptor in codec list */
+MPF_DECLARE(mpf_codec_descriptor_t*) mpf_codec_list_descriptor_find(const mpf_codec_list_t *codec_list, const mpf_codec_descriptor_t *descriptor)
+{
+ int i;
+ mpf_codec_descriptor_t *matched_descriptor;
+ for(i=0; i<codec_list->descriptor_arr->nelts; i++) {
+ matched_descriptor = &APR_ARRAY_IDX(codec_list->descriptor_arr,i,mpf_codec_descriptor_t);
+ if(mpf_codec_descriptors_match(descriptor,matched_descriptor) == TRUE) {
+ return matched_descriptor;
+ }
+ }
+ return NULL;
+}
+
+/** Find matched attribs in codec capabilities by descriptor specified */
+static mpf_codec_attribs_t* mpf_codec_capabilities_attribs_find(const mpf_codec_capabilities_t *capabilities, const mpf_codec_descriptor_t *descriptor)
+{
+ int i;
+ mpf_codec_attribs_t *attribs;
+ for(i=0; i<capabilities->attrib_arr->nelts; i++) {
+ attribs = &APR_ARRAY_IDX(capabilities->attrib_arr,i,mpf_codec_attribs_t);
+ if(mpf_sampling_rate_check(descriptor->sampling_rate,attribs->sample_rates) == TRUE) {
+ return attribs;
+ }
+ }
+ return NULL;
+}
+
+/** Modify codec list according to capabilities specified */
+MPF_DECLARE(apt_bool_t) mpf_codec_list_modify(mpf_codec_list_t *codec_list, const mpf_codec_capabilities_t *capabilities)
+{
+ int i;
+ mpf_codec_descriptor_t *descriptor;
+ if(!capabilities) {
+ return FALSE;
+ }
+
+ for(i=0; i<codec_list->descriptor_arr->nelts; i++) {
+ descriptor = &APR_ARRAY_IDX(codec_list->descriptor_arr,i,mpf_codec_descriptor_t);
+ /* match capabilities */
+ if(!mpf_codec_capabilities_attribs_find(capabilities,descriptor)) {
+ descriptor->enabled = FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
/** 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)
+MPF_DECLARE(apt_bool_t) mpf_codec_lists_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 */
+ codec_list1->primary_descriptor = NULL;
+ codec_list1->event_descriptor = NULL;
+ codec_list2->primary_descriptor = NULL;
+ codec_list2->event_descriptor = NULL;
+ /* find only one match for primary and named event descriptors,
+ set the matched descriptors as preffered, disable the others */
for(i=0; i<codec_list1->descriptor_arr->nelts; i++) {
- descriptor1 = (mpf_codec_descriptor_t*)codec_list1->descriptor_arr->elts + i;
- if(codec_list1->preffered) {
- descriptor1->enabled = FALSE;
+ descriptor1 = &APR_ARRAY_IDX(codec_list1->descriptor_arr,i,mpf_codec_descriptor_t);
+ if(descriptor1->enabled == FALSE) {
+ /* this descriptor has been already disabled, process only enabled ones */
continue;
}
- for(j=0; j<codec_list2->descriptor_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;
+ /* check whether this is a named event descriptor */
+ if(mpf_event_descriptor_check(descriptor1) == TRUE) {
+ /* named event descriptor */
+ if(codec_list1->event_descriptor) {
+ /* named event descriptor has been already set, disable this one */
+ descriptor1->enabled = FALSE;
+ }
+ else {
+ /* find if there is a match */
+ descriptor2 = mpf_codec_list_descriptor_find(codec_list2,descriptor1);
+ if(descriptor2 && descriptor2->enabled == TRUE) {
+ descriptor1->enabled = TRUE;
+ codec_list1->event_descriptor = descriptor1;
+ codec_list2->event_descriptor = descriptor2;
+ }
+ else {
+ /* no match found, disable this descriptor */
+ descriptor1->enabled = FALSE;
+ }
+ }
+ }
+ else {
+ /* primary descriptor */
+ if(codec_list1->primary_descriptor) {
+ /* primary descriptor has been already set, disable this one */
+ descriptor1->enabled = FALSE;
+ }
+ else {
+ /* find if there is a match */
+ descriptor2 = mpf_codec_list_descriptor_find(codec_list2,descriptor1);
+ if(descriptor2 && descriptor2->enabled == TRUE) {
+ descriptor1->enabled = TRUE;
+ codec_list1->primary_descriptor = descriptor1;
+ codec_list2->primary_descriptor = descriptor2;
+ }
+ else {
+ /* no match found, disable this descriptor */
+ descriptor1->enabled = FALSE;
+ }
}
}
}
- for(j=0; j<codec_list2->descriptor_arr->nelts; j++) {
- descriptor2 = (mpf_codec_descriptor_t*)codec_list2->descriptor_arr->elts + j;
- descriptor2->enabled = (codec_list2->preffered == descriptor2) ? TRUE : FALSE;
+
+ for(i=0; i<codec_list2->descriptor_arr->nelts; i++) {
+ descriptor2 = &APR_ARRAY_IDX(codec_list2->descriptor_arr,i,mpf_codec_descriptor_t);
+ if(descriptor2 == codec_list2->primary_descriptor || descriptor2 == codec_list2->event_descriptor) {
+ descriptor2->enabled = TRUE;
+ }
+ else {
+ descriptor2->enabled = FALSE;
+ }
}
return TRUE;
*/
#include "mpf_codec.h"
+#include "mpf_rtp_pt.h"
#include "g711/g711.h"
#define G711u_CODEC_NAME "PCMU"
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;
+ const apr_int16_t *decode_buf;
unsigned char *encode_buf;
- apr_uint32_t i;
+ apr_size_t i;
decode_buf = frame_in->buffer;
encode_buf = frame_out->buffer;
- frame_out->size = frame_in->size / sizeof(short);
+ frame_out->size = frame_in->size / sizeof(apr_int16_t);
for(i=0; i<frame_out->size; i++) {
encode_buf[i] = linear_to_ulaw(decode_buf[i]);
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;
+ apr_int16_t *decode_buf;
const unsigned char *encode_buf;
- apr_uint32_t i;
+ apr_size_t i;
decode_buf = frame_out->buffer;
encode_buf = frame_in->buffer;
- frame_out->size = frame_in->size * sizeof(short);
+ frame_out->size = frame_in->size * sizeof(apr_int16_t);
for(i=0; i<frame_in->size; i++) {
decode_buf[i] = ulaw_to_linear(encode_buf[i]);
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;
+ const apr_int16_t *decode_buf;
unsigned char *encode_buf;
- apr_uint32_t i;
+ apr_size_t i;
decode_buf = frame_in->buffer;
encode_buf = frame_out->buffer;
- frame_out->size = frame_in->size / sizeof(short);
+ frame_out->size = frame_in->size / sizeof(apr_int16_t);
for(i=0; i<frame_out->size; i++) {
encode_buf[i] = linear_to_alaw(decode_buf[i]);
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;
+ apr_int16_t *decode_buf;
const unsigned char *encode_buf;
- apr_uint32_t i;
+ apr_size_t i;
decode_buf = frame_out->buffer;
encode_buf = frame_in->buffer;
- frame_out->size = frame_in->size * sizeof(short);
+ frame_out->size = frame_in->size * sizeof(apr_int16_t);
for(i=0; i<frame_in->size; i++) {
decode_buf[i] = alaw_to_linear(encode_buf[i]);
};
static const mpf_codec_descriptor_t g711u_descriptor = {
- 0,
+ RTP_PT_PCMU,
{G711u_CODEC_NAME, G711u_CODEC_NAME_LENGTH},
8000,
1,
- NULL,
+ {NULL, 0},
TRUE
};
static const mpf_codec_descriptor_t g711a_descriptor = {
- 8,
+ RTP_PT_PCMA,
{G711a_CODEC_NAME, G711a_CODEC_NAME_LENGTH},
8000,
1,
- NULL,
+ {NULL,0},
TRUE
};
#define APR_WANT_BYTEFUNC
#include <apr_want.h>
#include "mpf_codec.h"
-
-/* linear 16-bit PCM (host horder) */
-#define LPCM_CODEC_NAME "LPCM"
-#define LPCM_CODEC_NAME_LENGTH (sizeof(LPCM_CODEC_NAME)-1)
+#include "mpf_rtp_pt.h"
/* linear 16-bit PCM (RFC3551) */
#define L16_CODEC_NAME "L16"
static apt_bool_t l16_encode(mpf_codec_t *codec, const mpf_codec_frame_t *frame_in, mpf_codec_frame_t *frame_out)
{
apr_uint32_t i;
- const short *buf_in = frame_in->buffer;
- short *buf_out = frame_out->buffer;
- apr_size_t samples = frame_in->size / sizeof(short);
+ const apr_int16_t *buf_in = frame_in->buffer;
+ apr_int16_t *buf_out = frame_out->buffer;
+ apr_size_t samples = frame_in->size / sizeof(apr_int16_t);
frame_out->size = frame_in->size;
static apt_bool_t l16_decode(mpf_codec_t *codec, const mpf_codec_frame_t *frame_in, mpf_codec_frame_t *frame_out)
{
apr_uint32_t i;
- const short *buf_in = frame_in->buffer;
- short *buf_out = frame_out->buffer;
- apr_size_t samples = frame_in->size / sizeof(short);
+ const apr_int16_t *buf_in = frame_in->buffer;
+ apr_int16_t *buf_out = frame_out->buffer;
+ apr_size_t samples = frame_in->size / sizeof(apr_int16_t);
frame_out->size = frame_in->size;
return TRUE;
}
-
-
-static const mpf_codec_vtable_t lpcm_vtable = {
- NULL
-};
-
static const mpf_codec_vtable_t l16_vtable = {
l16_open,
l16_close,
NULL
};
-static const mpf_codec_attribs_t lpcm_attribs = {
- {LPCM_CODEC_NAME, LPCM_CODEC_NAME_LENGTH}, /* codec name */
- 16, /* bits per sample */
- MPF_SAMPLE_RATE_8000 | MPF_SAMPLE_RATE_16000 |
- MPF_SAMPLE_RATE_32000 | MPF_SAMPLE_RATE_48000 /* supported sampling rates */
-};
-
static const mpf_codec_attribs_t l16_attribs = {
{L16_CODEC_NAME, L16_CODEC_NAME_LENGTH}, /* codec name */
16, /* bits per sample */
MPF_SAMPLE_RATE_32000 | MPF_SAMPLE_RATE_48000 /* supported sampling rates */
};
-mpf_codec_descriptor_t* mpf_codec_lpcm_descriptor_create(apr_uint16_t sampling_rate, apr_byte_t channel_count, apr_pool_t *pool)
-{
- mpf_codec_descriptor_t *descriptor = apr_palloc(pool,sizeof(mpf_codec_descriptor_t));
- mpf_codec_descriptor_init(descriptor);
- descriptor->payload_type = 96;
- descriptor->name.buf = LPCM_CODEC_NAME;
- descriptor->name.length = LPCM_CODEC_NAME_LENGTH;
- descriptor->sampling_rate = sampling_rate;
- descriptor->channel_count = channel_count;
- return descriptor;
-}
-
-mpf_codec_t* mpf_codec_lpcm_create(apr_pool_t *pool)
-{
- return mpf_codec_create(&lpcm_vtable,&lpcm_attribs,NULL,pool);
-}
-
mpf_codec_t* mpf_codec_l16_create(apr_pool_t *pool)
{
return mpf_codec_create(&l16_vtable,&l16_attribs,NULL,pool);
#include <stdlib.h>
#include "mpf_codec_manager.h"
+#include "mpf_rtp_pt.h"
+#include "mpf_named_event.h"
#include "apt_log.h"
struct mpf_codec_manager_t {
/** Memory pool */
- apr_pool_t *pool;
+ apr_pool_t *pool;
- /** Dynamic array of codecs (mpf_codec_t*) */
- apr_array_header_t *codec_arr;
+ /** Dynamic (resizable) array of codecs (mpf_codec_t*) */
+ apr_array_header_t *codec_arr;
+ /** Default named event descriptor */
+ mpf_codec_descriptor_t *event_descriptor;
};
-mpf_codec_descriptor_t* mpf_codec_lpcm_descriptor_create(apr_uint16_t sampling_rate, apr_byte_t channel_count, apr_pool_t *pool);
-
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*));
+ codec_manager->event_descriptor = mpf_event_descriptor_create(8000,pool);
return codec_manager;
}
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;
+ APR_ARRAY_PUSH(codec_manager->codec_arr,mpf_codec_t*) = 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;
+ mpf_codec_t *codec;
if(!descriptor) {
return NULL;
}
for(i=0; i<codec_manager->codec_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;
- }
+ codec = APR_ARRAY_IDX(codec_manager->codec_arr,i,mpf_codec_t*);
+ if(mpf_codec_descriptor_match_by_attribs(descriptor,codec->static_descriptor,codec->attribs) == TRUE) {
+ return mpf_codec_clone(codec,pool);
}
}
- 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(mpf_codec_t*) mpf_codec_manager_default_codec_get(const mpf_codec_manager_t *codec_manager, apr_pool_t *pool)
-{
- mpf_codec_t *codec;
- mpf_codec_descriptor_t *descriptor = mpf_codec_lpcm_descriptor_create(8000,1,pool);
- codec = mpf_codec_manager_codec_get(codec_manager,descriptor,pool);
- return codec;
+ return NULL;
}
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)
mpf_codec_list_init(codec_list,codec_manager->codec_arr->nelts,pool);
for(i=0; i<codec_manager->codec_arr->nelts; i++) {
- codec = ((mpf_codec_t**)codec_manager->codec_arr->elts)[i];
+ codec = APR_ARRAY_IDX(codec_manager->codec_arr,i,mpf_codec_t*);
static_descriptor = codec->static_descriptor;
if(static_descriptor) {
descriptor = mpf_codec_list_add(codec_list);
}
}
}
+ if(codec_manager->event_descriptor) {
+ descriptor = mpf_codec_list_add(codec_list);
+ if(descriptor) {
+ *descriptor = *codec_manager->event_descriptor;
+ }
+ }
return TRUE;
}
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;
+ if(codec) {
+ 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;
+ /* set default 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 = RTP_PT_DYNAMIC;
+ descriptor->sampling_rate = 8000;
+ descriptor->channel_count = 1;
+ }
}
else {
- descriptor->payload_type = 96;
- descriptor->sampling_rate = 8000;
- descriptor->channel_count = 1;
+ mpf_codec_descriptor_t *event_descriptor = codec_manager->event_descriptor;
+ if(event_descriptor && apt_string_compare(&event_descriptor->name,&name) == TRUE) {
+ descriptor = mpf_codec_list_add(codec_list);
+ *descriptor = *event_descriptor;
+ }
+ else {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No Such Codec [%s]",str);
+ return FALSE;
+ }
}
+
/* parse optional payload type */
str = apr_strtok(codec_desc_str, separator, &state);
if(str) {
int i;
mpf_codec_t *codec;
for(i=0; i<codec_manager->codec_arr->nelts; i++) {
- codec = ((mpf_codec_t**)codec_manager->codec_arr->elts)[i];
+ codec = APR_ARRAY_IDX(codec_manager->codec_arr,i,mpf_codec_t*);
if(apt_string_compare(&codec->attribs->name,codec_name) == TRUE) {
return codec;
}
* limitations under the License.
*/
+#ifdef WIN32
+#pragma warning(disable: 4127)
+#endif
+#include <apr_ring.h>
#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 "mpf_multiplier.h"
+#include "mpf_mixer.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);
+/** Item of the association matrix */
+typedef struct {
+ unsigned char on;
+} matrix_item_t;
-MPF_DECLARE(mpf_context_t*) mpf_context_create(void *obj, apr_size_t max_termination_count, apr_pool_t *pool)
+/** Item of the association matrix header */
+typedef struct {
+ mpf_termination_t *termination;
+ unsigned char tx_count;
+ unsigned char rx_count;
+} header_item_t;
+
+/** Media processing context */
+struct mpf_context_t {
+ /** Ring entry */
+ APR_RING_ENTRY(mpf_context_t) link;
+ /** Back pointer to the context factory */
+ mpf_context_factory_t *factory;
+ /** Pool to allocate memory from */
+ apr_pool_t *pool;
+ /** External object */
+ void *obj;
+
+ /** Max number of terminations in the context */
+ apr_size_t capacity;
+ /** Current number of terminations in the context */
+ apr_size_t count;
+ /** Header of the association matrix */
+ header_item_t *header;
+ /** Association matrix, which represents the topology */
+ matrix_item_t **matrix;
+
+ /** Array of media processing objects constructed while
+ applying topology based on association matrix */
+ apr_array_header_t *mpf_objects;
+};
+
+/** Factory of media contexts */
+struct mpf_context_factory_t {
+ /** Ring head */
+ APR_RING_HEAD(mpf_context_head_t, mpf_context_t) head;
+};
+
+
+static APR_INLINE apt_bool_t stream_direction_compatibility_check(mpf_termination_t *termination1, mpf_termination_t *termination2);
+static mpf_object_t* mpf_context_bridge_create(mpf_context_t *context, apr_size_t i);
+static mpf_object_t* mpf_context_multiplier_create(mpf_context_t *context, apr_size_t i);
+static mpf_object_t* mpf_context_mixer_create(mpf_context_t *context, apr_size_t j);
+
+
+MPF_DECLARE(mpf_context_factory_t*) mpf_context_factory_create(apr_pool_t *pool)
+{
+ mpf_context_factory_t *factory = apr_palloc(pool, sizeof(mpf_context_factory_t));
+ APR_RING_INIT(&factory->head, mpf_context_t, link);
+ return factory;
+}
+
+MPF_DECLARE(void) mpf_context_factory_destroy(mpf_context_factory_t *factory)
+{
+ mpf_context_t *context;
+ while(!APR_RING_EMPTY(&factory->head, mpf_context_t, link)) {
+ context = APR_RING_FIRST(&factory->head);
+ mpf_context_destroy(context);
+ APR_RING_REMOVE(context, link);
+ }
+}
+
+MPF_DECLARE(apt_bool_t) mpf_context_factory_process(mpf_context_factory_t *factory)
+{
+ mpf_context_t *context;
+ for(context = APR_RING_FIRST(&factory->head);
+ context != APR_RING_SENTINEL(&factory->head, mpf_context_t, link);
+ context = APR_RING_NEXT(context, link)) {
+
+ mpf_context_process(context);
+ }
+
+ return TRUE;
+}
+
+
+MPF_DECLARE(mpf_context_t*) mpf_context_create(
+ mpf_context_factory_t *factory,
+ void *obj,
+ apr_size_t max_termination_count,
+ apr_pool_t *pool)
{
apr_size_t i,j;
+ matrix_item_t *matrix_item;
+ header_item_t *header_item;
mpf_context_t *context = apr_palloc(pool,sizeof(mpf_context_t));
+ context->factory = factory;
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; i<max_termination_count; i++) {
- context->table[i] = apr_palloc(pool,sizeof(table_item_t)*max_termination_count);
- for(j=0; j<max_termination_count; j++) {
- context->table[i][j] = NULL;
+ context->capacity = max_termination_count;
+ context->count = 0;
+ context->mpf_objects = apr_array_make(pool,1,sizeof(mpf_object_t*));
+ context->header = apr_palloc(pool,context->capacity * sizeof(header_item_t));
+ context->matrix = apr_palloc(pool,context->capacity * sizeof(matrix_item_t*));
+ for(i=0; i<context->capacity; i++) {
+ header_item = &context->header[i];
+ header_item->termination = NULL;
+ header_item->tx_count = 0;
+ header_item->rx_count = 0;
+ context->matrix[i] = apr_palloc(pool,context->capacity * sizeof(matrix_item_t));
+ for(j=0; j<context->capacity; j++) {
+ matrix_item = &context->matrix[i][j];
+ matrix_item->on = 0;
}
}
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; i<count; i++){
- termination = context->table[i][i];
+ for(i=0; i<context->capacity; i++){
+ termination = context->header[i].termination;
if(termination) {
mpf_context_termination_subtract(context,termination);
- if(termination->audio_stream) {
- mpf_audio_stream_destroy(termination->audio_stream);
- }
+ mpf_termination_subtract(termination);
}
}
return TRUE;
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; i<count; i++) {
- if(!context->table[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;
+ header_item_t *header_item;
+ for(i=0; i<context->capacity; i++) {
+ header_item = &context->header[i];
+ if(header_item->termination) {
+ continue;
}
+ if(!context->count) {
+ apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Add Context");
+ APR_RING_INSERT_TAIL(&context->factory->head,context,mpf_context_t,link);
+ }
+
+ apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Add Termination");
+ header_item->termination = termination;
+ header_item->tx_count = 0;
+ header_item->rx_count = 0;
+
+ termination->slot = i;
+ context->count++;
+ return TRUE;
}
return FALSE;
}
MPF_DECLARE(apt_bool_t) mpf_context_termination_subtract(mpf_context_t *context, mpf_termination_t *termination)
{
+ header_item_t *header_item1;
+ header_item_t *header_item2;
+ matrix_item_t *item;
+ apr_size_t j,k;
apr_size_t i = termination->slot;
- if(i >= context->max_termination_count) {
+ if(i >= context->capacity) {
return FALSE;
}
- if(context->table[i][i] != termination) {
+ header_item1 = &context->header[i];
+ if(header_item1->termination != termination) {
return FALSE;
}
apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Subtract Termination");
- context->table[i][i] = NULL;
+ for(j=0,k=0; j<context->capacity && k<context->count; j++) {
+ header_item2 = &context->header[j];
+ if(!header_item2->termination) {
+ continue;
+ }
+ k++;
+
+ item = &context->matrix[i][j];
+ if(item->on) {
+ item->on = 0;
+ header_item1->tx_count--;
+ header_item2->rx_count--;
+ }
+
+ item = &context->matrix[j][i];
+ if(item->on) {
+ item->on = 0;
+ header_item2->tx_count--;
+ header_item1->rx_count--;
+ }
+ }
+ header_item1->termination = NULL;
+
termination->slot = (apr_size_t)-1;
- context->termination_count--;
+ context->count--;
+ if(!context->count) {
+ apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Remove Context");
+ APR_RING_REMOVE(context,link);
+ }
return TRUE;
}
-MPF_DECLARE(apt_bool_t) mpf_context_process(mpf_context_t *context)
+MPF_DECLARE(apt_bool_t) mpf_context_association_add(mpf_context_t *context, mpf_termination_t *termination1, mpf_termination_t *termination2)
{
- mpf_object_t *object;
- apr_size_t i,j;
- for(i=0; i<context->max_termination_count; i++) {
- for(j=0; j<context->max_termination_count; j++) {
- if(i==j) continue;
+ header_item_t *header_item1;
+ matrix_item_t *matrix_item1;
+ header_item_t *header_item2;
+ matrix_item_t *matrix_item2;
+ apr_size_t i = termination1->slot;
+ apr_size_t j = termination2->slot;
+ if(i >= context->capacity || j >= context->capacity) {
+ return FALSE;
+ }
- object = context->table[i][j];
- if(object && object->process) {
- object->process(object);
- }
+ header_item1 = &context->header[i];
+ header_item2 = &context->header[j];
+
+ if(header_item1->termination != termination1 || header_item2->termination != termination2) {
+ return FALSE;
+ }
+
+ matrix_item1 = &context->matrix[i][j];
+ matrix_item2 = &context->matrix[j][i];
+
+ /* 1 -> 2 */
+ if(!matrix_item1->on) {
+ if(stream_direction_compatibility_check(header_item1->termination,header_item2->termination) == TRUE) {
+ matrix_item1->on = 1;
+ header_item1->tx_count ++;
+ header_item2->rx_count ++;
+ }
+ }
+
+ /* 2 -> 1 */
+ if(!matrix_item2->on) {
+ if(stream_direction_compatibility_check(header_item2->termination,header_item1->termination) == TRUE) {
+ matrix_item2->on = 1;
+ header_item2->tx_count ++;
+ header_item1->rx_count ++;
}
}
return TRUE;
}
-MPF_DECLARE(apt_bool_t) mpf_context_topology_apply(mpf_context_t *context, mpf_termination_t *termination)
+MPF_DECLARE(apt_bool_t) mpf_context_association_remove(mpf_context_t *context, mpf_termination_t *termination1, mpf_termination_t *termination2)
{
- 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;
+ header_item_t *header_item1;
+ matrix_item_t *matrix_item1;
+ header_item_t *header_item2;
+ matrix_item_t *matrix_item2;
+ apr_size_t i = termination1->slot;
+ apr_size_t j = termination2->slot;
+ if(i >= context->capacity || j >= context->capacity) {
+ return FALSE;
}
- i = termination->slot;
- for(j=0; j<context->max_termination_count; j++) {
- if(i == j) continue;
+ header_item1 = &context->header[i];
+ header_item2 = &context->header[j];
- sink_termination = context->table[j][j];
- object = mpf_context_connection_create(context,termination,sink_termination);
- if(object) {
- context->table[i][j] = object;
- }
+ if(header_item1->termination != termination1 || header_item2->termination != termination2) {
+ return FALSE;
}
- j = termination->slot;
- for(i=0; i<context->max_termination_count; i++) {
- if(i == j) continue;
+ matrix_item1 = &context->matrix[i][j];
+ matrix_item2 = &context->matrix[j][i];
- source_termination = context->table[i][i];
- object = mpf_context_connection_create(context,source_termination,termination);
- if(object) {
- context->table[i][j] = object;
- }
+ /* 1 -> 2 */
+ if(matrix_item1->on == 1) {
+ matrix_item1->on = 0;
+ header_item1->tx_count --;
+ header_item2->rx_count --;
}
+ /* 2 -> 1 */
+ if(matrix_item2->on == 1) {
+ matrix_item2->on = 0;
+ header_item2->tx_count --;
+ header_item1->rx_count --;
+ }
return TRUE;
}
-MPF_DECLARE(apt_bool_t) mpf_context_topology_destroy(mpf_context_t *context, mpf_termination_t *termination)
+MPF_DECLARE(apt_bool_t) mpf_context_associations_reset(mpf_context_t *context)
{
- 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;
- }
+ apr_size_t i,j,k;
+ header_item_t *header_item1;
+ header_item_t *header_item2;
+ matrix_item_t *item;
+
+ /* destroy existing topology / if any */
+ mpf_context_topology_destroy(context);
- i = termination->slot;
- for(j=0; j<context->max_termination_count; j++) {
- if(i == j) continue;
+ /* reset assigned associations */
+ for(i=0,k=0; i<context->capacity && k<context->count; i++) {
+ header_item1 = &context->header[i];
+ if(!header_item1->termination) {
+ continue;
+ }
+ k++;
+
+ if(!header_item1->tx_count && !header_item1->rx_count) {
+ continue;
+ }
+
+ for(j=i; j<context->capacity; j++) {
+ header_item2 = &context->header[j];
+ if(!header_item2->termination) {
+ continue;
+ }
+
+ item = &context->matrix[i][j];
+ if(item->on) {
+ item->on = 0;
+ header_item1->tx_count--;
+ header_item2->rx_count--;
+ }
- object = context->table[i][j];
- if(object) {
- if(object->destroy) {
- object->destroy(object);
+ item = &context->matrix[j][i];
+ if(item->on) {
+ item->on = 0;
+ header_item2->tx_count--;
+ header_item1->rx_count--;
}
- context->table[i][j] = NULL;
}
}
+ return TRUE;
+}
- j = termination->slot;
- for(i=0; i<context->max_termination_count; i++) {
- if(i == j) continue;
+static apt_bool_t mpf_context_object_add(mpf_context_t *context, mpf_object_t *object)
+{
+ if(!object) {
+ return FALSE;
+ }
+
+ APR_ARRAY_PUSH(context->mpf_objects, mpf_object_t*) = object;
+#if 1
+ mpf_object_trace(object);
+#endif
+ return TRUE;
+}
+
+MPF_DECLARE(apt_bool_t) mpf_context_topology_apply(mpf_context_t *context)
+{
+ apr_size_t i,k;
+ header_item_t *header_item;
+ mpf_object_t *object;
+
+ /* first destroy existing topology / if any */
+ mpf_context_topology_destroy(context);
- object = context->table[i][j];
- if(object) {
- if(object->destroy) {
- object->destroy(object);
+ for(i=0,k=0; i<context->capacity && k<context->count; i++) {
+ header_item = &context->header[i];
+ if(!header_item->termination) {
+ continue;
+ }
+ k++;
+
+ if(header_item->tx_count > 0) {
+ object = NULL;
+ if(header_item->tx_count == 1) {
+ object = mpf_context_bridge_create(context,i);
}
- context->table[i][j] = NULL;
+ else { /* tx_count > 1 */
+ object = mpf_context_multiplier_create(context,i);
+ }
+
+ mpf_context_object_add(context,object);
+ }
+ if(header_item->rx_count > 1) {
+ object = mpf_context_mixer_create(context,i);
+ mpf_context_object_add(context,object);
}
}
+
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_DECLARE(apt_bool_t) mpf_context_topology_destroy(mpf_context_t *context)
{
- mpf_object_t *object = NULL;
- mpf_audio_stream_t *source;
- mpf_audio_stream_t *sink;
- if(!src_termination || !sink_termination) {
- return NULL;
+ if(context->mpf_objects->nelts) {
+ int i;
+ mpf_object_t *object;
+ for(i=0; i<context->mpf_objects->nelts; i++) {
+ object = APR_ARRAY_IDX(context->mpf_objects,i,mpf_object_t*);
+ mpf_object_destroy(object);
+ }
+ apr_array_clear(context->mpf_objects);
}
- 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->descriptor->sampling_rate != tx_codec->descriptor->sampling_rate) {
- apt_log(APT_LOG_MARK,APT_PRIO_WARNING,
- "Resampling is not supported now. "
- "Try to configure and use the same sampling rate on both ends");
- return NULL;
- }
- if(rx_codec->vtable && rx_codec->vtable->decode) {
- /* set decoder before bridge */
- mpf_audio_stream_t *decoder = mpf_decoder_create(source,context->pool);
- source = decoder;
- }
- if(tx_codec->vtable && tx_codec->vtable->encode) {
- /* 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 TRUE;
+}
+
+MPF_DECLARE(apt_bool_t) mpf_context_process(mpf_context_t *context)
+{
+ int i;
+ mpf_object_t *object;
+ for(i=0; i<context->mpf_objects->nelts; i++) {
+ object = APR_ARRAY_IDX(context->mpf_objects,i,mpf_object_t*);
+ if(object && object->process) {
+ object->process(object);
}
}
- return object;
+ return TRUE;
+}
+
+
+static mpf_object_t* mpf_context_bridge_create(mpf_context_t *context, apr_size_t i)
+{
+ header_item_t *header_item1 = &context->header[i];
+ header_item_t *header_item2;
+ matrix_item_t *item;
+ apr_size_t j;
+ for(j=0; j<context->capacity; j++) {
+ header_item2 = &context->header[j];
+ if(!header_item2->termination) {
+ continue;
+ }
+ item = &context->matrix[i][j];
+ if(!item->on) {
+ continue;
+ }
+
+ if(header_item2->rx_count > 1) {
+ /* mixer will be created instead */
+ return NULL;
+ }
+
+ /* create bridge i -> j */
+ if(header_item1->termination && header_item2->termination) {
+ return mpf_bridge_create(
+ header_item1->termination->audio_stream,
+ header_item2->termination->audio_stream,
+ header_item1->termination->codec_manager,
+ context->pool);
+ }
+ }
+ return NULL;
+}
+
+static mpf_object_t* mpf_context_multiplier_create(mpf_context_t *context, apr_size_t i)
+{
+ mpf_audio_stream_t **sink_arr;
+ header_item_t *header_item1 = &context->header[i];
+ header_item_t *header_item2;
+ matrix_item_t *item;
+ apr_size_t j,k;
+ sink_arr = apr_palloc(context->pool,header_item1->tx_count * sizeof(mpf_audio_stream_t*));
+ for(j=0,k=0; j<context->capacity && k<header_item1->tx_count; j++) {
+ header_item2 = &context->header[j];
+ if(!header_item2->termination) {
+ continue;
+ }
+ item = &context->matrix[i][j];
+ if(!item->on) {
+ continue;
+ }
+ sink_arr[k] = header_item2->termination->audio_stream;
+ k++;
+ }
+ return mpf_multiplier_create(
+ header_item1->termination->audio_stream,
+ sink_arr,
+ header_item1->tx_count,
+ header_item1->termination->codec_manager,
+ context->pool);
+}
+
+static mpf_object_t* mpf_context_mixer_create(mpf_context_t *context, apr_size_t j)
+{
+ mpf_audio_stream_t **source_arr;
+ header_item_t *header_item1 = &context->header[j];
+ header_item_t *header_item2;
+ matrix_item_t *item;
+ apr_size_t i,k;
+ source_arr = apr_palloc(context->pool,header_item1->rx_count * sizeof(mpf_audio_stream_t*));
+ for(i=0,k=0; i<context->capacity && k<header_item1->rx_count; i++) {
+ header_item2 = &context->header[i];
+ if(!header_item2->termination) {
+ continue;
+ }
+ item = &context->matrix[i][j];
+ if(!item->on) {
+ continue;
+ }
+ source_arr[k] = header_item2->termination->audio_stream;
+ k++;
+ }
+ return mpf_mixer_create(
+ source_arr,
+ header_item1->rx_count,
+ header_item1->termination->audio_stream,
+ header_item1->termination->codec_manager,
+ context->pool);
+}
+
+static APR_INLINE apt_bool_t stream_direction_compatibility_check(mpf_termination_t *termination1, mpf_termination_t *termination2)
+{
+ mpf_audio_stream_t *source = termination1->audio_stream;
+ mpf_audio_stream_t *sink = termination2->audio_stream;
+ if(source && (source->direction & STREAM_DIRECTION_RECEIVE) == STREAM_DIRECTION_RECEIVE &&
+ sink && (sink->direction & STREAM_DIRECTION_SEND) == STREAM_DIRECTION_SEND) {
+ return TRUE;
+ }
+ return FALSE;
}
struct mpf_decoder_t {
mpf_audio_stream_t *base;
mpf_audio_stream_t *source;
+ mpf_codec_t *codec;
mpf_frame_t frame_in;
};
return mpf_audio_stream_destroy(decoder->source);
}
-static apt_bool_t mpf_decoder_open(mpf_audio_stream_t *stream)
+static apt_bool_t mpf_decoder_open(mpf_audio_stream_t *stream, mpf_codec_t *codec)
{
mpf_decoder_t *decoder = stream->obj;
- return mpf_audio_stream_rx_open(decoder->source);
+ return mpf_audio_stream_rx_open(decoder->source,decoder->codec);
}
static apt_bool_t mpf_decoder_close(mpf_audio_stream_t *stream)
static apt_bool_t mpf_decoder_process(mpf_audio_stream_t *stream, mpf_frame_t *frame)
{
mpf_decoder_t *decoder = stream->obj;
+ decoder->frame_in.type = MEDIA_FRAME_TYPE_NONE;
+ decoder->frame_in.marker = MPF_MARKER_NONE;
if(mpf_audio_stream_frame_read(decoder->source,&decoder->frame_in) != TRUE) {
return FALSE;
}
frame->type = decoder->frame_in.type;
+ frame->marker = decoder->frame_in.marker;
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);
+ mpf_codec_decode(decoder->codec,&decoder->frame_in.codec_frame,&frame->codec_frame);
}
return TRUE;
}
+static void mpf_decoder_trace(mpf_audio_stream_t *stream, mpf_stream_direction_e direction, apt_text_stream_t *output)
+{
+ apr_size_t offset;
+ mpf_codec_descriptor_t *descriptor;
+ mpf_decoder_t *decoder = stream->obj;
+
+ mpf_audio_stream_trace(decoder->source,direction,output);
+
+ descriptor = decoder->base->rx_descriptor;
+ if(descriptor) {
+ offset = output->pos - output->text.buf;
+ output->pos += apr_snprintf(output->pos, output->text.length - offset,
+ "->Decoder->[%s/%d/%d]",
+ descriptor->name.buf,
+ descriptor->sampling_rate,
+ descriptor->channel_count);
+ }
+}
static const mpf_audio_stream_vtable_t vtable = {
mpf_decoder_destroy,
mpf_decoder_process,
NULL,
NULL,
- NULL
+ NULL,
+ mpf_decoder_trace
};
-MPF_DECLARE(mpf_audio_stream_t*) mpf_decoder_create(mpf_audio_stream_t *source, apr_pool_t *pool)
+MPF_DECLARE(mpf_audio_stream_t*) mpf_decoder_create(mpf_audio_stream_t *source, mpf_codec_t *codec, apr_pool_t *pool)
{
apr_size_t frame_size;
- mpf_codec_t *codec;
mpf_decoder_t *decoder;
- if(!source || !source->rx_codec) {
+ mpf_stream_capabilities_t *capabilities;
+ if(!source || !codec) {
return NULL;
}
decoder = apr_palloc(pool,sizeof(mpf_decoder_t));
- decoder->base = mpf_audio_stream_create(decoder,&vtable,STREAM_MODE_RECEIVE,pool);
+ capabilities = mpf_stream_capabilities_create(STREAM_DIRECTION_RECEIVE,pool);
+ decoder->base = mpf_audio_stream_create(decoder,&vtable,capabilities,pool);
+ if(!decoder->base) {
+ return NULL;
+ }
+ decoder->base->rx_descriptor = mpf_codec_lpcm_descriptor_create(
+ source->rx_descriptor->sampling_rate,
+ source->rx_descriptor->channel_count,
+ pool);
+ decoder->base->rx_event_descriptor = source->rx_event_descriptor;
+
decoder->source = source;
+ decoder->codec = codec;
- codec = source->rx_codec;
- frame_size = mpf_codec_frame_size_calculate(codec->descriptor,codec->attribs);
- decoder->base->rx_codec = codec;
+ frame_size = mpf_codec_frame_size_calculate(source->rx_descriptor,codec->attribs);
decoder->frame_in.codec_frame.size = frame_size;
decoder->frame_in.codec_frame.buffer = apr_palloc(pool,frame_size);
return decoder->base;
--- /dev/null
+/*
+ * Copyright 2009 Tomas Valenta, 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_dtmf_detector.h"
+#include "apr_thread_mutex.h"
+#include "apt_log.h"
+#include "mpf_named_event.h"
+#include <math.h>
+
+#ifndef M_PI
+# define M_PI 3.141592653589793238462643
+#endif
+
+/** Max detected DTMF digits buffer length */
+#define MPF_DTMFDET_BUFFER_LEN 32
+
+/** Number of DTMF frequencies */
+#define DTMF_FREQUENCIES 8
+
+/** Window length in samples (at 8kHz) for Goertzel's frequency analysis */
+#define GOERTZEL_SAMPLES_8K 102
+
+/** See RFC4733 */
+#define DTMF_EVENT_ID_MAX 15 /* 0123456789*#ABCD */
+
+/**
+ * Goertzel frequency detector (second-order IIR filter) state:
+ *
+ * s(t) = x(t) + coef * s(t-1) - s(t-2), where s(0)=0; s(1) = 0;
+ * x(t) is the input signal
+ *
+ * Then energy of frequency f in the signal is:
+ * X(f)X'(f) = s(t-2)^2 + s(t-1)^2 - coef*s(t-2)*s(t-1)
+ */
+typedef struct goertzel_state_t {
+ /** coef = cos(2*pi*f_tone/f_sampling) */
+ double coef;
+ /** s(t-2) or resulting energy @see goertzel_state_t */
+ double s1;
+ /** s(t-1) @see goertzel_state_t */
+ double s2;
+} goertzel_state_t;
+
+/** DTMF frequencies */
+static const double dtmf_freqs[DTMF_FREQUENCIES] = {
+ 697, 770, 852, 941, /* Row frequencies */
+ 1209, 1336, 1477, 1633}; /* Col frequencies */
+
+/** [row, col] major frequency to digit mapping */
+static const char freq2digits[DTMF_FREQUENCIES/2][DTMF_FREQUENCIES/2] =
+ { { '1', '2', '3', 'A' },
+ { '4', '5', '6', 'B' },
+ { '7', '8', '9', 'C' },
+ { '*', '0', '#', 'D' } };
+
+/** Media Processing Framework's Dual Tone Multiple Frequncy detector */
+struct mpf_dtmf_detector_t {
+ /** Mutex to guard the buffer */
+ struct apr_thread_mutex_t *mutex;
+ /** Recognizer band */
+ enum mpf_dtmf_detector_band_e band;
+ /** Detected digits buffer */
+ char buf[MPF_DTMFDET_BUFFER_LEN+1];
+ /** Number of digits in the buffer */
+ apr_size_t digits;
+ /** Number of lost digits due to full buffer */
+ apr_size_t lost_digits;
+ /** Frequency analyzators */
+ struct goertzel_state_t energies[DTMF_FREQUENCIES];
+ /** Total energy of signal */
+ double totenergy;
+ /** Number of samples in a window */
+ apr_size_t wsamples;
+ /** Number of samples processed */
+ apr_size_t nsamples;
+ /** Previously detected and last reported digits */
+ char last1, last2, curr;
+};
+
+
+MPF_DECLARE(struct mpf_dtmf_detector_t *) mpf_dtmf_detector_create_ex(
+ const struct mpf_audio_stream_t *stream,
+ enum mpf_dtmf_detector_band_e band,
+ struct apr_pool_t *pool)
+{
+ apr_status_t status;
+ struct mpf_dtmf_detector_t *det;
+ int flg_band = band;
+
+ if (!stream->tx_descriptor) flg_band &= ~MPF_DTMF_DETECTOR_INBAND;
+/*
+ Event descriptor is not important actually
+ if (!stream->tx_event_descriptor) flg_band &= ~MPF_DTMF_DETECTOR_OUTBAND;
+*/
+ if (!flg_band) return NULL;
+
+ det = apr_palloc(pool, sizeof(mpf_dtmf_detector_t));
+ if (!det) return NULL;
+ status = apr_thread_mutex_create(&det->mutex, APR_THREAD_MUTEX_DEFAULT, pool);
+ if (status != APR_SUCCESS) return NULL;
+
+ det->band = (enum mpf_dtmf_detector_band_e) flg_band;
+ det->buf[0] = 0;
+ det->digits = 0;
+ det->lost_digits = 0;
+
+ if (det->band & MPF_DTMF_DETECTOR_INBAND) {
+ apr_size_t i;
+ for (i = 0; i < DTMF_FREQUENCIES; i++) {
+ det->energies[i].coef = 2 * cos(2 * M_PI * dtmf_freqs[i] /
+ stream->tx_descriptor->sampling_rate);
+ det->energies[i].s1 = 0;
+ det->energies[i].s2 = 0;
+ }
+ det->nsamples = 0;
+ det->wsamples = GOERTZEL_SAMPLES_8K * (stream->tx_descriptor->sampling_rate / 8000);
+ det->last1 = det->last2 = det->curr = 0;
+ det->totenergy = 0;
+ }
+
+ return det;
+}
+
+MPF_DECLARE(char) mpf_dtmf_detector_digit_get(struct mpf_dtmf_detector_t *detector)
+{
+ char digit;
+ apr_thread_mutex_lock(detector->mutex);
+ digit = detector->buf[0];
+ if (digit) {
+ strcpy(detector->buf, detector->buf + 1);
+ detector->digits--;
+ }
+ apr_thread_mutex_unlock(detector->mutex);
+ return digit;
+}
+
+MPF_DECLARE(apr_size_t) mpf_dtmf_detector_digits_lost(const struct mpf_dtmf_detector_t *detector)
+{
+ return detector->lost_digits;
+}
+
+MPF_DECLARE(void) mpf_dtmf_detector_reset(struct mpf_dtmf_detector_t *detector)
+{
+ apr_thread_mutex_lock(detector->mutex);
+ detector->buf[0] = 0;
+ detector->lost_digits = 0;
+ detector->digits = 0;
+ detector->curr = detector->last1 = detector->last2 = 0;
+ detector->nsamples = 0;
+ detector->totenergy = 0;
+ apr_thread_mutex_unlock(detector->mutex);
+}
+
+static APR_INLINE void mpf_dtmf_detector_add_digit(
+ struct mpf_dtmf_detector_t *detector,
+ char digit)
+{
+ if (!digit) return;
+ apr_thread_mutex_lock(detector->mutex);
+ if (detector->digits < MPF_DTMFDET_BUFFER_LEN) {
+ detector->buf[detector->digits++] = digit;
+ detector->buf[detector->digits] = 0;
+ } else
+ detector->lost_digits++;
+ apr_thread_mutex_unlock(detector->mutex);
+}
+
+static APR_INLINE void goertzel_sample(
+ struct mpf_dtmf_detector_t *detector,
+ apr_int16_t sample)
+{
+ apr_size_t i;
+ double s;
+ for (i = 0; i < DTMF_FREQUENCIES; i++) {
+ s = detector->energies[i].s1;
+ detector->energies[i].s1 = detector->energies[i].s2;
+ detector->energies[i].s2 = sample +
+ detector->energies[i].coef * detector->energies[i].s1 - s;
+ }
+ detector->totenergy += sample * sample;
+}
+
+static void goertzel_energies_digit(struct mpf_dtmf_detector_t *detector)
+{
+ apr_size_t i, rmax = 0, cmax = 0;
+ double reng = 0, ceng = 0;
+ char digit = 0;
+
+ /* Calculate energies and maxims */
+ for (i = 0; i < DTMF_FREQUENCIES; i++) {
+ double eng = detector->energies[i].s1 * detector->energies[i].s1 +
+ detector->energies[i].s2 * detector->energies[i].s2 -
+ detector->energies[i].coef * detector->energies[i].s1 * detector->energies[i].s2;
+ if (i < DTMF_FREQUENCIES/2) {
+ if (eng > reng) {
+ rmax = i;
+ reng = eng;
+ }
+ } else {
+ if (eng > ceng) {
+ cmax = i;
+ ceng = eng;
+ }
+ }
+ }
+
+ if ((reng < 8.0e8 * detector->wsamples / GOERTZEL_SAMPLES_8K) ||
+ (ceng < 8.0e8 * detector->wsamples / GOERTZEL_SAMPLES_8K))
+ {
+ /* energy not high enough */
+ } else if ((ceng > reng) && (reng < ceng * 0.398)) { /* twist > 4dB, error */
+ /* Twist check
+ * CEPT => twist < 6dB
+ * AT&T => forward twist < 4dB and reverse twist < 8dB
+ * -ndB < 10 log10( v1 / v2 ), where v1 < v2
+ * -4dB < 10 log10( v1 / v2 )
+ * -0.4 < log10( v1 / v2 )
+ * 0.398 < v1 / v2
+ * 0.398 * v2 < v1
+ */
+ } else if ((ceng < reng) && (ceng < reng * 0.158)) { /* twist > 8db, error */
+ /* Reverse twist check failed */
+ } else if (0.025 * detector->totenergy > (reng + ceng)) { /* 16db */
+ /* Signal energy to total energy ratio test failed */
+ } else {
+ digit = freq2digits[rmax][cmax - DTMF_FREQUENCIES/2];
+ }
+
+ /* Three successive detections will trigger the detection */
+ if (digit != detector->curr) {
+ if (digit && ((detector->last1 == digit) && (detector->last2 == digit))) {
+ detector->curr = digit;
+ mpf_dtmf_detector_add_digit(detector, digit);
+ } else if ((detector->last1 != detector->curr) && (detector->last2 != detector->curr)) {
+ detector->curr = 0;
+ }
+ }
+ detector->last1 = detector->last2;
+ detector->last2 = digit;
+
+ /* Reset Goertzel's detectors */
+ for (i = 0; i < DTMF_FREQUENCIES; i++) {
+ detector->energies[i].s1 = 0;
+ detector->energies[i].s2 = 0;
+ }
+ detector->totenergy = 0;
+}
+
+MPF_DECLARE(void) mpf_dtmf_detector_get_frame(
+ struct mpf_dtmf_detector_t *detector,
+ const struct mpf_frame_t *frame)
+{
+ if ((detector->band & MPF_DTMF_DETECTOR_OUTBAND) &&
+ (frame->type & MEDIA_FRAME_TYPE_EVENT) &&
+ (frame->event_frame.event_id <= DTMF_EVENT_ID_MAX) &&
+ (frame->marker == MPF_MARKER_START_OF_EVENT))
+ {
+ if (detector->band & MPF_DTMF_DETECTOR_INBAND) {
+ detector->band &= ~MPF_DTMF_DETECTOR_INBAND;
+ apt_log(APT_LOG_MARK, APT_PRIO_INFO, "Out-of-band digit arrived, turning "
+ "in-band DTMF detector off");
+ }
+ mpf_dtmf_detector_add_digit(detector, mpf_event_id_to_dtmf_char(
+ frame->event_frame.event_id));
+ return;
+ }
+
+ if ((detector->band & MPF_DTMF_DETECTOR_INBAND) && (frame->type & MEDIA_FRAME_TYPE_AUDIO)) {
+ apr_int16_t *samples = frame->codec_frame.buffer;
+ apr_size_t i;
+
+ for (i = 0; i < frame->codec_frame.size / 2; i++) {
+ goertzel_sample(detector, samples[i]);
+ if (++detector->nsamples >= detector->wsamples) {
+ goertzel_energies_digit(detector);
+ detector->nsamples = 0;
+ }
+ }
+ }
+}
+
+MPF_DECLARE(void) mpf_dtmf_detector_destroy(struct mpf_dtmf_detector_t *detector)
+{
+ apr_thread_mutex_destroy(detector->mutex);
+ detector->mutex = NULL;
+}
--- /dev/null
+/*
+ * Copyright 2009 Tomas Valenta, 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_dtmf_generator.h"
+#include "apr.h"
+#include "apr_thread_mutex.h"
+#include "apt_log.h"
+#include "mpf_named_event.h"
+#include <math.h>
+
+#ifndef M_PI
+# define M_PI 3.141592653589793238462643
+#endif
+
+/** Max DTMF digits waiting to be sent */
+#define MPF_DTMFGEN_QUEUE_LEN 32
+
+/** See RFC4733 */
+#define DTMF_EVENT_ID_MAX 15 /* 0123456789*#ABCD */
+
+/** See RFC4733 */
+#define DTMF_EVENT_VOLUME 10
+
+/** Amplitude of single sine wave from tone generator */
+#define DTMF_SINE_AMPLITUDE 12288
+
+/** State of the DTMF generator */
+typedef enum mpf_dtmf_generator_state_e {
+ /** Ready to generate next digit in queue */
+ DTMF_GEN_STATE_IDLE,
+ /** Generating tone */
+ DTMF_GEN_STATE_TONE,
+ /** Retransmitting final RTP packet */
+ DTMF_GEN_STATE_ENDING,
+ /** Generating silence between tones */
+ DTMF_GEN_STATE_SILENCE
+} mpf_dtmf_generator_state_e;
+
+/**
+ * Sine wave generator (second-order IIR filter) state:
+ *
+ * s(t) = Amp*sin(2*pi*f_tone/f_sampling*t)
+ *
+ * s(t) = coef * s(t-1) - s(t-2); s(0)=0; s(1)=Amp*sin(2*pi*f_tone/f_sampling)
+ */
+typedef struct sine_state_t {
+ /** coef = cos(2*pi*f_tone/f_sampling) */
+ double coef;
+ /** s(t-2) @see sine_state_t */
+ double s1;
+ /** s(t-1) @see sine_state_t */
+ double s2;
+} sine_state_t;
+
+/** Mapping event_it to frequency pair */
+static const double dtmf_freq[DTMF_EVENT_ID_MAX+1][2] = {
+ {941, 1336}, /* 0 */
+ {697, 1209}, /* 1 */
+ {697, 1336}, /* 2 */
+ {697, 1477}, /* 3 */
+ {770, 1209}, /* 4 */
+ {770, 1336}, /* 5 */
+ {770, 1477}, /* 6 */
+ {852, 1209}, /* 7 */
+ {852, 1336}, /* 8 */
+ {852, 1477}, /* 9 */
+ {941, 1209}, /* * */
+ {941, 1477}, /* # */
+ {697, 1633}, /* A */
+ {770, 1633}, /* B */
+ {852, 1633}, /* C */
+ {941, 1633} /* D */
+};
+
+/** Media Processing Framework's Dual Tone Multiple Frequncy generator */
+struct mpf_dtmf_generator_t {
+ /** Generator state */
+ enum mpf_dtmf_generator_state_e state;
+ /** In-band or out-of-band */
+ enum mpf_dtmf_generator_band_e band;
+ /** Mutex to guard the queue */
+ struct apr_thread_mutex_t *mutex;
+ /** Queue of digits to generate */
+ char queue[MPF_DTMFGEN_QUEUE_LEN+1];
+ /** DTMF event_id according to RFC4733 */
+ apr_byte_t event_id;
+ /** Duration in RTP units: (sample_rate / 1000) * milliseconds */
+ apr_uint32_t tone_duration;
+ /** Duration of inter-digit silence @see tone_duration */
+ apr_uint32_t silence_duration;
+ /** Multipurpose counter; mostly in RTP time units */
+ apr_uint32_t counter;
+ /** Frame duration in RTP units */
+ apr_uint32_t frame_duration;
+ /** RTP named event duration (0..0xFFFF) */
+ apr_uint32_t event_duration;
+ /** Set MPF_MARKER_NEW_SEGMENT in the next event frame */
+ apt_bool_t new_segment;
+ /** Lower frequency generator */
+ struct sine_state_t sine1;
+ /** Higher frequency generator */
+ struct sine_state_t sine2;
+ /** Sampling rate of audio in Hz; used in tone generator */
+ apr_size_t sample_rate_audio;
+ /** Sampling rate of telephone-events in Hz; used for timing */
+ apr_size_t sample_rate_events;
+ /** How often to issue event packet */
+ apr_size_t events_ptime;
+ /** Milliseconds elapsed since last event packet */
+ apr_size_t since_last_event;
+};
+
+
+MPF_DECLARE(struct mpf_dtmf_generator_t *) mpf_dtmf_generator_create_ex(
+ const struct mpf_audio_stream_t *stream,
+ enum mpf_dtmf_generator_band_e band,
+ apr_size_t tone_ms,
+ apr_size_t silence_ms,
+ struct apr_pool_t *pool)
+{
+ struct mpf_dtmf_generator_t *gen;
+ apr_status_t status;
+ int flg_band = band;
+
+ if (!stream->rx_descriptor) flg_band &= ~MPF_DTMF_GENERATOR_INBAND;
+ if (!stream->rx_event_descriptor) flg_band &= ~MPF_DTMF_GENERATOR_OUTBAND;
+ if (!flg_band) return NULL;
+
+ gen = apr_palloc(pool, sizeof(struct mpf_dtmf_generator_t));
+ if (!gen) return NULL;
+ status = apr_thread_mutex_create(&gen->mutex, APR_THREAD_MUTEX_DEFAULT, pool);
+ if (status != APR_SUCCESS) return NULL;
+ gen->band = (enum mpf_dtmf_generator_band_e) flg_band;
+ gen->queue[0] = 0;
+ gen->state = DTMF_GEN_STATE_IDLE;
+ if (stream->rx_descriptor)
+ gen->sample_rate_audio = stream->rx_descriptor->sampling_rate;
+ gen->sample_rate_events = stream->rx_event_descriptor ?
+ stream->rx_event_descriptor->sampling_rate : gen->sample_rate_audio;
+ gen->frame_duration = gen->sample_rate_events / 1000 * CODEC_FRAME_TIME_BASE;
+ gen->tone_duration = gen->sample_rate_events / 1000 * tone_ms;
+ gen->silence_duration = gen->sample_rate_events / 1000 * silence_ms;
+ gen->events_ptime = CODEC_FRAME_TIME_BASE; /* Should be got from event_descriptor */
+ return gen;
+}
+
+
+MPF_DECLARE(apt_bool_t) mpf_dtmf_generator_enqueue(
+ struct mpf_dtmf_generator_t *generator,
+ const char *digits)
+{
+ apr_size_t qlen, dlen;
+ apt_bool_t ret;
+
+ dlen = strlen(digits);
+ apr_thread_mutex_lock(generator->mutex);
+ qlen = strlen(generator->queue);
+ if (qlen + dlen > MPF_DTMFGEN_QUEUE_LEN) {
+ ret = FALSE;
+ apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "DTMF queue too short (%d), "
+ "cannot add %d digit%s, already has %d", MPF_DTMFGEN_QUEUE_LEN,
+ dlen, dlen > 1 ? "s" : "", qlen);
+ } else {
+ strcpy(generator->queue + qlen, digits);
+ ret = TRUE;
+ }
+ apr_thread_mutex_unlock(generator->mutex);
+ return ret;
+}
+
+
+MPF_DECLARE(void) mpf_dtmf_generator_reset(struct mpf_dtmf_generator_t *generator)
+{
+ apr_thread_mutex_lock(generator->mutex);
+ generator->state = DTMF_GEN_STATE_IDLE;
+ generator->queue[0] = 0;
+ apr_thread_mutex_unlock(generator->mutex);
+}
+
+
+MPF_DECLARE(apt_bool_t) mpf_dtmf_generator_sending(const struct mpf_dtmf_generator_t *generator)
+{
+ return *generator->queue || ((generator->state != DTMF_GEN_STATE_IDLE) &&
+ (generator->state != DTMF_GEN_STATE_SILENCE));
+}
+
+
+MPF_DECLARE(apt_bool_t) mpf_dtmf_generator_put_frame(
+ struct mpf_dtmf_generator_t *generator,
+ struct mpf_frame_t *frame)
+{
+ apr_thread_mutex_lock(generator->mutex);
+ if ((generator->state == DTMF_GEN_STATE_IDLE) && *generator->queue) {
+ /* Get next valid digit from queue */
+ do {
+ generator->event_id = (apr_byte_t) mpf_dtmf_char_to_event_id(*generator->queue);
+ strcpy(generator->queue, generator->queue + 1);
+ } while (*generator->queue && (generator->event_id > DTMF_EVENT_ID_MAX));
+ /* Reset state */
+ if (generator->event_id <= DTMF_EVENT_ID_MAX) {
+ generator->state = DTMF_GEN_STATE_TONE;
+ generator->counter = 0;
+ generator->event_duration = 0;
+ generator->since_last_event = generator->events_ptime;
+ generator->new_segment = FALSE;
+ /* Initialize tone generator */
+ if (generator->band & MPF_DTMF_GENERATOR_INBAND) {
+ double omega;
+
+ omega = 2 * M_PI * dtmf_freq[generator->event_id][0] / generator->sample_rate_audio;
+ generator->sine1.s1 = 0;
+ generator->sine1.s2 = DTMF_SINE_AMPLITUDE * sin(omega);
+ generator->sine1.coef = 2 * cos(omega);
+
+ omega = 2 * M_PI * dtmf_freq[generator->event_id][1] / generator->sample_rate_audio;
+ generator->sine2.s1 = 0;
+ generator->sine2.s2 = DTMF_SINE_AMPLITUDE * sin(omega);
+ generator->sine2.coef = 2 * cos(omega);
+ }
+ }
+ }
+ apr_thread_mutex_unlock(generator->mutex);
+ if (generator->state == DTMF_GEN_STATE_IDLE) return FALSE;
+
+ if (generator->state == DTMF_GEN_STATE_TONE) {
+ generator->counter += generator->frame_duration;
+ generator->event_duration += generator->frame_duration;
+ if (generator->band & MPF_DTMF_GENERATOR_INBAND) {
+ apr_size_t i;
+ apr_int16_t *samples = (apr_int16_t *) frame->codec_frame.buffer;
+ double s;
+
+ frame->type |= MEDIA_FRAME_TYPE_AUDIO;
+ /* Tone generator */
+ for (i = 0; i < frame->codec_frame.size / 2; i++) {
+ s = generator->sine1.s1;
+ generator->sine1.s1 = generator->sine1.s2;
+ generator->sine1.s2 = generator->sine1.coef * generator->sine1.s1 - s;
+ samples[i] = (apr_int16_t) (s + generator->sine2.s1);
+ s = generator->sine2.s1;
+ generator->sine2.s1 = generator->sine2.s2;
+ generator->sine2.s2 = generator->sine2.coef * generator->sine2.s1 - s;
+ }
+ }
+ if (generator->band & MPF_DTMF_GENERATOR_OUTBAND) {
+ generator->since_last_event += CODEC_FRAME_TIME_BASE;
+ if (generator->since_last_event >= generator->events_ptime)
+ generator->since_last_event = 0;
+ else
+ return TRUE;
+ frame->type |= MEDIA_FRAME_TYPE_EVENT;
+ frame->event_frame.reserved = 0;
+ frame->event_frame.event_id = generator->event_id;
+ frame->event_frame.volume = DTMF_EVENT_VOLUME;
+ if (generator->counter >= generator->tone_duration) {
+ generator->state = DTMF_GEN_STATE_ENDING;
+ generator->counter = 0;
+ frame->event_frame.edge = 1;
+ frame->marker = MPF_MARKER_END_OF_EVENT;
+ if (generator->event_duration > 0xFFFF) {
+ /* Shorten the tone a bit instead of lenghtening */
+ generator->new_segment = TRUE;
+ frame->event_frame.duration = 0xFFFF;
+ generator->event_duration = 0;
+ } else
+ frame->event_frame.duration = generator->event_duration;
+ } else {
+ frame->event_frame.edge = 0;
+ if (generator->counter == generator->frame_duration) /* First chunk of event */
+ frame->marker = MPF_MARKER_START_OF_EVENT;
+ else if (generator->new_segment) {
+ frame->marker = MPF_MARKER_NEW_SEGMENT;
+ generator->new_segment = FALSE;
+ } else
+ frame->marker = MPF_MARKER_NONE;
+ if (generator->event_duration > 0xFFFF) {
+ frame->event_frame.duration = 0xFFFF;
+ generator->event_duration = 0;
+ generator->new_segment = TRUE;
+ } else
+ frame->event_frame.duration = generator->event_duration;
+ }
+ return TRUE;
+ } /* MPF_DTMF_GENERATOR_OUTBAND */
+ if (generator->counter >= generator->tone_duration) {
+ generator->state = DTMF_GEN_STATE_SILENCE;
+ generator->counter = 0;
+ }
+ return TRUE;
+ }
+ else if (generator->state == DTMF_GEN_STATE_ENDING) {
+ generator->since_last_event += CODEC_FRAME_TIME_BASE;
+ if (generator->since_last_event >= generator->events_ptime)
+ generator->since_last_event = 0;
+ else
+ return TRUE;
+ generator->counter++;
+ frame->type |= MEDIA_FRAME_TYPE_EVENT;
+ frame->marker = MPF_MARKER_END_OF_EVENT;
+ frame->event_frame.event_id = generator->event_id;
+ frame->event_frame.volume = DTMF_EVENT_VOLUME;
+ frame->event_frame.reserved = 0;
+ frame->event_frame.edge = 1;
+ if (generator->new_segment)
+ /* Tone was shortened a little bit */
+ frame->event_frame.duration = 0xFFFF;
+ else
+ frame->event_frame.duration = generator->event_duration;
+ if (generator->counter >= 2) {
+ generator->state = DTMF_GEN_STATE_SILENCE;
+ generator->counter *= generator->frame_duration;
+ }
+ if (generator->band & MPF_DTMF_GENERATOR_INBAND) {
+ frame->type |= MEDIA_FRAME_TYPE_AUDIO;
+ memset(frame->codec_frame.buffer, 0, frame->codec_frame.size);
+ }
+ return TRUE;
+ }
+ else if (generator->state == DTMF_GEN_STATE_SILENCE) {
+ generator->counter += generator->frame_duration;
+ if (generator->counter >= generator->silence_duration)
+ generator->state = DTMF_GEN_STATE_IDLE;
+ }
+
+ return FALSE;
+}
+
+
+MPF_DECLARE(void) mpf_dtmf_generator_destroy(struct mpf_dtmf_generator_t *generator)
+{
+ mpf_dtmf_generator_reset(generator);
+ apr_thread_mutex_destroy(generator->mutex);
+ generator->mutex = NULL;
+}
struct mpf_encoder_t {
mpf_audio_stream_t *base;
mpf_audio_stream_t *sink;
+ mpf_codec_t *codec;
mpf_frame_t frame_out;
};
return mpf_audio_stream_destroy(encoder->sink);
}
-static apt_bool_t mpf_encoder_open(mpf_audio_stream_t *stream)
+static apt_bool_t mpf_encoder_open(mpf_audio_stream_t *stream, mpf_codec_t *codec)
{
mpf_encoder_t *encoder = stream->obj;
- return mpf_audio_stream_tx_open(encoder->sink);
+ return mpf_audio_stream_tx_open(encoder->sink,encoder->codec);
}
static apt_bool_t mpf_encoder_close(mpf_audio_stream_t *stream)
mpf_encoder_t *encoder = stream->obj;
encoder->frame_out.type = frame->type;
+ encoder->frame_out.marker = frame->marker;
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);
+ mpf_codec_encode(encoder->codec,&frame->codec_frame,&encoder->frame_out.codec_frame);
}
return mpf_audio_stream_frame_write(encoder->sink,&encoder->frame_out);
}
+static void mpf_encoder_trace(mpf_audio_stream_t *stream, mpf_stream_direction_e direction, apt_text_stream_t *output)
+{
+ apr_size_t offset;
+ mpf_codec_descriptor_t *descriptor;
+ mpf_encoder_t *encoder = stream->obj;
+
+ descriptor = encoder->base->tx_descriptor;
+ if(descriptor) {
+ offset = output->pos - output->text.buf;
+ output->pos += apr_snprintf(output->pos, output->text.length - offset,
+ "[%s/%d/%d]->Encoder->",
+ descriptor->name.buf,
+ descriptor->sampling_rate,
+ descriptor->channel_count);
+ }
+
+ mpf_audio_stream_trace(encoder->sink,direction,output);
+}
+
static const mpf_audio_stream_vtable_t vtable = {
mpf_encoder_destroy,
NULL,
mpf_encoder_open,
mpf_encoder_close,
- mpf_encoder_process
+ mpf_encoder_process,
+ mpf_encoder_trace
};
-MPF_DECLARE(mpf_audio_stream_t*) mpf_encoder_create(mpf_audio_stream_t *sink, apr_pool_t *pool)
+MPF_DECLARE(mpf_audio_stream_t*) mpf_encoder_create(mpf_audio_stream_t *sink, mpf_codec_t *codec, apr_pool_t *pool)
{
apr_size_t frame_size;
- mpf_codec_t *codec;
mpf_encoder_t *encoder;
- if(!sink || !sink->tx_codec) {
+ mpf_stream_capabilities_t *capabilities;
+ if(!sink || !codec) {
return NULL;
}
encoder = apr_palloc(pool,sizeof(mpf_encoder_t));
- encoder->base = mpf_audio_stream_create(encoder,&vtable,STREAM_MODE_SEND,pool);
+ capabilities = mpf_stream_capabilities_create(STREAM_DIRECTION_SEND,pool);
+ encoder->base = mpf_audio_stream_create(encoder,&vtable,capabilities,pool);
+ if(!encoder->base) {
+ return NULL;
+ }
+ encoder->base->tx_descriptor = mpf_codec_lpcm_descriptor_create(
+ sink->tx_descriptor->sampling_rate,
+ sink->tx_descriptor->channel_count,
+ pool);
+ encoder->base->tx_event_descriptor = sink->tx_event_descriptor;
+
encoder->sink = sink;
+ encoder->codec = codec;
- codec = sink->tx_codec;
- frame_size = mpf_codec_frame_size_calculate(codec->descriptor,codec->attribs);
- encoder->base->tx_codec = codec;
+ frame_size = mpf_codec_frame_size_calculate(sink->tx_descriptor,codec->attribs);
encoder->frame_out.codec_frame.size = frame_size;
encoder->frame_out.codec_frame.buffer = apr_palloc(pool,frame_size);
return encoder->base;
*/
#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_scheduler.h"
#include "mpf_codec_descriptor.h"
#include "mpf_codec_manager.h"
+#include "mpf_timer_manager.h"
#include "apt_obj_list.h"
#include "apt_cyclic_queue.h"
#include "apt_log.h"
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;
+ mpf_context_factory_t *context_factory;
+ mpf_scheduler_t *scheduler;
+ mpf_timer_manager_t *timer_manager;
const mpf_codec_manager_t *codec_manager;
};
-static void mpf_engine_main(mpf_timer_t *timer, void *data);
+static void mpf_engine_main(mpf_scheduler_t *scheduler, 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_lpcm_create(apr_pool_t *pool);
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)
+MPF_DECLARE(mpf_engine_t*) mpf_engine_create(unsigned long rate, 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->context_factory = NULL;
engine->codec_manager = NULL;
- msg_pool = apt_task_msg_pool_create_dynamic(sizeof(mpf_message_t),pool);
+ msg_pool = apt_task_msg_pool_create_dynamic(sizeof(mpf_message_container_t),pool);
apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create "MPF_TASK_NAME);
engine->task = apt_task_create(engine,msg_pool,pool);
engine->task_msg_type = TASK_MSG_USER;
+ engine->context_factory = mpf_context_factory_create(engine->pool);
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);
+ engine->scheduler = mpf_scheduler_create(rate,engine->pool);
+ mpf_scheduler_media_clock_set(engine->scheduler,CODEC_FRAME_TIME_BASE,mpf_engine_main,engine);
+ engine->timer_manager = mpf_timer_manager_create(engine->scheduler,engine->pool);
return engine;
}
+MPF_DECLARE(mpf_context_t*) mpf_engine_context_create(
+ mpf_engine_t *engine,
+ void *obj,
+ apr_size_t max_termination_count,
+ apr_pool_t *pool)
+{
+ return mpf_context_create(engine->context_factory,obj,max_termination_count,pool);
+}
+
+MPF_DECLARE(apt_bool_t) mpf_engine_context_destroy(mpf_context_t *context)
+{
+ return mpf_context_destroy(context);
+}
+
+MPF_DECLARE(void*) mpf_engine_context_object_get(mpf_context_t *context)
+{
+ return mpf_context_object_get(context);
+}
+
MPF_DECLARE(apt_task_t*) mpf_task_get(mpf_engine_t *engine)
{
return engine->task;
engine->task_msg_type = type;
}
+static mpf_message_t* mpf_engine_message_get(mpf_engine_t *engine, mpf_task_msg_t **task_msg)
+{
+ mpf_message_container_t *container;
+ mpf_message_t *mpf_message;
+ if(*task_msg) {
+ container = (mpf_message_container_t*) (*task_msg)->data;
+ if(container->count >= MAX_MPF_MESSAGE_COUNT) {
+ /* container has been already filled,
+ implicitly send the requests and get new task message */
+ mpf_engine_message_send(engine,task_msg);
+ return mpf_engine_message_get(engine,task_msg);
+ }
+ }
+ else {
+ *task_msg = apt_task_msg_get(engine->task);
+ container = (mpf_message_container_t*) (*task_msg)->data;
+ container->count = 0;
+ }
+
+ mpf_message = &container->messages[container->count];
+ container->count++;
+ return mpf_message;
+}
+
+
+MPF_DECLARE(apt_bool_t) mpf_engine_termination_message_add(
+ mpf_engine_t *engine,
+ mpf_command_type_e command_id,
+ mpf_context_t *context,
+ mpf_termination_t *termination,
+ void *descriptor,
+ mpf_task_msg_t **task_msg)
+{
+ mpf_message_t *mpf_message = mpf_engine_message_get(engine,task_msg);
+ if(!mpf_message) {
+ return FALSE;
+ }
+ mpf_message->message_type = MPF_MESSAGE_TYPE_REQUEST;
+ mpf_message->command_id = command_id;
+ mpf_message->context = context;
+ mpf_message->termination = termination;
+ mpf_message->assoc_termination = NULL;
+ mpf_message->descriptor = descriptor;
+ return TRUE;
+}
+
+MPF_DECLARE(apt_bool_t) mpf_engine_assoc_message_add(
+ mpf_engine_t *engine,
+ mpf_command_type_e command_id,
+ mpf_context_t *context,
+ mpf_termination_t *termination,
+ mpf_termination_t *assoc_termination,
+ mpf_task_msg_t **task_msg)
+{
+ mpf_message_t *mpf_message = mpf_engine_message_get(engine,task_msg);
+ if(!mpf_message) {
+ return FALSE;
+ }
+ mpf_message->message_type = MPF_MESSAGE_TYPE_REQUEST;
+ mpf_message->command_id = command_id;
+ mpf_message->context = context;
+ mpf_message->termination = termination;
+ mpf_message->assoc_termination = assoc_termination;
+ mpf_message->descriptor = NULL;
+ return TRUE;
+}
+
+MPF_DECLARE(apt_bool_t) mpf_engine_topology_message_add(
+ mpf_engine_t *engine,
+ mpf_command_type_e command_id,
+ mpf_context_t *context,
+ mpf_task_msg_t **task_msg)
+{
+ mpf_message_t *mpf_message = mpf_engine_message_get(engine,task_msg);
+ if(!mpf_message) {
+ return FALSE;
+ }
+ mpf_message->message_type = MPF_MESSAGE_TYPE_REQUEST;
+ mpf_message->command_id = command_id;
+ mpf_message->context = context;
+ mpf_message->termination = NULL;
+ mpf_message->assoc_termination = NULL;
+ mpf_message->descriptor = NULL;
+ return TRUE;
+}
+
+MPF_DECLARE(apt_bool_t) mpf_engine_message_send(mpf_engine_t *engine, mpf_task_msg_t **task_msg)
+{
+ apt_bool_t status = FALSE;
+ if(*task_msg) {
+ status = apt_task_msg_signal(engine->task,*task_msg);
+ *task_msg = NULL;
+ }
+ return status;
+}
+
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);
-
+ mpf_timer_manager_destroy(engine->timer_manager);
+ mpf_scheduler_destroy(engine->scheduler);
+ mpf_context_factory_destroy(engine->context_factory);
apt_cyclic_queue_destroy(engine->request_queue);
apr_thread_mutex_destroy(engine->request_queue_guard);
return TRUE;
{
mpf_engine_t *engine = apt_task_object_get(task);
- engine->timer = mpf_timer_start(CODEC_FRAME_TIME_BASE,mpf_engine_main,engine,engine->pool);
+ mpf_scheduler_start(engine->scheduler);
apt_task_child_start(task);
return TRUE;
}
{
mpf_engine_t *engine = apt_task_object_get(task);
- mpf_timer_stop(engine->timer);
- mpf_engine_contexts_destroy(engine);
+ mpf_scheduler_stop(engine->scheduler);
apt_task_child_terminate(task);
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_message_container_t *event_msg;
+ mpf_message_t *mpf_message;
mpf_engine_t *engine;
engine = termination->event_handler_obj;
if(!engine) {
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;
+ event_msg = (mpf_message_container_t*) task_msg->data;
+ mpf_message = event_msg->messages;
+ event_msg->count = 1;
+
+ mpf_message->command_id = event_id;
+ mpf_message->message_type = MPF_MESSAGE_TYPE_EVENT;
+ mpf_message->status_code = MPF_STATUS_CODE_SUCCESS;
+ mpf_message->context = NULL;
+ mpf_message->termination = termination;
+ mpf_message->descriptor = descriptor;
return apt_task_msg_parent_signal(engine->task,task_msg);
}
static apt_bool_t mpf_engine_msg_process(apt_task_t *task, apt_task_msg_t *msg)
{
+ apr_size_t i;
mpf_engine_t *engine = apt_task_object_get(task);
apt_task_msg_t *response_msg;
- mpf_message_t *response;
+ mpf_message_container_t *response;
+ mpf_message_t *mpf_response;
mpf_context_t *context;
mpf_termination_t *termination;
- const mpf_message_t *request = (const mpf_message_t*) msg->data;
+ const mpf_message_t *mpf_request;
+ const mpf_message_container_t *request = (const mpf_message_container_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 = (mpf_message_container_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);
+ for(i=0; i<request->count; i++) {
+ mpf_request = &request->messages[i];
+ mpf_response = &response->messages[i];
+
+ if(mpf_request->message_type != MPF_MESSAGE_TYPE_REQUEST) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Invalid MPF Message Type [%d]",mpf_request->message_type);
+ continue;
+ }
+
+ mpf_response->message_type = MPF_MESSAGE_TYPE_RESPONSE;
+ mpf_response->status_code = MPF_STATUS_CODE_SUCCESS;
+ context = mpf_request->context;
+ termination = mpf_request->termination;
+ switch(mpf_request->command_id) {
+ case MPF_ADD_TERMINATION:
+ {
+ termination->event_handler_obj = engine;
+ termination->event_handler = mpf_engine_event_raise;
+ termination->codec_manager = engine->codec_manager;
+ termination->timer_manager = engine->timer_manager;
+
+ mpf_termination_add(termination,mpf_request->descriptor);
+ if(mpf_context_termination_add(context,termination) == FALSE) {
+ mpf_termination_subtract(termination);
+ mpf_response->status_code = MPF_STATUS_CODE_FAILURE;
+ break;
+ }
+ break;
}
- mpf_termination_validate(termination);
- if(mpf_context_termination_add(context,termination) == FALSE) {
- response->status_code = MPF_STATUS_CODE_FAILURE;
+ case MPF_MODIFY_TERMINATION:
+ {
+ mpf_termination_modify(termination,mpf_request->descriptor);
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);
+ case MPF_SUBTRACT_TERMINATION:
+ {
+ if(mpf_context_termination_subtract(context,termination) == FALSE) {
+ mpf_response->status_code = MPF_STATUS_CODE_FAILURE;
+ break;
+ }
+ mpf_termination_subtract(termination);
+ break;
}
- 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;
+ case MPF_ADD_ASSOCIATION:
+ {
+ mpf_context_association_add(context,termination,mpf_request->assoc_termination);
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;
+ case MPF_REMOVE_ASSOCIATION:
+ {
+ mpf_context_association_remove(context,termination,mpf_request->assoc_termination);
+ break;
}
- 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);
+ case MPF_RESET_ASSOCIATIONS:
+ {
+ mpf_context_associations_reset(context);
+ break;
+ }
+ case MPF_APPLY_TOPOLOGY:
+ {
+ mpf_context_topology_apply(context);
+ break;
+ }
+ case MPF_DESTROY_TOPOLOGY:
+ {
+ mpf_context_topology_destroy(context);
+ break;
+ }
+ default:
+ {
+ mpf_response->status_code = MPF_STATUS_CODE_FAILURE;
}
- 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)
+static void mpf_engine_main(mpf_scheduler_t *scheduler, 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);
}
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);
- }
+ /* process factory of media contexts */
+ mpf_context_factory_process(engine->context_factory);
}
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);
+ mpf_codec_manager_t *codec_manager = mpf_codec_manager_create(4,pool);
if(codec_manager) {
mpf_codec_t *codec;
- codec = mpf_codec_lpcm_create(pool);
- mpf_codec_manager_codec_register(codec_manager,codec);
codec = mpf_codec_g711u_create(pool);
mpf_codec_manager_codec_register(codec_manager,codec);
return TRUE;
}
-static apt_bool_t mpf_file_termination_modify(mpf_termination_t *termination, void *descriptor)
+static apt_bool_t mpf_file_termination_add(mpf_termination_t *termination, void *descriptor)
{
+ apt_bool_t status = TRUE;
mpf_audio_stream_t *audio_stream = termination->audio_stream;
if(!audio_stream) {
audio_stream = mpf_file_stream_create(termination,termination->pool);
termination->audio_stream = audio_stream;
}
- return mpf_file_stream_modify(audio_stream,descriptor);
+ if(descriptor) {
+ status = mpf_file_stream_modify(audio_stream,descriptor);
+ }
+ return status;
+}
+
+static apt_bool_t mpf_file_termination_modify(mpf_termination_t *termination, void *descriptor)
+{
+ apt_bool_t status = TRUE;
+ mpf_audio_stream_t *audio_stream = termination->audio_stream;
+ if(!audio_stream) {
+ return FALSE;
+ }
+
+ if(descriptor) {
+ status = mpf_file_stream_modify(audio_stream,descriptor);
+ }
+ return status;
+}
+
+static apt_bool_t mpf_file_termination_subtract(mpf_termination_t *termination)
+{
+ return TRUE;
}
static const mpf_termination_vtable_t file_vtable = {
mpf_file_termination_destroy,
+ mpf_file_termination_add,
mpf_file_termination_modify,
+ mpf_file_termination_subtract
};
static mpf_termination_t* mpf_file_termination_create(
mpf_frame_buffer_t* mpf_frame_buffer_create(apr_size_t frame_size, apr_size_t frame_count, apr_pool_t *pool)
{
apr_size_t i;
+ mpf_frame_t *frame;
mpf_frame_buffer_t *buffer = apr_palloc(pool,sizeof(mpf_frame_buffer_t));
buffer->frame_size = frame_size;
buffer->raw_data = apr_palloc(pool,buffer->frame_size*buffer->frame_count);
buffer->frames = apr_palloc(pool,sizeof(mpf_frame_t)*buffer->frame_count);
for(i=0; i<buffer->frame_count; i++) {
- buffer->frames[i].type = MEDIA_FRAME_TYPE_NONE;
- buffer->frames[i].codec_frame.buffer = buffer->raw_data + i*buffer->frame_size;
+ frame = &buffer->frames[i];
+ frame->type = MEDIA_FRAME_TYPE_NONE;
+ frame->marker = MPF_MARKER_NONE;
+ frame->codec_frame.buffer = buffer->raw_data + i*buffer->frame_size;
}
buffer->write_pos = buffer->read_pos = 0;
/* normal read */
mpf_frame_t *src_media_frame = mpf_frame_buffer_frame_get(buffer,buffer->read_pos);
media_frame->type = src_media_frame->type;
+ media_frame->marker = src_media_frame->marker;
if(media_frame->type & MEDIA_FRAME_TYPE_AUDIO) {
media_frame->codec_frame.size = src_media_frame->codec_frame.size;
memcpy(
media_frame->event_frame = src_media_frame->event_frame;
}
src_media_frame->type = MEDIA_FRAME_TYPE_NONE;
+ src_media_frame->marker = MPF_MARKER_NONE;
buffer->read_pos ++;
}
else {
/* underflow */
media_frame->type = MEDIA_FRAME_TYPE_NONE;
+ media_frame->marker = MPF_MARKER_NONE;
}
apr_thread_mutex_unlock(buffer->guard);
return TRUE;
#include "mpf_jitter_buffer.h"
+#if ENABLE_JB_TRACE
+#define JB_TRACE printf
+#else
+static APR_INLINE void null_trace() {}
+#define JB_TRACE null_trace
+#endif
+
struct mpf_jitter_buffer_t {
+ /* jitter buffer config */
mpf_jb_config_t *config;
+ /* codec to be used to dissect payload */
+ mpf_codec_t *codec;
+ /* cyclic raw data */
apr_byte_t *raw_data;
+ /* frames (out of raw data) */
mpf_frame_t *frames;
+ /* number of frames */
apr_size_t frame_count;
+ /* frame timestamp units (samples) */
apr_size_t frame_ts;
+ /* frame size in bytes */
apr_size_t frame_size;
+ /* playout delay in timetsamp units */
apr_size_t playout_delay_ts;
+ /* write should be synchronized (offset calculated) */
apr_byte_t write_sync;
+ /* write timestamp offset */
int write_ts_offset;
+ /* write pointer in timestamp units */
apr_size_t write_ts;
+ /* read pointer in timestamp units */
apr_size_t read_ts;
- apr_pool_t *pool;
+ /* timestamp event starts at */
+ apr_size_t event_write_base_ts;
+ /* the first (base) frame of the event */
+ mpf_named_event_frame_t event_write_base;
+ /* the last received update for the event */
+ const mpf_named_event_frame_t *event_write_update;
};
-mpf_jitter_buffer_t* mpf_jitter_buffer_create(mpf_jb_config_t *jb_config, mpf_codec_t *codec, apr_pool_t *pool)
+mpf_jitter_buffer_t* mpf_jitter_buffer_create(mpf_jb_config_t *jb_config, mpf_codec_descriptor_t *descriptor, mpf_codec_t *codec, apr_pool_t *pool)
{
size_t i;
+ mpf_frame_t *frame;
mpf_jitter_buffer_t *jb = apr_palloc(pool,sizeof(mpf_jitter_buffer_t));
if(!jb_config) {
/* create default jb config */
}
}
jb->config = jb_config;
+ jb->codec = codec;
- jb->frame_ts = mpf_codec_frame_samples_calculate(codec->descriptor);
- jb->frame_size = mpf_codec_frame_size_calculate(codec->descriptor,codec->attribs);
+ jb->frame_ts = mpf_codec_frame_samples_calculate(descriptor);
+ jb->frame_size = mpf_codec_frame_size_calculate(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; i<jb->frame_count; i++) {
- jb->frames[i].type = MEDIA_FRAME_TYPE_NONE;
- jb->frames[i].codec_frame.buffer = jb->raw_data + i*jb->frame_size;
+ frame = &jb->frames[i];
+ frame->type = MEDIA_FRAME_TYPE_NONE;
+ frame->marker = MPF_MARKER_NONE;
+ frame->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;
+ descriptor->channel_count * descriptor->sampling_rate / 1000;
jb->write_sync = 1;
jb->write_ts_offset = 0;
jb->write_ts = jb->read_ts = 0;
+ jb->event_write_base_ts = 0;
+ memset(&jb->event_write_base,0,sizeof(mpf_named_event_frame_t));
+ jb->event_write_update = NULL;
+
return jb;
}
jb->write_sync = 1;
jb->write_ts_offset = 0;
jb->write_ts = jb->read_ts;
+
+ jb->event_write_base_ts = 0;
+ memset(&jb->event_write_base,0,sizeof(mpf_named_event_frame_t));
+ jb->event_write_update = NULL;
+
return TRUE;
}
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)
+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)
{
- jb_result_t result = JB_OK;
if(jb->write_sync) {
jb->write_ts_offset = ts - jb->write_ts;
jb->write_sync = 0;
/* not frame alligned */
return JB_DISCARD_NOT_ALLIGNED;
}
+ return JB_OK;
+}
- if(*write_ts >= jb->write_ts) {
- if(*write_ts - jb->write_ts > jb->frame_ts) {
+jb_result_t mpf_jitter_buffer_write(mpf_jitter_buffer_t *jb, 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;
+ jb_result_t result = mpf_jitter_buffer_write_prepare(jb,ts,&write_ts);
+ if(result != JB_OK) {
+ return result;
+ }
+
+ 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) {
+ if(write_ts >= jb->read_ts) {
/* backward write */
}
else {
/* too late */
- result = JB_DISCARD_TOO_LATE;
+ JB_TRACE("JB write ts=%d too late\n",write_ts);
+ return 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;
+ available_frame_count = jb->frame_count - (write_ts - jb->read_ts)/jb->frame_ts;
+ if(available_frame_count <= 0) {
+ /* too early */
+ JB_TRACE("JB write ts=%d too early\n",write_ts);
+ return JB_DISCARD_TOO_EARLY;
}
+ JB_TRACE("JB write ts=%d size=%d\n",write_ts,size);
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) {
+ if(mpf_codec_dissect(jb->codec,&buffer,&size,&media_frame->codec_frame) == FALSE) {
break;
}
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)
+jb_result_t mpf_jitter_buffer_event_write(mpf_jitter_buffer_t *jb, const mpf_named_event_frame_t *named_event, apr_uint32_t ts, apr_byte_t marker)
{
- return JB_OK;
+ mpf_frame_t *media_frame;
+ apr_size_t write_ts;
+ jb_result_t result = mpf_jitter_buffer_write_prepare(jb,ts,&write_ts);
+ if(result != JB_OK) {
+ return result;
+ }
+
+ /* new event detection */
+ if(!marker) {
+ if(jb->event_write_base.event_id != named_event->event_id) {
+ /* new event detected, marker is missing though */
+ marker = 1;
+ }
+ else if(jb->event_write_base_ts != write_ts) {
+ /* detect whether this is a new segment of the same event or new event with missing marker
+ assuming a threshold which equals to 4 frames */
+ if(write_ts > jb->event_write_base_ts + jb->event_write_update->duration + 4*jb->frame_ts) {
+ /* new event detected, marker is missing though */
+ marker = 1;
+ }
+ else {
+ /* new segment of the same long-lasting event detected */
+ jb->event_write_base = *named_event;
+ jb->event_write_update = &jb->event_write_base;
+ jb->event_write_base_ts = write_ts;
+ }
+ }
+ }
+ if(marker) {
+ /* new event */
+ jb->event_write_base = *named_event;
+ jb->event_write_update = &jb->event_write_base;
+ jb->event_write_base_ts = write_ts;
+ }
+ else {
+ /* an update */
+ if(named_event->duration <= jb->event_write_update->duration) {
+ /* ignore this update, it's either a retransmission or
+ something from the past, which makes no sense now */
+ return JB_OK;
+ }
+ /* calculate position in jitter buffer considering the last received event (update) */
+ write_ts += jb->event_write_update->duration;
+ }
+
+ if(write_ts < jb->read_ts) {
+ /* too late */
+ JB_TRACE("JB write ts=%d event=%d duration=%d too late\n",
+ write_ts,named_event->event_id,named_event->duration);
+ return JB_DISCARD_TOO_LATE;
+ }
+ else if( (write_ts - jb->read_ts)/jb->frame_ts >= jb->frame_count) {
+ /* too early */
+ JB_TRACE("JB write ts=%d event=%d duration=%d too early\n",
+ write_ts,named_event->event_id,named_event->duration);
+ return JB_DISCARD_TOO_EARLY;
+ }
+
+ JB_TRACE("JB write ts=%d event=%d duration=%d\n",
+ write_ts,named_event->event_id,named_event->duration);
+ media_frame = mpf_jitter_buffer_frame_get(jb,write_ts);
+ media_frame->event_frame = *named_event;
+ media_frame->type |= MEDIA_FRAME_TYPE_EVENT;
+ if(marker) {
+ media_frame->marker = MPF_MARKER_START_OF_EVENT;
+ }
+ else if(named_event->edge == 1) {
+ media_frame->marker = MPF_MARKER_END_OF_EVENT;
+ }
+ jb->event_write_update = &media_frame->event_frame;
+
+ write_ts += jb->frame_ts;
+ if(write_ts > jb->write_ts) {
+ jb->write_ts = write_ts;
+ }
+ return result;
}
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 */
+ JB_TRACE("JB read ts=%d\n", jb->read_ts);
media_frame->type = src_media_frame->type;
+ media_frame->marker = src_media_frame->marker;
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);
}
else {
/* underflow */
+ JB_TRACE("JB read ts=%d underflow\n", jb->read_ts);
media_frame->type = MEDIA_FRAME_TYPE_NONE;
+ media_frame->marker = MPF_MARKER_NONE;
jb->write_ts += jb->frame_ts;
}
src_media_frame->type = MEDIA_FRAME_TYPE_NONE;
+ src_media_frame->marker = MPF_MARKER_NONE;
jb->read_ts += jb->frame_ts;
return TRUE;
}
--- /dev/null
+/*
+ * 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_mixer.h"
+#include "mpf_encoder.h"
+#include "mpf_decoder.h"
+#include "mpf_resampler.h"
+#include "mpf_codec_manager.h"
+#include "apt_log.h"
+
+typedef struct mpf_mixer_t mpf_mixer_t;
+
+/** MPF mixer derived from MPF object */
+struct mpf_mixer_t {
+ /** MPF mixer base */
+ mpf_object_t base;
+ /** Array of audio sources */
+ mpf_audio_stream_t **source_arr;
+ /** Number of audio sources */
+ apr_size_t source_count;
+ /** Audio sink */
+ mpf_audio_stream_t *sink;
+
+ /** Frame to read from audio source */
+ mpf_frame_t frame;
+ /** Mixed frame to write to audio sink */
+ mpf_frame_t mix_frame;
+};
+
+static apt_bool_t mpf_frames_mix(mpf_frame_t *mix_frame, const mpf_frame_t *frame)
+{
+ apr_size_t i;
+ apr_int16_t *mix_buf = mix_frame->codec_frame.buffer;
+ const apr_int16_t *buf = frame->codec_frame.buffer;
+ apr_size_t samples = frame->codec_frame.size / sizeof(apr_int16_t);
+
+ if(mix_frame->codec_frame.size != frame->codec_frame.size) {
+ return FALSE;
+ }
+
+ if((frame->type & MEDIA_FRAME_TYPE_AUDIO) == MEDIA_FRAME_TYPE_AUDIO) {
+ for(i=0; i<samples; i++) {
+ /* overflow MUST be considered */
+ mix_buf[i] = mix_buf[i] + buf[i];
+ }
+ mix_frame->type |= MEDIA_FRAME_TYPE_AUDIO;
+ }
+
+ return TRUE;
+}
+
+static apt_bool_t mpf_mixer_process(mpf_object_t *object)
+{
+ apr_size_t i;
+ mpf_audio_stream_t *source;
+ mpf_mixer_t *mixer = (mpf_mixer_t*) object;
+
+ mixer->mix_frame.type = MEDIA_FRAME_TYPE_NONE;
+ mixer->mix_frame.marker = MPF_MARKER_NONE;
+ memset(mixer->mix_frame.codec_frame.buffer,0,mixer->mix_frame.codec_frame.size);
+ for(i=0; i<mixer->source_count; i++) {
+ source = mixer->source_arr[i];
+ if(source) {
+ mixer->frame.type = MEDIA_FRAME_TYPE_NONE;
+ mixer->frame.marker = MPF_MARKER_NONE;
+ source->vtable->read_frame(source,&mixer->frame);
+ mpf_frames_mix(&mixer->mix_frame,&mixer->frame);
+ }
+ }
+ mixer->sink->vtable->write_frame(mixer->sink,&mixer->mix_frame);
+ return TRUE;
+}
+
+static apt_bool_t mpf_mixer_destroy(mpf_object_t *object)
+{
+ apr_size_t i;
+ mpf_audio_stream_t *source;
+ mpf_mixer_t *mixer = (mpf_mixer_t*) object;
+
+ for(i=0; i<mixer->source_count; i++) {
+ source = mixer->source_arr[i];
+ if(source) {
+ mpf_audio_stream_rx_close(source);
+ }
+ }
+ mpf_audio_stream_tx_close(mixer->sink);
+ return TRUE;
+}
+
+static void mpf_mixer_trace(mpf_object_t *object)
+{
+ mpf_mixer_t *mixer = (mpf_mixer_t*) object;
+ apr_size_t i;
+ mpf_audio_stream_t *source;
+ char buf[2048];
+ apr_size_t offset;
+
+ apt_text_stream_t output;
+ apt_text_stream_init(&output,buf,sizeof(buf)-1);
+
+ for(i=0; i<mixer->source_count; i++) {
+ source = mixer->source_arr[i];
+ if(source) {
+ mpf_audio_stream_trace(source,STREAM_DIRECTION_RECEIVE,&output);
+ if(apt_text_is_eos(&output) == FALSE) {
+ *output.pos++ = ';';
+ }
+ }
+ }
+
+ offset = output.pos - output.text.buf;
+ output.pos += apr_snprintf(output.pos, output.text.length - offset,
+ "->Mixer->");
+
+ mpf_audio_stream_trace(mixer->sink,STREAM_DIRECTION_SEND,&output);
+
+ *output.pos = '\0';
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,output.text.buf);
+}
+
+MPF_DECLARE(mpf_object_t*) mpf_mixer_create(
+ mpf_audio_stream_t **source_arr,
+ apr_size_t source_count,
+ mpf_audio_stream_t *sink,
+ const mpf_codec_manager_t *codec_manager,
+ apr_pool_t *pool)
+{
+ apr_size_t i;
+ apr_size_t frame_size;
+ mpf_codec_descriptor_t *descriptor;
+ mpf_audio_stream_t *source;
+ mpf_mixer_t *mixer;
+ if(!source_arr || !source_count || !sink) {
+ return NULL;
+ }
+
+ mixer = apr_palloc(pool,sizeof(mpf_mixer_t));
+ mixer->source_arr = NULL;
+ mixer->source_count = 0;
+ mixer->sink = NULL;
+ mpf_object_init(&mixer->base);
+ mixer->base.process = mpf_mixer_process;
+ mixer->base.destroy = mpf_mixer_destroy;
+ mixer->base.trace = mpf_mixer_trace;
+
+ if(mpf_audio_stream_tx_validate(sink,NULL,NULL,pool) == FALSE) {
+ return NULL;
+ }
+
+ descriptor = sink->tx_descriptor;
+ if(descriptor && mpf_codec_lpcm_descriptor_match(descriptor) == FALSE) {
+ mpf_codec_t *codec = mpf_codec_manager_codec_get(codec_manager,descriptor,pool);
+ if(codec) {
+ /* set encoder after mixer */
+ mpf_audio_stream_t *encoder = mpf_encoder_create(sink,codec,pool);
+ sink = encoder;
+ }
+ }
+ mixer->sink = sink;
+ mpf_audio_stream_tx_open(sink,NULL);
+
+ for(i=0; i<source_count; i++) {
+ source = source_arr[i];
+ if(!source) continue;
+
+ if(mpf_audio_stream_rx_validate(source,NULL,NULL,pool) == FALSE) {
+ continue;
+ }
+
+ descriptor = source->rx_descriptor;
+ if(descriptor && mpf_codec_lpcm_descriptor_match(descriptor) == FALSE) {
+ mpf_codec_t *codec = mpf_codec_manager_codec_get(codec_manager,descriptor,pool);
+ if(codec) {
+ /* set decoder before mixer */
+ mpf_audio_stream_t *decoder = mpf_decoder_create(source,codec,pool);
+ source = decoder;
+ }
+ }
+ source_arr[i] = source;
+ mpf_audio_stream_rx_open(source,NULL);
+ }
+ mixer->source_arr = source_arr;
+ mixer->source_count = source_count;
+
+ descriptor = sink->tx_descriptor;
+ frame_size = mpf_codec_linear_frame_size_calculate(descriptor->sampling_rate,descriptor->channel_count);
+ mixer->frame.codec_frame.size = frame_size;
+ mixer->frame.codec_frame.buffer = apr_palloc(pool,frame_size);
+ mixer->mix_frame.codec_frame.size = frame_size;
+ mixer->mix_frame.codec_frame.buffer = apr_palloc(pool,frame_size);
+ return &mixer->base;
+}
--- /dev/null
+/*
+ * 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_multiplier.h"
+#include "mpf_encoder.h"
+#include "mpf_decoder.h"
+#include "mpf_resampler.h"
+#include "mpf_codec_manager.h"
+#include "apt_log.h"
+
+typedef struct mpf_multiplier_t mpf_multiplier_t;
+
+/** MPF multiplier derived from MPF object */
+struct mpf_multiplier_t {
+ /** MPF multiplier base */
+ mpf_object_t base;
+ /** Audio source */
+ mpf_audio_stream_t *source;
+ /** Array of audio sinks */
+ mpf_audio_stream_t **sink_arr;
+ /** Number of audio sinks */
+ apr_size_t sink_count;
+
+ /** Media frame used to read data from source and write it to sinks */
+ mpf_frame_t frame;
+};
+
+static apt_bool_t mpf_multiplier_process(mpf_object_t *object)
+{
+ apr_size_t i;
+ mpf_audio_stream_t *sink;
+ mpf_multiplier_t *multiplier = (mpf_multiplier_t*) object;
+
+ multiplier->frame.type = MEDIA_FRAME_TYPE_NONE;
+ multiplier->frame.marker = MPF_MARKER_NONE;
+ multiplier->source->vtable->read_frame(multiplier->source,&multiplier->frame);
+
+ if((multiplier->frame.type & MEDIA_FRAME_TYPE_AUDIO) == 0) {
+ memset( multiplier->frame.codec_frame.buffer,
+ 0,
+ multiplier->frame.codec_frame.size);
+ }
+
+ for(i=0; i<multiplier->sink_count; i++) {
+ sink = multiplier->sink_arr[i];
+ if(sink) {
+ sink->vtable->write_frame(sink,&multiplier->frame);
+ }
+ }
+ return TRUE;
+}
+
+static apt_bool_t mpf_multiplier_destroy(mpf_object_t *object)
+{
+ apr_size_t i;
+ mpf_audio_stream_t *sink;
+ mpf_multiplier_t *multiplier = (mpf_multiplier_t*) object;
+
+ mpf_audio_stream_rx_close(multiplier->source);
+ for(i=0; i<multiplier->sink_count; i++) {
+ sink = multiplier->sink_arr[i];
+ if(sink) {
+ mpf_audio_stream_tx_close(sink);
+ }
+ }
+ return TRUE;
+}
+
+static void mpf_multiplier_trace(mpf_object_t *object)
+{
+ mpf_multiplier_t *multiplier = (mpf_multiplier_t*) object;
+ apr_size_t i;
+ mpf_audio_stream_t *sink;
+ char buf[2048];
+ apr_size_t offset;
+
+ apt_text_stream_t output;
+ apt_text_stream_init(&output,buf,sizeof(buf)-1);
+
+ mpf_audio_stream_trace(multiplier->source,STREAM_DIRECTION_RECEIVE,&output);
+
+ offset = output.pos - output.text.buf;
+ output.pos += apr_snprintf(output.pos, output.text.length - offset,
+ "->Multiplier->");
+
+ for(i=0; i<multiplier->sink_count; i++) {
+ sink = multiplier->sink_arr[i];
+ if(sink) {
+ mpf_audio_stream_trace(sink,STREAM_DIRECTION_SEND,&output);
+ if(apt_text_is_eos(&output) == FALSE) {
+ *output.pos++ = ';';
+ }
+ }
+ }
+
+ *output.pos = '\0';
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,output.text.buf);
+}
+
+MPF_DECLARE(mpf_object_t*) mpf_multiplier_create(
+ mpf_audio_stream_t *source,
+ mpf_audio_stream_t **sink_arr,
+ apr_size_t sink_count,
+ const mpf_codec_manager_t *codec_manager,
+ apr_pool_t *pool)
+{
+ apr_size_t i;
+ apr_size_t frame_size;
+ mpf_codec_descriptor_t *descriptor;
+ mpf_audio_stream_t *sink;
+ mpf_multiplier_t *multiplier;
+ if(!source || !sink_arr || !sink_count) {
+ return NULL;
+ }
+
+ multiplier = apr_palloc(pool,sizeof(mpf_multiplier_t));
+ multiplier->source = NULL;
+ multiplier->sink_arr = NULL;
+ multiplier->sink_count = 0;
+ mpf_object_init(&multiplier->base);
+ multiplier->base.process = mpf_multiplier_process;
+ multiplier->base.destroy = mpf_multiplier_destroy;
+ multiplier->base.trace = mpf_multiplier_trace;
+
+ if(mpf_audio_stream_rx_validate(source,NULL,NULL,pool) == FALSE) {
+ return NULL;
+ }
+
+ descriptor = source->rx_descriptor;
+ if(descriptor && mpf_codec_lpcm_descriptor_match(descriptor) == FALSE) {
+ mpf_codec_t *codec = mpf_codec_manager_codec_get(codec_manager,descriptor,pool);
+ if(codec) {
+ /* set decoder before bridge */
+ mpf_audio_stream_t *decoder = mpf_decoder_create(source,codec,pool);
+ source = decoder;
+ }
+ }
+ multiplier->source = source;
+ mpf_audio_stream_rx_open(source,NULL);
+
+ for(i=0; i<sink_count; i++) {
+ sink = sink_arr[i];
+ if(!sink) continue;
+
+ if(mpf_audio_stream_tx_validate(sink,NULL,NULL,pool) == FALSE) {
+ continue;
+ }
+
+ descriptor = sink->tx_descriptor;
+ if(descriptor && mpf_codec_lpcm_descriptor_match(descriptor) == FALSE) {
+ mpf_codec_t *codec = mpf_codec_manager_codec_get(codec_manager,descriptor,pool);
+ if(codec) {
+ /* set encoder after bridge */
+ mpf_audio_stream_t *encoder = mpf_encoder_create(sink,codec,pool);
+ sink = encoder;
+ }
+ }
+ sink_arr[i] = sink;
+ mpf_audio_stream_tx_open(sink,NULL);
+ }
+ multiplier->sink_arr = sink_arr;
+ multiplier->sink_count = sink_count;
+
+ descriptor = source->rx_descriptor;
+ frame_size = mpf_codec_linear_frame_size_calculate(descriptor->sampling_rate,descriptor->channel_count);
+ multiplier->frame.codec_frame.size = frame_size;
+ multiplier->frame.codec_frame.buffer = apr_palloc(pool,frame_size);
+ return &multiplier->base;
+}
--- /dev/null
+/*
+ * 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_named_event.h"
+
+#define TEL_EVENT_NAME "telephone-event"
+#define TEL_EVENT_NAME_LENGTH (sizeof(TEL_EVENT_NAME)-1)
+
+#define TEL_EVENT_FMTP "0-15"
+#define TEL_EVENT_FMTP_LENGTH (sizeof(TEL_EVENT_FMTP)-1)
+
+
+MPF_DECLARE(mpf_codec_descriptor_t*) mpf_event_descriptor_create(apr_uint16_t sampling_rate, apr_pool_t *pool)
+{
+ mpf_codec_descriptor_t *descriptor = apr_palloc(pool,sizeof(mpf_codec_descriptor_t));
+ mpf_codec_descriptor_init(descriptor);
+ descriptor->payload_type = 101;
+ descriptor->name.buf = TEL_EVENT_NAME;
+ descriptor->name.length = TEL_EVENT_NAME_LENGTH;
+ descriptor->sampling_rate = sampling_rate;
+ descriptor->channel_count = 1;
+ descriptor->format.buf = TEL_EVENT_FMTP;
+ descriptor->format.length = TEL_EVENT_FMTP_LENGTH;
+ return descriptor;
+}
+
+MPF_DECLARE(apt_bool_t) mpf_event_descriptor_check(const mpf_codec_descriptor_t *descriptor)
+{
+ apt_str_t name;
+ name.buf = TEL_EVENT_NAME;
+ name.length = TEL_EVENT_NAME_LENGTH;
+ return apt_string_compare(&descriptor->name,&name);
+}
+
+MPF_DECLARE(apr_uint32_t) mpf_dtmf_char_to_event_id(const char dtmf_char)
+{
+ if ((dtmf_char >= '0') && (dtmf_char <= '9'))
+ return dtmf_char - '0';
+ else if (dtmf_char == '*')
+ return 10;
+ else if (dtmf_char == '#')
+ return 11;
+ else if ((dtmf_char >= 'A') && (dtmf_char <= 'D'))
+ return 12 + dtmf_char - 'A';
+
+ return 255; /* Invalid DTMF event */
+}
+
+MPF_DECLARE(char) mpf_event_id_to_dtmf_char(const apr_uint32_t event_id)
+{
+ if (event_id <= 9)
+ return '0' + (char)event_id;
+ else if (event_id == 10)
+ return '*';
+ else if (event_id == 11)
+ return '#';
+ else if (event_id <= 15)
+ return 'A' + (char)event_id - 12;
+
+ return 0; /* Not a DTMF event */
+}
--- /dev/null
+/*
+ * 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_resampler.h"
+#include "apt_log.h"
+
+MPF_DECLARE(mpf_audio_stream_t*) mpf_resampler_create(mpf_audio_stream_t *source, mpf_audio_stream_t *sink, apr_pool_t *pool)
+{
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,
+ "Currently resampling is not supported. "
+ "Try to configure and use the same sampling rate on both ends");
+ return NULL;
+}
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_DECLARE(const apt_str_t*) mpf_rtp_direction_str_get(mpf_stream_direction_e direction)
{
mpf_rtp_attrib_e attrib_id = RTP_ATTRIB_UNKNOWN;
switch(direction) {
- case STREAM_MODE_SEND:
+ case STREAM_DIRECTION_SEND:
attrib_id = RTP_ATTRIB_SENDONLY;
break;
- case STREAM_MODE_RECEIVE:
+ case STREAM_DIRECTION_RECEIVE:
attrib_id = RTP_ATTRIB_RECVONLY;
break;
- case STREAM_MODE_SEND_RECEIVE:
+ case STREAM_DIRECTION_DUPLEX:
attrib_id = RTP_ATTRIB_SENDRECV;
break;
default:
*/
#include <apr_network_io.h>
+#include "apt_net.h"
#include "mpf_rtp_stream.h"
#include "mpf_termination.h"
#include "mpf_codec_manager.h"
+#include "mpf_timer_manager.h"
#include "mpf_rtp_header.h"
+#include "mpf_rtcp_packet.h"
#include "mpf_rtp_defs.h"
+#include "mpf_rtp_pt.h"
#include "apt_log.h"
+/** Max size of RTP packet */
+#define MAX_RTP_PACKET_SIZE 1500
+/** Max size of RTCP packet */
+#define MAX_RTCP_PACKET_SIZE 1500
+
+/* Reason strings used in RTCP BYE messages (informative only) */
+#define RTCP_BYE_SESSION_ENDED "Session ended"
+#define RTCP_BYE_TALKSPURT_ENDED "Talskpurt ended"
+
+#if ENABLE_RTP_PACKET_TRACE
+#define RTP_TRACE printf
+#else
+static APR_INLINE void null_trace() {}
+#define RTP_TRACE null_trace
+#endif
/** RTP stream */
typedef struct mpf_rtp_stream_t mpf_rtp_stream_t;
mpf_rtp_media_descriptor_t *local_media;
mpf_rtp_media_descriptor_t *remote_media;
+ mpf_media_state_e state;
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_socket_t *rtp_socket;
+ apr_socket_t *rtcp_socket;
+ apr_sockaddr_t *rtp_l_sockaddr;
+ apr_sockaddr_t *rtp_r_sockaddr;
+ apr_sockaddr_t *rtcp_l_sockaddr;
+ apr_sockaddr_t *rtcp_r_sockaddr;
+
+ mpf_timer_t *rtcp_tx_timer;
+ mpf_timer_t *rtcp_rx_timer;
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_open(mpf_audio_stream_t *stream, mpf_codec_t *codec);
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_open(mpf_audio_stream_t *stream, mpf_codec_t *codec);
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);
mpf_rtp_stream_transmit
};
-static apt_bool_t mpf_rtp_socket_create(mpf_rtp_stream_t *stream, mpf_rtp_media_descriptor_t *local_media);
+static apt_bool_t mpf_rtp_socket_pair_create(mpf_rtp_stream_t *stream, mpf_rtp_media_descriptor_t *local_media);
+static void mpf_rtp_socket_pair_close(mpf_rtp_stream_t *stream);
+
+static apt_bool_t mpf_rtcp_report_send(mpf_rtp_stream_t *stream);
+static apt_bool_t mpf_rtcp_bye_send(mpf_rtp_stream_t *stream, apt_str_t *reason);
+static void mpf_rtcp_tx_timer_proc(mpf_timer_t *timer, void *obj);
+static void mpf_rtcp_rx_timer_proc(mpf_timer_t *timer, void *obj);
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));
+ mpf_stream_capabilities_t *capabilities = mpf_stream_capabilities_create(STREAM_DIRECTION_DUPLEX,pool);
+ mpf_audio_stream_t *audio_stream = mpf_audio_stream_create(rtp_stream,&vtable,capabilities,pool);
+ if(!audio_stream) {
+ return NULL;
+ }
+
+ audio_stream->direction = STREAM_DIRECTION_NONE;
+ audio_stream->termination = termination;
+
+ rtp_stream->base = audio_stream;
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_stream->rtp_socket = NULL;
+ rtp_stream->rtcp_socket = NULL;
+ rtp_stream->rtp_l_sockaddr = NULL;
+ rtp_stream->rtp_r_sockaddr = NULL;
+ rtp_stream->rtcp_l_sockaddr = NULL;
+ rtp_stream->rtcp_r_sockaddr = NULL;
+ rtp_stream->rtcp_tx_timer = NULL;
+ rtp_stream->rtcp_rx_timer = NULL;
+ rtp_stream->state = MPF_MEDIA_DISABLED;
rtp_receiver_init(&rtp_stream->receiver);
rtp_transmitter_init(&rtp_stream->transmitter);
- rtp_stream->transmitter.ssrc = (apr_uint32_t)apr_time_now();
+ rtp_stream->transmitter.sr_stat.ssrc = (apr_uint32_t)apr_time_now();
+
+ if(config->rtcp == TRUE) {
+ if(config->rtcp_tx_interval) {
+ rtp_stream->rtcp_tx_timer = mpf_timer_create(
+ termination->timer_manager,
+ mpf_rtcp_tx_timer_proc,
+ rtp_stream, pool);
+ }
+ if(config->rtcp_rx_resolution) {
+ rtp_stream->rtcp_rx_timer = mpf_timer_create(
+ termination->timer_manager,
+ mpf_rtcp_rx_timer_proc,
+ rtp_stream, pool);
+ }
+ }
- return rtp_stream->base;
+ return audio_stream;
}
-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)
+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, mpf_stream_capabilities_t *capabilities)
{
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;
+ local_media->state = MPF_MEDIA_ENABLED;
+ local_media->direction = STREAM_DIRECTION_DUPLEX;
}
if(remote_media) {
- local_media->base.id = remote_media->base.id;
+ local_media->id = remote_media->id;
}
- if(local_media->base.ip.length == 0) {
- local_media->base.ip = rtp_stream->config->ip;
- local_media->base.ext_ip = rtp_stream->config->ext_ip;
+ if(local_media->ip.length == 0) {
+ local_media->ip = rtp_stream->config->ip;
+ local_media->ext_ip = rtp_stream->config->ext_ip;
}
- if(local_media->base.port == 0) {
+ if(local_media->port == 0) {
/* RTP port management */
apr_port_t first_port_in_search = rtp_stream->config->rtp_port_cur;
apt_bool_t is_port_ok = FALSE;
do {
- local_media->base.port = rtp_stream->config->rtp_port_cur;
+ local_media->port = rtp_stream->config->rtp_port_cur;
rtp_stream->config->rtp_port_cur += 2;
if(rtp_stream->config->rtp_port_cur == rtp_stream->config->rtp_port_max) {
rtp_stream->config->rtp_port_cur = rtp_stream->config->rtp_port_min;
}
- if(mpf_rtp_socket_create(rtp_stream,local_media) == TRUE) {
+ if(mpf_rtp_socket_pair_create(rtp_stream,local_media) == TRUE) {
is_port_ok = TRUE;
}
} while((is_port_ok == FALSE) && (first_port_in_search != rtp_stream->config->rtp_port_cur));
if(is_port_ok == FALSE) {
- local_media->base.state = MPF_MEDIA_DISABLED;
+ local_media->state = MPF_MEDIA_DISABLED;
status = FALSE;
}
}
- else if(mpf_rtp_socket_create(rtp_stream,local_media) == FALSE) {
- local_media->base.state = MPF_MEDIA_DISABLED;
+ else if(mpf_rtp_socket_pair_create(rtp_stream,local_media) == FALSE) {
+ local_media->state = MPF_MEDIA_DISABLED;
status = FALSE;
}
&rtp_stream->config->codec_list,
rtp_stream->pool);
}
+
+ if(capabilities) {
+ mpf_codec_list_modify(&local_media->codec_list,&capabilities->codecs);
+ }
}
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)
+static apt_bool_t mpf_rtp_stream_local_media_update(mpf_rtp_stream_t *rtp_stream, mpf_rtp_media_descriptor_t *media, mpf_stream_capabilities_t *capabilities)
{
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(apt_string_compare(&rtp_stream->local_media->ip,&media->ip) == FALSE ||
+ rtp_stream->local_media->port != media->port) {
- if(mpf_rtp_socket_create(rtp_stream,media) == FALSE) {
- media->base.state = MPF_MEDIA_DISABLED;
+ if(mpf_rtp_socket_pair_create(rtp_stream,media) == FALSE) {
+ media->state = MPF_MEDIA_DISABLED;
status = FALSE;
}
}
rtp_stream->base->termination->codec_manager,
&media->codec_list,
rtp_stream->pool);
+ if(capabilities) {
+ mpf_codec_list_modify(&media->codec_list,&capabilities->codecs);
+ }
}
rtp_stream->local_media = media;
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;
+ if(media->state == MPF_MEDIA_ENABLED) {
+ if(!rtp_stream->remote_media ||
+ apt_string_compare(&rtp_stream->remote_media->ip,&media->ip) == FALSE ||
+ rtp_stream->remote_media->port != media->port) {
+
+ /* update RTP port */
+ rtp_stream->rtp_r_sockaddr = NULL;
+ apr_sockaddr_info_get(
+ &rtp_stream->rtp_r_sockaddr,
+ media->ip.buf,
+ APR_INET,
+ media->port,
+ 0,
+ rtp_stream->pool);
+ if(!rtp_stream->rtp_r_sockaddr) {
+ status = FALSE;
+ }
+
+ /* update RTCP port */
+ rtp_stream->rtcp_r_sockaddr = NULL;
+ apr_sockaddr_info_get(
+ &rtp_stream->rtcp_r_sockaddr,
+ media->ip.buf,
+ APR_INET,
+ media->port+1,
+ 0,
+ rtp_stream->pool);
}
}
static apt_bool_t mpf_rtp_stream_media_negotiate(mpf_rtp_stream_t *rtp_stream)
{
- if(!rtp_stream->local_media || !rtp_stream->remote_media) {
+ mpf_rtp_media_descriptor_t *local_media = rtp_stream->local_media;
+ mpf_rtp_media_descriptor_t *remote_media = rtp_stream->remote_media;
+ if(!local_media || !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);
+ local_media->id = remote_media->id;
+ local_media->mid = remote_media->mid;
+ local_media->ptime = remote_media->ptime;
+
+ if(rtp_stream->state == MPF_MEDIA_DISABLED && remote_media->state == MPF_MEDIA_ENABLED) {
+ /* enable RTP/RTCP session */
+ rtp_stream->state = MPF_MEDIA_ENABLED;
+ if(rtp_stream->rtp_l_sockaddr) {
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Enable RTP Session %s:%hu",
+ rtp_stream->rtp_l_sockaddr->hostname,
+ rtp_stream->rtp_l_sockaddr->port);
+ }
- 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);
+ if(rtp_stream->rtcp_tx_timer) {
+ mpf_timer_set(rtp_stream->rtcp_tx_timer,rtp_stream->config->rtcp_tx_interval);
+ }
+ if(rtp_stream->rtcp_rx_timer) {
+ mpf_timer_set(rtp_stream->rtcp_rx_timer,rtp_stream->config->rtcp_rx_resolution);
+ }
}
+ else if(rtp_stream->state == MPF_MEDIA_ENABLED && remote_media->state == MPF_MEDIA_DISABLED) {
+ /* disable RTP/RTCP session */
+ rtp_stream->state = MPF_MEDIA_DISABLED;
+ if(rtp_stream->rtp_l_sockaddr) {
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Disable RTP Session %s:%hu",
+ rtp_stream->rtp_l_sockaddr->hostname,
+ rtp_stream->rtp_l_sockaddr->port);
+ }
- /* 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);
+ if(rtp_stream->rtcp_tx_timer) {
+ mpf_timer_kill(rtp_stream->rtcp_tx_timer);
+ }
+ if(rtp_stream->rtcp_rx_timer) {
+ mpf_timer_kill(rtp_stream->rtcp_rx_timer);
+ }
+ if(rtp_stream->config->rtcp == TRUE && rtp_stream->config->rtcp_bye_policy != RTCP_BYE_DISABLE) {
+ apt_str_t reason = {RTCP_BYE_SESSION_ENDED, sizeof(RTCP_BYE_SESSION_ENDED)-1};
+ mpf_rtcp_bye_send(rtp_stream,&reason);
+ }
}
- else {
- mpf_codec_list_intersect(&rtp_stream->remote_media->codec_list,&rtp_stream->local_media->codec_list);
+
+ local_media->state = remote_media->state;
+ local_media->direction = mpf_stream_reverse_direction_get(remote_media->direction);
+ rtp_stream->base->direction = local_media->direction;
+
+ if(remote_media->state == MPF_MEDIA_ENABLED) {
+ if(mpf_codec_list_is_empty(&remote_media->codec_list) == TRUE) {
+ /* no remote codecs available, initialize them according to the local codecs */
+ mpf_codec_list_copy(&remote_media->codec_list,
+ &local_media->codec_list,
+ rtp_stream->pool);
+ }
+
+ /* intersect local and remote codecs */
+ if(rtp_stream->config->own_preferrence == TRUE) {
+ mpf_codec_lists_intersect(
+ &local_media->codec_list,
+ &remote_media->codec_list);
+ }
+ else {
+ mpf_codec_lists_intersect(
+ &remote_media->codec_list,
+ &local_media->codec_list);
+ }
}
- rtp_stream->base->mode = rtp_stream->local_media->mode;
+ return TRUE;
+}
+
+MPF_DECLARE(apt_bool_t) mpf_rtp_stream_add(mpf_audio_stream_t *stream)
+{
+ return TRUE;
+}
+
+MPF_DECLARE(apt_bool_t) mpf_rtp_stream_remove(mpf_audio_stream_t *stream)
+{
+ mpf_rtp_stream_t *rtp_stream = stream->obj;
+
+ if(rtp_stream->state == MPF_MEDIA_ENABLED) {
+ /* disable RTP/RTCP session */
+ rtp_stream->state = MPF_MEDIA_DISABLED;
+ if(rtp_stream->rtp_l_sockaddr) {
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Remove RTP Session %s:%hu",
+ rtp_stream->rtp_l_sockaddr->hostname,
+ rtp_stream->rtp_l_sockaddr->port);
+ }
+
+ if(rtp_stream->rtcp_tx_timer) {
+ mpf_timer_kill(rtp_stream->rtcp_tx_timer);
+ }
+ if(rtp_stream->rtcp_rx_timer) {
+ mpf_timer_kill(rtp_stream->rtcp_rx_timer);
+ }
+ if(rtp_stream->config->rtcp == TRUE && rtp_stream->config->rtcp_bye_policy != RTCP_BYE_DISABLE) {
+ apt_str_t reason = {RTCP_BYE_SESSION_ENDED, sizeof(RTCP_BYE_SESSION_ENDED)-1};
+ mpf_rtcp_bye_send(rtp_stream,&reason);
+ }
+ }
+
+ mpf_rtp_socket_pair_close(rtp_stream);
return TRUE;
}
if(!rtp_stream->local_media) {
/* create local media */
- status = mpf_rtp_stream_local_media_create(rtp_stream,descriptor->local,descriptor->remote);
+ status = mpf_rtp_stream_local_media_create(rtp_stream,descriptor->local,descriptor->remote,descriptor->capabilities);
}
else if(descriptor->local) {
/* update local media */
- status = mpf_rtp_stream_local_media_update(rtp_stream,descriptor->local);
+ status = mpf_rtp_stream_local_media_update(rtp_stream,descriptor->local,descriptor->capabilities);
}
if(descriptor->remote && status == TRUE) {
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) {
+ if((rtp_stream->base->direction & STREAM_DIRECTION_SEND) == STREAM_DIRECTION_SEND) {
+ mpf_codec_list_t *codec_list = &rtp_stream->remote_media->codec_list;
+ rtp_stream->base->tx_descriptor = codec_list->primary_descriptor;
+ if(rtp_stream->base->tx_descriptor) {
rtp_stream->transmitter.samples_per_frame =
- (apr_uint32_t)mpf_codec_frame_samples_calculate(rtp_stream->base->tx_codec->descriptor);
+ (apr_uint32_t)mpf_codec_frame_samples_calculate(rtp_stream->base->tx_descriptor);
+ }
+ if(codec_list->event_descriptor) {
+ rtp_stream->base->tx_event_descriptor = codec_list->event_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((rtp_stream->base->direction & STREAM_DIRECTION_RECEIVE) == STREAM_DIRECTION_RECEIVE) {
+ mpf_codec_list_t *codec_list = &rtp_stream->local_media->codec_list;
+ rtp_stream->base->rx_descriptor = codec_list->primary_descriptor;
+ if(codec_list->event_descriptor) {
+ rtp_stream->base->rx_event_descriptor = codec_list->event_descriptor;
+ }
}
if(!descriptor->local) {
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)
+static apt_bool_t mpf_rtp_rx_stream_open(mpf_audio_stream_t *stream, mpf_codec_t *codec)
{
mpf_rtp_stream_t *rtp_stream = stream->obj;
rtp_receiver_t *receiver = &rtp_stream->receiver;
- if(!rtp_stream->socket || !rtp_stream->local_media) {
+ if(!rtp_stream->rtp_socket || !rtp_stream->rtp_l_sockaddr || !rtp_stream->rtp_r_sockaddr) {
return FALSE;
}
receiver->jb = mpf_jitter_buffer_create(
&rtp_stream->config->jb_config,
- stream->rx_codec,
+ stream->rx_descriptor,
+ 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,
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Open RTP Receiver %s:%hu <- %s:%hu playout [%d ms]",
+ rtp_stream->rtp_l_sockaddr->hostname,
+ rtp_stream->rtp_l_sockaddr->port,
+ rtp_stream->rtp_r_sockaddr->hostname,
+ rtp_stream->rtp_r_sockaddr->port,
rtp_stream->config->jb_config.initial_playout_delay);
return TRUE;
}
{
mpf_rtp_stream_t *rtp_stream = stream->obj;
rtp_receiver_t *receiver = &rtp_stream->receiver;
+
+ if(!rtp_stream->rtp_l_sockaddr || !rtp_stream->rtp_r_sockaddr) {
+ return FALSE;
+ }
+
receiver->stat.lost_packets = 0;
if(receiver->stat.received_packets) {
apr_uint32_t expected_packets = receiver->history.seq_cycles +
}
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,
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Close RTP Receiver %s:%hu <- %s:%hu [r:%lu l:%lu j:%lu]",
+ rtp_stream->rtp_l_sockaddr->hostname,
+ rtp_stream->rtp_l_sockaddr->port,
+ rtp_stream->rtp_r_sockaddr->hostname,
+ rtp_stream->rtp_r_sockaddr->port,
receiver->stat.received_packets,
receiver->stat.lost_packets,
- receiver->stat.jitter);
+ receiver->rr_stat.jitter);
return TRUE;
}
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->rr_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;
return header;
}
+static APR_INLINE void rtp_periodic_history_update(rtp_receiver_t *receiver)
+{
+ apr_uint32_t expected_packets;
+ apr_uint32_t expected_interval;
+ apr_uint32_t received_interval;
+ apr_uint32_t lost_interval;
+
+ /* calculate expected packets */
+ expected_packets = receiver->history.seq_cycles +
+ receiver->history.seq_num_max - receiver->history.seq_num_base + 1;
+
+ /* calculate expected interval */
+ expected_interval = expected_packets - receiver->periodic_history.expected_prior;
+ /* update expected prior */
+ receiver->periodic_history.expected_prior = expected_packets;
+
+ /* calculate received interval */
+ received_interval = receiver->stat.received_packets - receiver->periodic_history.received_prior;
+ /* update received prior */
+ receiver->periodic_history.received_prior = receiver->stat.received_packets;
+ /* calculate lost interval */
+ if(expected_interval > received_interval) {
+ lost_interval = expected_interval - received_interval;
+ }
+ else {
+ lost_interval = 0;
+ }
+
+ /* update lost fraction */
+ if(expected_interval == 0 || lost_interval == 0) {
+ receiver->rr_stat.fraction = 0;
+ }
+ else {
+ receiver->rr_stat.fraction = (lost_interval << 8) / expected_interval;
+ }
+
+ if(expected_packets > receiver->stat.received_packets) {
+ receiver->rr_stat.lost = expected_packets - receiver->stat.received_packets;
+ }
+ else {
+ receiver->rr_stat.lost = 0;
+ }
+
+ receiver->periodic_history.discarded_prior = receiver->stat.discarded_packets;
+ receiver->periodic_history.jitter_min = receiver->rr_stat.jitter;
+ receiver->periodic_history.jitter_max = receiver->rr_stat.jitter;
+}
+
typedef enum {
RTP_SSRC_UPDATE,
RTP_SSRC_PROBATION,
static APR_INLINE rtp_ssrc_result_e rtp_rx_ssrc_update(rtp_receiver_t *receiver, apr_uint32_t ssrc)
{
- if(receiver->stat.ssrc == ssrc) {
+ if(receiver->rr_stat.ssrc == ssrc) {
/* known ssrc */
if(receiver->history.ssrc_probation) {
/* reset the probation for new ssrc */
if(receiver->history.ssrc_new == ssrc) {
if(--receiver->history.ssrc_probation == 0) {
/* restart with new ssrc */
- receiver->stat.ssrc = ssrc;
+ receiver->rr_stat.ssrc = ssrc;
return RTP_SSRC_RESTART;
}
else {
}
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;
}
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->rr_stat.jitter += deviation - ((receiver->rr_stat.jitter + 8) >> 4);
+ RTP_TRACE("jitter=%d deviation=%d\n",receiver->rr_stat.jitter,deviation);
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->rr_stat.jitter < receiver->periodic_history.jitter_min) {
+ receiver->periodic_history.jitter_min = receiver->rr_stat.jitter;
}
- if(receiver->stat.jitter > receiver->periodic_history.jitter_max) {
- receiver->periodic_history.jitter_max = receiver->stat.jitter;
+ if(receiver->rr_stat.jitter > receiver->periodic_history.jitter_max) {
+ receiver->periodic_history.jitter_max = receiver->rr_stat.jitter;
}
return RTP_TS_UPDATE;
}
}
}
-static apt_bool_t rtp_rx_packet_receive(rtp_receiver_t *receiver, mpf_codec_t *codec, void *buffer, apr_size_t size)
+static apt_bool_t rtp_rx_packet_receive(mpf_rtp_stream_t *rtp_stream, void *buffer, apr_size_t size)
{
+ rtp_receiver_t *receiver = &rtp_stream->receiver;
+ mpf_codec_descriptor_t *descriptor = rtp_stream->base->rx_descriptor;
apr_time_t time;
rtp_ssrc_result_e ssrc_result;
rtp_header_t *header = rtp_rx_header_skip(&buffer,&size);
time = apr_time_now();
-#if PRINT_RTP_PACKET_STAT
- printf("RTP time=%6lu ssrc=%8lx pt=%3u %cts=%9lu seq=%5u size=%lu\n",
+ RTP_TRACE("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->ssrc, header->type, (header->marker == 1) ? '*' : ' ',
header->timestamp, header->sequence, size);
-#endif
if(!receiver->stat.received_packets) {
/* initialization */
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) {
+ if(header->type == descriptor->payload_type) {
/* codec */
- if(mpf_jitter_buffer_write(receiver->jb,codec,buffer,size,header->timestamp) != JB_OK) {
+ if(rtp_rx_ts_update(receiver,descriptor,&time,header->timestamp) == RTP_TS_DRIFT) {
+ rtp_rx_restart(receiver);
+ return FALSE;
+ }
+
+ if(mpf_jitter_buffer_write(receiver->jb,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) {
+ else if(rtp_stream->base->rx_event_descriptor &&
+ header->type == rtp_stream->base->rx_event_descriptor->payload_type) {
/* 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) {
+ named_event->duration = ntohs((apr_uint16_t)named_event->duration);
+ if(mpf_jitter_buffer_event_write(receiver->jb,named_event,header->timestamp,(apr_byte_t)header->marker) != JB_OK) {
receiver->stat.discarded_packets++;
- rtp_rx_failure_threshold_check(receiver);
}
}
- else if(header->type == 13 || header->type == 19) {
- /* CN packet*/
+ else if(header->type == RTP_PT_CN) {
+ /* CN packet */
receiver->stat.ignored_packets++;
}
else {
static apt_bool_t rtp_rx_process(mpf_rtp_stream_t *rtp_stream)
{
- char buffer[1500];
+ char buffer[MAX_RTP_PACKET_SIZE];
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);
+ while(max_count && apr_socket_recv(rtp_stream->rtp_socket,buffer,&size) == APR_SUCCESS) {
+ rtp_rx_packet_receive(rtp_stream,buffer,size);
size = sizeof(buffer);
max_count--;
}
-
-
-
-static apt_bool_t mpf_rtp_tx_stream_open(mpf_audio_stream_t *stream)
+static apt_bool_t mpf_rtp_tx_stream_open(mpf_audio_stream_t *stream, mpf_codec_t *codec)
{
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) {
+
+ if(!rtp_stream->rtp_socket || !rtp_stream->rtp_l_sockaddr || !rtp_stream->rtp_r_sockaddr) {
+ return FALSE;
+ }
+
+ if(!codec) {
return FALSE;
}
transmitter->current_frames = 0;
frame_size = mpf_codec_frame_size_calculate(
- stream->tx_codec->descriptor,
- stream->tx_codec->attribs);
+ stream->tx_descriptor,
+ 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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Open RTP Transmitter %s:%hu -> %s:%hu",
+ rtp_stream->rtp_l_sockaddr->hostname,
+ rtp_stream->rtp_l_sockaddr->port,
+ rtp_stream->rtp_r_sockaddr->hostname,
+ rtp_stream->rtp_r_sockaddr->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);
+ if(!rtp_stream->rtp_l_sockaddr || !rtp_stream->rtp_r_sockaddr) {
+ return FALSE;
+ }
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Close RTP Transmitter %s:%hu -> %s:%hu [s:%lu o:%lu]",
+ rtp_stream->rtp_l_sockaddr->hostname,
+ rtp_stream->rtp_l_sockaddr->port,
+ rtp_stream->rtp_l_sockaddr->hostname,
+ rtp_stream->rtp_r_sockaddr->port,
+ rtp_stream->transmitter.sr_stat.sent_packets,
+ rtp_stream->transmitter.sr_stat.sent_octets);
return TRUE;
}
-static APR_INLINE void rtp_header_prepare(rtp_transmitter_t *transmitter, apr_byte_t payload_type)
+static APR_INLINE void rtp_header_prepare(
+ rtp_transmitter_t *transmitter,
+ rtp_header_t *header,
+ apr_byte_t payload_type,
+ apr_byte_t marker,
+ apr_uint32_t timestamp)
{
- 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->marker = marker;
header->type = payload_type;
- header->sequence = htons(++transmitter->last_seq_num);
- header->timestamp = htonl(transmitter->timestamp);
- header->ssrc = htonl(transmitter->ssrc);
+ header->timestamp = timestamp;
+ header->ssrc = htonl(transmitter->sr_stat.ssrc);
+}
- if(transmitter->inactivity) {
- transmitter->inactivity = 0;
+static APR_INLINE apt_bool_t mpf_rtp_data_send(mpf_rtp_stream_t *rtp_stream, rtp_transmitter_t *transmitter, const mpf_frame_t *frame)
+{
+ apt_bool_t status = TRUE;
+ 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) {
+ rtp_header_t *header = (rtp_header_t*)transmitter->packet_data;
+ header->sequence = htons(++transmitter->last_seq_num);
+ RTP_TRACE("> RTP time=%6lu ssrc=%8lx pt=%3u %cts=%9lu seq=%5u\n",
+ (apr_uint32_t)apr_time_usec(apr_time_now()),
+ transmitter->sr_stat.ssrc, header->type,
+ (header->marker == 1) ? '*' : ' ',
+ header->timestamp, transmitter->last_seq_num);
+ header->timestamp = htonl(header->timestamp);
+ if(apr_socket_sendto(
+ rtp_stream->rtp_socket,
+ rtp_stream->rtp_r_sockaddr,
+ 0,
+ transmitter->packet_data,
+ &transmitter->packet_size) == APR_SUCCESS) {
+ transmitter->sr_stat.sent_packets++;
+ transmitter->sr_stat.sent_octets += transmitter->packet_size - sizeof(rtp_header_t);
+ }
+ else {
+ status = FALSE;
+ }
+ transmitter->current_frames = 0;
}
+ return status;
+}
- transmitter->packet_size = sizeof(rtp_header_t);
+static APR_INLINE apt_bool_t mpf_rtp_event_send(mpf_rtp_stream_t *rtp_stream, rtp_transmitter_t *transmitter, const mpf_frame_t *frame)
+{
+ char packet_data[20];
+ apr_size_t packet_size = sizeof(rtp_header_t) + sizeof(mpf_named_event_frame_t);
+ rtp_header_t *header = (rtp_header_t*) packet_data;
+ mpf_named_event_frame_t *named_event = (mpf_named_event_frame_t*)(header+1);
+ rtp_header_prepare(
+ transmitter,
+ header,
+ rtp_stream->base->tx_event_descriptor->payload_type,
+ (frame->marker == MPF_MARKER_START_OF_EVENT) ? 1 : 0,
+ transmitter->timestamp_base);
+
+ *named_event = frame->event_frame;
+ named_event->edge = (frame->marker == MPF_MARKER_END_OF_EVENT) ? 1 : 0;
+
+ header->sequence = htons(++transmitter->last_seq_num);
+ RTP_TRACE("> RTP time=%6lu ssrc=%8lx pt=%3u %cts=%9lu seq=%5u event=%2u dur=%3u %c\n",
+ (apr_uint32_t)apr_time_usec(apr_time_now()),
+ transmitter->sr_stat.ssrc,
+ header->type, (header->marker == 1) ? '*' : ' ',
+ header->timestamp, transmitter->last_seq_num,
+ named_event->event_id, named_event->duration,
+ (named_event->edge == 1) ? '*' : ' ');
+
+ header->timestamp = htonl(header->timestamp);
+ named_event->duration = htons((apr_uint16_t)named_event->duration);
+ if(apr_socket_sendto(
+ rtp_stream->rtp_socket,
+ rtp_stream->rtp_r_sockaddr,
+ 0,
+ packet_data,
+ &packet_size) != APR_SUCCESS) {
+ return FALSE;
+ }
+ transmitter->sr_stat.sent_packets++;
+ transmitter->sr_stat.sent_octets += sizeof(mpf_named_event_frame_t);
+ return TRUE;
}
static apt_bool_t mpf_rtp_stream_transmit(mpf_audio_stream_t *stream, const mpf_frame_t *frame)
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(frame->type == MEDIA_FRAME_TYPE_NONE) {
+ if(!transmitter->inactivity) {
+ if(transmitter->current_frames == 0) {
+ /* set inactivity (ptime alligned) */
+ transmitter->inactivity = 1;
+ if(rtp_stream->config->rtcp == TRUE && rtp_stream->config->rtcp_bye_policy == RTCP_BYE_PER_TALKSPURT) {
+ apt_str_t reason = {RTCP_BYE_TALKSPURT_ENDED, sizeof(RTCP_BYE_TALKSPURT_ENDED)-1};
+ mpf_rtcp_bye_send(rtp_stream,&reason);
+ }
+ }
+ else {
+ /* ptime allignment */
+ status = mpf_rtp_data_send(rtp_stream,transmitter,frame);
+ }
}
+ return status;
}
-
- 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++;
+
+ if((frame->type & MEDIA_FRAME_TYPE_EVENT) == MEDIA_FRAME_TYPE_EVENT){
+ /* transmit event as soon as received */
+ if(stream->tx_event_descriptor) {
+ if(frame->marker == MPF_MARKER_START_OF_EVENT) {
+ /* store start time (base) of the event */
+ transmitter->timestamp_base = transmitter->timestamp;
}
- else {
- status = FALSE;
+ else if(frame->marker == MPF_MARKER_NEW_SEGMENT) {
+ /* update base in case of long-lasting events */
+ transmitter->timestamp_base = transmitter->timestamp;
+ }
+
+ status = mpf_rtp_event_send(rtp_stream,transmitter,frame);
+ }
+ }
+
+ if((frame->type & MEDIA_FRAME_TYPE_AUDIO) == MEDIA_FRAME_TYPE_AUDIO){
+ if(transmitter->current_frames == 0) {
+ rtp_header_t *header = (rtp_header_t*)transmitter->packet_data;
+ rtp_header_prepare(
+ transmitter,
+ header,
+ stream->tx_descriptor->payload_type,
+ transmitter->inactivity,
+ transmitter->timestamp);
+ transmitter->packet_size = sizeof(rtp_header_t);
+ if(transmitter->inactivity) {
+ transmitter->inactivity = 0;
}
- transmitter->current_frames = 0;
}
+ status = mpf_rtp_data_send(rtp_stream,transmitter,frame);
}
return status;
}
-static apt_bool_t mpf_rtp_socket_create(mpf_rtp_stream_t *stream, mpf_rtp_media_descriptor_t *local_media)
+static apr_socket_t* mpf_socket_create(apr_sockaddr_t **l_sockaddr, const char *ip, apr_port_t port, apr_pool_t *pool)
{
- if(stream->socket) {
- apr_socket_close(stream->socket);
- stream->socket = NULL;
- }
-
- stream->local_sockaddr = NULL;
+ apr_socket_t *socket = NULL;
+ *l_sockaddr = NULL;
apr_sockaddr_info_get(
- &stream->local_sockaddr,
- local_media->base.ip.buf,
+ l_sockaddr,
+ ip,
APR_INET,
- local_media->base.port,
+ 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);
+ pool);
+ if(!*l_sockaddr) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Get Sockaddr %s:%hu",ip,port);
+ return NULL;
+ }
+ if(apr_socket_create(&socket,(*l_sockaddr)->family,SOCK_DGRAM,0,pool) != APR_SUCCESS) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Socket %s:%hu", ip,port);
+ return NULL;
+ }
+
+ apr_socket_opt_set(socket,APR_SO_NONBLOCK,1);
+ apr_socket_timeout_set(socket,0);
+ apr_socket_opt_set(socket,APR_SO_REUSEADDR,1);
+
+ if(apr_socket_bind(socket,*l_sockaddr) != APR_SUCCESS) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Bind Socket to %s:%hu", ip,port);
+ apr_socket_close(socket);
+ return NULL;
+ }
+ return socket;
+}
+
+static apt_bool_t mpf_rtp_socket_pair_create(mpf_rtp_stream_t *stream, mpf_rtp_media_descriptor_t *local_media)
+{
+ stream->rtp_socket = mpf_socket_create(&stream->rtp_l_sockaddr,local_media->ip.buf,local_media->port,stream->pool);
+ if(!stream->rtp_socket) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create RTP Socket");
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);
+
+ stream->rtcp_socket = mpf_socket_create(&stream->rtcp_l_sockaddr,local_media->ip.buf,local_media->port+1,stream->pool);
+ if(!stream->rtcp_socket) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create RTCP Socket");
+ }
+ return TRUE;
+}
+
+static void mpf_rtp_socket_pair_close(mpf_rtp_stream_t *stream)
+{
+ if(stream->rtp_socket) {
+ apr_socket_close(stream->rtp_socket);
+ stream->rtp_socket = NULL;
+ }
+ if(stream->rtcp_socket) {
+ apr_socket_close(stream->rtcp_socket);
+ stream->rtcp_socket = NULL;
+ }
+}
+
+
+
+static APR_INLINE void rtcp_sr_generate(mpf_rtp_stream_t *rtp_stream, rtcp_sr_stat_t *sr_stat)
+{
+ *sr_stat = rtp_stream->transmitter.sr_stat;
+ apt_ntp_time_get(&sr_stat->ntp_sec, &sr_stat->ntp_frac);
+ sr_stat->rtp_ts = rtp_stream->transmitter.timestamp;
+
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Generate RTCP SR [ssrc:%lu s:%lu o:%lu ts:%lu]",
+ sr_stat->ssrc,
+ sr_stat->sent_packets,
+ sr_stat->sent_octets,
+ sr_stat->rtp_ts);
+ rtcp_sr_hton(sr_stat);
+}
+
+static APR_INLINE void rtcp_rr_generate(mpf_rtp_stream_t *rtp_stream, rtcp_rr_stat_t *rr_stat)
+{
+ *rr_stat = rtp_stream->receiver.rr_stat;
+ rr_stat->last_seq = rtp_stream->receiver.history.seq_num_max;
+
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Generate RTCP RR [ssrc:%lu last_seq:%lu j:%lu lost:%lu frac:%d]",
+ rr_stat->ssrc,
+ rr_stat->last_seq,
+ rr_stat->jitter,
+ rr_stat->lost,
+ rr_stat->fraction);
+ rtcp_rr_hton(rr_stat);
+}
+
+/* Generate either RTCP SR or RTCP RR packet */
+static APR_INLINE apr_size_t rtcp_report_generate(mpf_rtp_stream_t *rtp_stream, rtcp_packet_t *rtcp_packet, apr_size_t length)
+{
+ apr_size_t offset = 0;
+ rtcp_header_init(&rtcp_packet->header,RTCP_RR);
+ if(rtp_stream->base->direction & STREAM_DIRECTION_SEND) {
+ rtcp_packet->header.pt = RTCP_SR;
+ }
+ if(rtp_stream->base->direction & STREAM_DIRECTION_RECEIVE) {
+ rtcp_packet->header.count = 1;
+ }
+ offset += sizeof(rtcp_header_t);
+
+ if(rtcp_packet->header.pt == RTCP_SR) {
+ rtcp_sr_generate(rtp_stream,&rtcp_packet->r.sr.sr_stat);
+ offset += sizeof(rtcp_sr_stat_t);
+ if(rtcp_packet->header.count) {
+ rtcp_rr_generate(rtp_stream,rtcp_packet->r.sr.rr_stat);
+ offset += sizeof(rtcp_rr_stat_t);
+ }
+ }
+ else if(rtcp_packet->header.pt == RTCP_RR) {
+ rtcp_packet->r.rr.ssrc = htonl(rtp_stream->transmitter.sr_stat.ssrc);
+ rtcp_rr_generate(rtp_stream,rtcp_packet->r.rr.rr_stat);
+ offset += sizeof(rtcp_packet->r.rr);
+ }
+ rtcp_header_length_set(&rtcp_packet->header,offset);
+ return offset;
+}
+
+/* Generate RTCP SDES packet */
+static APR_INLINE apr_size_t rtcp_sdes_generate(mpf_rtp_stream_t *rtp_stream, rtcp_packet_t *rtcp_packet, apr_size_t length)
+{
+ rtcp_sdes_item_t *item;
+ apr_size_t offset = 0;
+ apr_size_t padding;
+ rtcp_header_init(&rtcp_packet->header,RTCP_SDES);
+ offset += sizeof(rtcp_header_t);
+
+ rtcp_packet->header.count ++;
+ rtcp_packet->r.sdes.ssrc = htonl(rtp_stream->transmitter.sr_stat.ssrc);
+ offset += sizeof(apr_uint32_t);
+
+ /* insert SDES CNAME item */
+ item = &rtcp_packet->r.sdes.item[0];
+ item->type = RTCP_SDES_CNAME;
+ item->length = (apr_byte_t)rtp_stream->local_media->ip.length;
+ memcpy(item->data,rtp_stream->local_media->ip.buf,item->length);
+ offset += sizeof(rtcp_sdes_item_t) - 1 + item->length;
+
+ /* terminate with end marker and pad to next 4-octet boundary */
+ padding = 4 - (offset & 0x3);
+ while(padding--) {
+ item = (rtcp_sdes_item_t*) ((char*)rtcp_packet + offset);
+ item->type = RTCP_SDES_END;
+ offset++;
+ }
+
+ rtcp_header_length_set(&rtcp_packet->header,offset);
+ return offset;
+}
+
+/* Generate RTCP BYE packet */
+static APR_INLINE apr_size_t rtcp_bye_generate(mpf_rtp_stream_t *rtp_stream, rtcp_packet_t *rtcp_packet, apr_size_t length, apt_str_t *reason)
+{
+ apr_size_t offset = 0;
+ rtcp_header_init(&rtcp_packet->header,RTCP_BYE);
+ offset += sizeof(rtcp_header_t);
+
+ rtcp_packet->r.bye.ssrc[0] = htonl(rtp_stream->transmitter.sr_stat.ssrc);
+ rtcp_packet->header.count++;
+ offset += rtcp_packet->header.count * sizeof(apr_uint32_t);
+
+ if(reason->length) {
+ apr_size_t padding;
+
+ memcpy(rtcp_packet->r.bye.data,reason->buf,reason->length);
+ rtcp_packet->r.bye.length = (apr_byte_t)reason->length;
+ offset += rtcp_packet->r.bye.length;
+
+ /* terminate with end marker and pad to next 4-octet boundary */
+ padding = 4 - (reason->length & 0x3);
+ if(padding) {
+ char *end = rtcp_packet->r.bye.data + reason->length;
+ memset(end,0,padding);
+ offset += padding;
+ }
+ }
+
+ rtcp_header_length_set(&rtcp_packet->header,offset);
+ return offset;
+}
+
+/* Send compound RTCP packet (SR/RR + SDES) */
+static apt_bool_t mpf_rtcp_report_send(mpf_rtp_stream_t *rtp_stream)
+{
+ char buffer[MAX_RTCP_PACKET_SIZE];
+ apr_size_t length = 0;
+ rtcp_packet_t *rtcp_packet;
+
+ if(!rtp_stream->rtcp_socket || !rtp_stream->rtcp_l_sockaddr || !rtp_stream->rtcp_r_sockaddr) {
+ /* session is not initialized */
return FALSE;
}
+
+ if(rtp_stream->base->direction != STREAM_DIRECTION_NONE) {
+ /* update periodic (prior) history */
+ rtp_periodic_history_update(&rtp_stream->receiver);
+ }
+
+ rtcp_packet = (rtcp_packet_t*) (buffer + length);
+ length += rtcp_report_generate(rtp_stream,rtcp_packet,sizeof(buffer)-length);
+
+ rtcp_packet = (rtcp_packet_t*) (buffer + length);
+ length += rtcp_sdes_generate(rtp_stream,rtcp_packet,sizeof(buffer)-length);
- 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;
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Send Compound RTCP Packet [%d bytes] %s:%hu -> %s:%hu",
+ length,
+ rtp_stream->rtcp_l_sockaddr->hostname,
+ rtp_stream->rtcp_l_sockaddr->port,
+ rtp_stream->rtcp_r_sockaddr->hostname,
+ rtp_stream->rtcp_r_sockaddr->port);
+ if(apr_socket_sendto(
+ rtp_stream->rtcp_socket,
+ rtp_stream->rtcp_r_sockaddr,
+ 0,
+ buffer,
+ &length) != APR_SUCCESS) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Send Compound RTCP Packet [%d bytes] %s:%hu -> %s:%hu",
+ length,
+ rtp_stream->rtcp_l_sockaddr->hostname,
+ rtp_stream->rtcp_l_sockaddr->port,
+ rtp_stream->rtcp_r_sockaddr->hostname,
+ rtp_stream->rtcp_r_sockaddr->port);
return FALSE;
}
return TRUE;
}
+
+/* Send compound RTCP packet (SR/RR + SDES + BYE) */
+static apt_bool_t mpf_rtcp_bye_send(mpf_rtp_stream_t *rtp_stream, apt_str_t *reason)
+{
+ char buffer[MAX_RTCP_PACKET_SIZE];
+ apr_size_t length = 0;
+ rtcp_packet_t *rtcp_packet;
+
+ if(!rtp_stream->rtcp_socket || !rtp_stream->rtcp_l_sockaddr || !rtp_stream->rtcp_r_sockaddr) {
+ /* session is not initialized */
+ return FALSE;
+ }
+
+ if(rtp_stream->base->direction != STREAM_DIRECTION_NONE) {
+ /* update periodic (prior) history */
+ rtp_periodic_history_update(&rtp_stream->receiver);
+ }
+
+ rtcp_packet = (rtcp_packet_t*) (buffer + length);
+ length += rtcp_report_generate(rtp_stream,rtcp_packet,sizeof(buffer)-length);
+
+ rtcp_packet = (rtcp_packet_t*) (buffer + length);
+ length += rtcp_sdes_generate(rtp_stream,rtcp_packet,sizeof(buffer)-length);
+
+ rtcp_packet = (rtcp_packet_t*) (buffer + length);
+ length += rtcp_bye_generate(rtp_stream,rtcp_packet,sizeof(buffer)-length,reason);
+
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Send Compound RTCP Packet [BYE] [%d bytes] %s:%hu -> %s:%hu",
+ length,
+ rtp_stream->rtcp_l_sockaddr->hostname,
+ rtp_stream->rtcp_l_sockaddr->port,
+ rtp_stream->rtcp_r_sockaddr->hostname,
+ rtp_stream->rtcp_r_sockaddr->port);
+ if(apr_socket_sendto(
+ rtp_stream->rtcp_socket,
+ rtp_stream->rtcp_r_sockaddr,
+ 0,
+ buffer,
+ &length) != APR_SUCCESS) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Send Compound RTCP Packet [BYE] [%d bytes] %s:%hu -> %s:%hu",
+ length,
+ rtp_stream->rtcp_l_sockaddr->hostname,
+ rtp_stream->rtcp_l_sockaddr->port,
+ rtp_stream->rtcp_r_sockaddr->hostname,
+ rtp_stream->rtcp_r_sockaddr->port);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static APR_INLINE void rtcp_sr_get(mpf_rtp_stream_t *rtp_stream, rtcp_sr_stat_t *sr_stat)
+{
+ rtcp_sr_ntoh(sr_stat);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Get RTCP SR [ssrc:%lu s:%lu o:%lu ts:%lu]",
+ sr_stat->ssrc,
+ sr_stat->sent_packets,
+ sr_stat->sent_octets,
+ sr_stat->rtp_ts);
+}
+
+static APR_INLINE void rtcp_rr_get(mpf_rtp_stream_t *rtp_stream, rtcp_rr_stat_t *rr_stat)
+{
+ rtcp_rr_ntoh(rr_stat);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Get RTCP RR [ssrc:%lu last_seq:%lu j:%lu lost:%lu frac:%d]",
+ rr_stat->ssrc,
+ rr_stat->last_seq,
+ rr_stat->jitter,
+ rr_stat->lost,
+ rr_stat->fraction);
+}
+
+static apt_bool_t mpf_rtcp_compound_packet_receive(mpf_rtp_stream_t *rtp_stream, char *buffer, apr_size_t length)
+{
+ rtcp_packet_t *rtcp_packet = (rtcp_packet_t*) buffer;
+ rtcp_packet_t *rtcp_packet_end;
+
+ rtcp_packet_end = (rtcp_packet_t*)(buffer + length);
+
+ while(rtcp_packet < rtcp_packet_end && rtcp_packet->header.version == RTP_VERSION) {
+ rtcp_packet->header.length = ntohs((apr_uint16_t)rtcp_packet->header.length);
+
+ if(rtcp_packet->header.pt == RTCP_SR) {
+ /* RTCP SR */
+ rtcp_sr_get(rtp_stream,&rtcp_packet->r.sr.sr_stat);
+ if(rtcp_packet->header.count) {
+ rtcp_rr_get(rtp_stream,rtcp_packet->r.sr.rr_stat);
+ }
+ }
+ else if(rtcp_packet->header.pt == RTCP_RR) {
+ /* RTCP RR */
+ rtcp_packet->r.rr.ssrc = ntohl(rtcp_packet->r.rr.ssrc);
+ if(rtcp_packet->header.count) {
+ rtcp_rr_get(rtp_stream,rtcp_packet->r.rr.rr_stat);
+ }
+ }
+ else if(rtcp_packet->header.pt == RTCP_SDES) {
+ /* RTCP SDES */
+ }
+ else if(rtcp_packet->header.pt == RTCP_BYE) {
+ /* RTCP BYE */
+ }
+ else {
+ /* unknown RTCP packet */
+ }
+
+ /* get next RTCP packet */
+ rtcp_packet = (rtcp_packet_t*)((apr_uint32_t*)rtcp_packet + rtcp_packet->header.length + 1);
+ }
+
+ if(rtcp_packet != rtcp_packet_end) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Malformed Compound RTCP Packet");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void mpf_rtcp_tx_timer_proc(mpf_timer_t *timer, void *obj)
+{
+ mpf_rtp_stream_t *rtp_stream = obj;
+
+ /* generate and send RTCP compound report (SR/RR + SDES) */
+ mpf_rtcp_report_send(rtp_stream);
+
+ /* re-schedule timer */
+ mpf_timer_set(timer,rtp_stream->config->rtcp_tx_interval);
+}
+
+static void mpf_rtcp_rx_timer_proc(mpf_timer_t *timer, void *obj)
+{
+ mpf_rtp_stream_t *rtp_stream = obj;
+ if(rtp_stream->rtcp_socket && rtp_stream->rtcp_l_sockaddr && rtp_stream->rtcp_r_sockaddr) {
+ char buffer[MAX_RTCP_PACKET_SIZE];
+ apr_size_t length = sizeof(buffer);
+
+ if(apr_socket_recv(rtp_stream->rtcp_socket,buffer,&length) == APR_SUCCESS) {
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Receive Compound RTCP Packet [%d bytes] %s:%hu <- %s:%hu",
+ length,
+ rtp_stream->rtcp_l_sockaddr->hostname,
+ rtp_stream->rtcp_l_sockaddr->port,
+ rtp_stream->rtcp_r_sockaddr->hostname,
+ rtp_stream->rtcp_r_sockaddr->port);
+ mpf_rtcp_compound_packet_receive(rtp_stream,buffer,length);
+ }
+ }
+
+ /* re-schedule timer */
+ mpf_timer_set(timer,rtp_stream->config->rtcp_rx_resolution);
+}
return TRUE;
}
-static apt_bool_t mpf_rtp_termination_modify(mpf_termination_t *termination, void *descriptor)
+static apt_bool_t mpf_rtp_termination_add(mpf_termination_t *termination, void *descriptor)
{
+ apt_bool_t status = TRUE;
mpf_rtp_termination_descriptor_t *rtp_descriptor = descriptor;
mpf_audio_stream_t *audio_stream = termination->audio_stream;
if(!audio_stream) {
termination->audio_stream = audio_stream;
}
- return mpf_rtp_stream_modify(audio_stream,&rtp_descriptor->audio);
+ status = mpf_rtp_stream_add(audio_stream);
+ if(rtp_descriptor) {
+ status = mpf_rtp_stream_modify(audio_stream,&rtp_descriptor->audio);
+ }
+ return status;
+}
+
+static apt_bool_t mpf_rtp_termination_modify(mpf_termination_t *termination, void *descriptor)
+{
+ apt_bool_t status = TRUE;
+ mpf_rtp_termination_descriptor_t *rtp_descriptor = descriptor;
+ mpf_audio_stream_t *audio_stream = termination->audio_stream;
+ if(!audio_stream) {
+ return FALSE;
+ }
+
+ if(rtp_descriptor) {
+ status = mpf_rtp_stream_modify(audio_stream,&rtp_descriptor->audio);
+ }
+ return status;
+}
+
+static apt_bool_t mpf_rtp_termination_subtract(mpf_termination_t *termination)
+{
+ mpf_audio_stream_t *audio_stream = termination->audio_stream;
+ if(!audio_stream) {
+ return FALSE;
+ }
+
+ return mpf_rtp_stream_remove(audio_stream);
}
static const mpf_termination_vtable_t rtp_vtable = {
mpf_rtp_termination_destroy,
+ mpf_rtp_termination_add,
mpf_rtp_termination_modify,
+ mpf_rtp_termination_subtract
};
static mpf_termination_t* mpf_rtp_termination_create(mpf_termination_factory_t *termination_factory, void *obj, apr_pool_t *pool)
--- /dev/null
+/*
+ * 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_scheduler.h"
+
+#ifdef WIN32
+#define ENABLE_MULTIMEDIA_TIMERS
+#endif
+
+#ifdef ENABLE_MULTIMEDIA_TIMERS
+
+#pragma warning(disable:4201)
+#include <mmsystem.h>
+#include <windows.h>
+
+#ifndef TIME_KILL_SYNCHRONOUS
+#define TIME_KILL_SYNCHRONOUS 0x0100
+#endif
+
+#else
+#include <apr_thread_proc.h>
+#endif
+
+
+struct mpf_scheduler_t {
+ apr_pool_t *pool;
+ unsigned long resolution; /* scheduler resolution */
+ unsigned long rate; /* faster than real-time simulation */
+
+ unsigned long media_resolution;
+ mpf_scheduler_proc_f media_proc;
+ void *media_obj;
+
+ unsigned long timer_resolution;
+ unsigned long timer_elapsed_time;
+ mpf_scheduler_proc_f timer_proc;
+ void *timer_obj;
+
+#ifdef ENABLE_MULTIMEDIA_TIMERS
+ unsigned int timer_id;
+#else
+ apr_thread_t *thread;
+ apt_bool_t running;
+#endif
+};
+
+static APR_INLINE void mpf_scheduler_init(mpf_scheduler_t *scheduler);
+
+/** Create scheduler */
+MPF_DECLARE(mpf_scheduler_t*) mpf_scheduler_create(unsigned long rate, apr_pool_t *pool)
+{
+ mpf_scheduler_t *scheduler = apr_palloc(pool,sizeof(mpf_scheduler_t));
+ mpf_scheduler_init(scheduler);
+ scheduler->pool = pool;
+ scheduler->resolution = 0;
+ if(rate == 0 || rate > 10) {
+ /* rate shows how many times scheduler should be faster than real-time,
+ 1 is the defualt and probably the only reasonable value,
+ however, the rates up to 10 times faster should be acceptable */
+ rate = 1;
+ }
+ scheduler->rate = rate;
+
+ scheduler->media_resolution = 0;
+ scheduler->media_obj = NULL;
+ scheduler->media_proc = NULL;
+
+ scheduler->timer_resolution = 0;
+ scheduler->timer_elapsed_time = 0;
+ scheduler->timer_obj = NULL;
+ scheduler->timer_proc = NULL;
+ return scheduler;
+}
+
+/** Destroy scheduler */
+MPF_DECLARE(void) mpf_scheduler_destroy(mpf_scheduler_t *scheduler)
+{
+ /* nothing to destroy */
+}
+
+/** Set media processing clock */
+MPF_DECLARE(apt_bool_t) mpf_scheduler_media_clock_set(
+ mpf_scheduler_t *scheduler,
+ unsigned long resolution,
+ mpf_scheduler_proc_f proc,
+ void *obj)
+{
+ scheduler->media_resolution = resolution / scheduler->rate;
+ scheduler->media_proc = proc;
+ scheduler->media_obj = obj;
+ return TRUE;
+}
+
+/** Set timer clock */
+MPF_DECLARE(apt_bool_t) mpf_scheduler_timer_clock_set(
+ mpf_scheduler_t *scheduler,
+ unsigned long resolution,
+ mpf_scheduler_proc_f proc,
+ void *obj)
+{
+ scheduler->timer_resolution = resolution / scheduler->rate;
+ scheduler->timer_elapsed_time = 0;
+ scheduler->timer_proc = proc;
+ scheduler->timer_obj = obj;
+ return TRUE;
+}
+
+static APR_INLINE void mpf_scheduler_resolution_set(mpf_scheduler_t *scheduler)
+{
+ if(scheduler->media_resolution) {
+ scheduler->resolution = scheduler->media_resolution;
+ }
+ else if(scheduler->timer_resolution) {
+ scheduler->resolution = scheduler->timer_resolution;
+ }
+}
+
+
+
+#ifdef ENABLE_MULTIMEDIA_TIMERS
+
+static APR_INLINE void mpf_scheduler_init(mpf_scheduler_t *scheduler)
+{
+ scheduler->timer_id = 0;
+}
+
+static void CALLBACK mm_timer_proc(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
+{
+ mpf_scheduler_t *scheduler = (mpf_scheduler_t*) dwUser;
+ if(scheduler->media_proc) {
+ scheduler->media_proc(scheduler,scheduler->media_obj);
+ }
+
+ if(scheduler->timer_proc) {
+ scheduler->timer_elapsed_time += scheduler->resolution;
+ if(scheduler->timer_elapsed_time >= scheduler->timer_resolution) {
+ scheduler->timer_elapsed_time = 0;
+ scheduler->timer_proc(scheduler,scheduler->timer_obj);
+ }
+ }
+}
+
+/** Start scheduler */
+MPF_DECLARE(apt_bool_t) mpf_scheduler_start(mpf_scheduler_t *scheduler)
+{
+ mpf_scheduler_resolution_set(scheduler);
+ scheduler->timer_id = timeSetEvent(
+ scheduler->resolution, 0, mm_timer_proc, (DWORD_PTR) scheduler,
+ TIME_PERIODIC | TIME_CALLBACK_FUNCTION | TIME_KILL_SYNCHRONOUS);
+ return scheduler->timer_id ? TRUE : FALSE;
+}
+
+/** Stop scheduler */
+MPF_DECLARE(apt_bool_t) mpf_scheduler_stop(mpf_scheduler_t *scheduler)
+{
+ if(!scheduler) {
+ return FALSE;
+ }
+
+ timeKillEvent(scheduler->timer_id);
+ scheduler->timer_id = 0;
+ return TRUE;
+}
+
+#else
+
+static APR_INLINE void mpf_scheduler_init(mpf_scheduler_t *scheduler)
+{
+ scheduler->thread = NULL;
+ scheduler->running = FALSE;
+}
+
+static void* APR_THREAD_FUNC timer_thread_proc(apr_thread_t *thread, void *data)
+{
+ mpf_scheduler_t *scheduler = data;
+ apr_interval_time_t timeout = scheduler->resolution * 1000;
+ apr_interval_time_t time_drift = 0;
+ apr_time_t time_now, time_last;
+
+ time_now = apr_time_now();
+ while(scheduler->running == TRUE) {
+ time_last = time_now;
+
+ if(scheduler->media_proc) {
+ scheduler->media_proc(scheduler,scheduler->media_obj);
+ }
+
+ if(scheduler->timer_proc) {
+ scheduler->timer_elapsed_time += scheduler->resolution;
+ if(scheduler->timer_elapsed_time >= scheduler->timer_resolution) {
+ scheduler->timer_elapsed_time = 0;
+ scheduler->timer_proc(scheduler,scheduler->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;
+}
+
+MPF_DECLARE(apt_bool_t) mpf_scheduler_start(mpf_scheduler_t *scheduler)
+{
+ mpf_scheduler_resolution_set(scheduler);
+
+ scheduler->running = TRUE;
+ if(apr_thread_create(&scheduler->thread,NULL,timer_thread_proc,scheduler,scheduler->pool) != APR_SUCCESS) {
+ scheduler->running = FALSE;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+MPF_DECLARE(apt_bool_t) mpf_scheduler_stop(mpf_scheduler_t *scheduler)
+{
+ if(!scheduler) {
+ return FALSE;
+ }
+
+ scheduler->running = FALSE;
+ if(scheduler->thread) {
+ apr_status_t s;
+ apr_thread_join(&s,scheduler->thread);
+ scheduler->thread = NULL;
+ }
+ return TRUE;
+}
+
+#endif
--- /dev/null
+/*
+ * 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_stream.h"
+
+/** Create stream capabilities */
+MPF_DECLARE(mpf_stream_capabilities_t*) mpf_stream_capabilities_create(mpf_stream_direction_e direction, apr_pool_t *pool)
+{
+ mpf_stream_capabilities_t *capabilities = (mpf_stream_capabilities_t*)apr_palloc(pool,sizeof(mpf_stream_capabilities_t));
+ capabilities->direction = direction;
+ mpf_codec_capabilities_init(&capabilities->codecs,1,pool);
+ return capabilities;
+}
+
+/** Clone stream capabilities */
+MPF_DECLARE(mpf_stream_capabilities_t*) mpf_stream_capabilities_clone(const mpf_stream_capabilities_t *src_capabilities, apr_pool_t *pool)
+{
+ mpf_stream_capabilities_t *capabilities = (mpf_stream_capabilities_t*)apr_palloc(pool,sizeof(mpf_stream_capabilities_t));
+ capabilities->direction = src_capabilities->direction;
+ mpf_codec_capabilities_clone(&capabilities->codecs,&src_capabilities->codecs,pool);
+ return capabilities;
+}
+
+/** Merge stream capabilities */
+MPF_DECLARE(apt_bool_t) mpf_stream_capabilities_merge(mpf_stream_capabilities_t *capabilities, const mpf_stream_capabilities_t *src_capabilities, apr_pool_t *pool)
+{
+ capabilities->direction |= src_capabilities->direction;
+ return mpf_codec_capabilities_merge(&capabilities->codecs,&src_capabilities->codecs,pool);
+}
+
+
+
+/** Create audio stream */
+MPF_DECLARE(mpf_audio_stream_t*) mpf_audio_stream_create(void *obj, const mpf_audio_stream_vtable_t *vtable, const mpf_stream_capabilities_t *capabilities, apr_pool_t *pool)
+{
+ mpf_audio_stream_t *stream;
+ if(!vtable || !capabilities) {
+ return NULL;
+ }
+
+ /* validate required fields */
+ if(capabilities->direction & STREAM_DIRECTION_SEND) {
+ /* validate sink */
+ if(!vtable->write_frame) {
+ return NULL;
+ }
+ }
+ if(capabilities->direction & STREAM_DIRECTION_RECEIVE) {
+ /* validate source */
+ if(!vtable->read_frame) {
+ return NULL;
+ }
+ }
+
+ stream = (mpf_audio_stream_t*)apr_palloc(pool,sizeof(mpf_audio_stream_t));
+ stream->obj = obj;
+ stream->vtable = vtable;
+ stream->termination = NULL;
+ stream->capabilities = capabilities;
+ stream->direction = capabilities->direction;
+ stream->rx_descriptor = NULL;
+ stream->rx_event_descriptor = NULL;
+ stream->tx_descriptor = NULL;
+ stream->tx_event_descriptor = NULL;
+ return stream;
+}
+
+/** Validate audio stream receiver */
+MPF_DECLARE(apt_bool_t) mpf_audio_stream_rx_validate(
+ mpf_audio_stream_t *stream,
+ const mpf_codec_descriptor_t *descriptor,
+ const mpf_codec_descriptor_t *event_descriptor,
+ apr_pool_t *pool)
+{
+ if(!stream->capabilities) {
+ return FALSE;
+ }
+
+ if(!stream->rx_descriptor) {
+ stream->rx_descriptor = mpf_codec_descriptor_create_by_capabilities(&stream->capabilities->codecs,descriptor,pool);
+ }
+ if(!stream->rx_event_descriptor) {
+ if(stream->capabilities->codecs.allow_named_events == TRUE && event_descriptor) {
+ stream->rx_event_descriptor = apr_palloc(pool,sizeof(mpf_codec_descriptor_t));
+ *stream->rx_event_descriptor = *event_descriptor;
+ }
+ }
+
+ return stream->rx_descriptor ? TRUE : FALSE;
+}
+
+/** Validate audio stream transmitter */
+MPF_DECLARE(apt_bool_t) mpf_audio_stream_tx_validate(
+ mpf_audio_stream_t *stream,
+ const mpf_codec_descriptor_t *descriptor,
+ const mpf_codec_descriptor_t *event_descriptor,
+ apr_pool_t *pool)
+{
+ if(!stream->capabilities) {
+ return FALSE;
+ }
+
+ if(!stream->tx_descriptor) {
+ stream->tx_descriptor = mpf_codec_descriptor_create_by_capabilities(&stream->capabilities->codecs,descriptor,pool);
+ }
+ if(!stream->tx_event_descriptor) {
+ if(stream->capabilities->codecs.allow_named_events == TRUE && event_descriptor) {
+ stream->tx_event_descriptor = apr_palloc(pool,sizeof(mpf_codec_descriptor_t));
+ *stream->tx_event_descriptor = *event_descriptor;
+ }
+ }
+ return stream->tx_descriptor ? TRUE : FALSE;
+}
+
+/** Trace media path */
+MPF_DECLARE(void) mpf_audio_stream_trace(mpf_audio_stream_t *stream, mpf_stream_direction_e direction, apt_text_stream_t *output)
+{
+ if(stream->vtable->trace) {
+ stream->vtable->trace(stream,direction,output);
+ return;
+ }
+
+ if(direction & STREAM_DIRECTION_SEND) {
+ mpf_codec_descriptor_t *descriptor = stream->tx_descriptor;
+ if(descriptor) {
+ apr_size_t offset = output->pos - output->text.buf;
+ output->pos += apr_snprintf(output->pos, output->text.length - offset,
+ "[%s/%d/%d]->Sink",
+ descriptor->name.buf,
+ descriptor->sampling_rate,
+ descriptor->channel_count);
+ }
+ }
+ if(direction & STREAM_DIRECTION_RECEIVE) {
+ mpf_codec_descriptor_t *descriptor = stream->rx_descriptor;
+ if(descriptor) {
+ apr_size_t offset = output->pos - output->text.buf;
+ output->pos += apr_snprintf(output->pos, output->text.length - offset,
+ "Source->[%s/%d/%d]",
+ descriptor->name.buf,
+ descriptor->sampling_rate,
+ descriptor->channel_count);
+ }
+ }
+}
termination->event_handler_obj = NULL;
termination->event_handler = NULL;
termination->codec_manager = NULL;
+ termination->timer_manager = NULL;
termination->termination_factory = termination_factory;
termination->vtable = vtable;
termination->slot = 0;
return termination;
}
-MPF_DECLARE(apt_bool_t) mpf_termination_destroy(mpf_termination_t *termination)
+MPF_DECLARE(apt_bool_t) mpf_termination_add(mpf_termination_t *termination, void *descriptor)
{
- if(termination->vtable && termination->vtable->destroy) {
- termination->vtable->destroy(termination);
+ if(termination->vtable && termination->vtable->add) {
+ termination->vtable->add(termination,descriptor);
}
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) {
return TRUE;
}
-MPF_DECLARE(apt_bool_t) mpf_termination_validate(mpf_termination_t *termination)
+MPF_DECLARE(apt_bool_t) mpf_termination_subtract(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_codec_manager_default_codec_get(
- termination->codec_manager,
- termination->pool);
- }
- }
- if((audio_stream->mode & STREAM_MODE_SEND) == STREAM_MODE_SEND) {
- if(!audio_stream->tx_codec) {
- audio_stream->tx_codec = mpf_codec_manager_default_codec_get(
- termination->codec_manager,
- termination->pool);
- }
- }
+ if(termination->vtable && termination->vtable->subtract) {
+ termination->vtable->subtract(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);
-}
--- /dev/null
+/*
+ * 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_factory.h"
+#include "mpf_termination.h"
+
+/** Create MPF termination from 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);
+}
+
+MPF_DECLARE(apt_bool_t) mpf_termination_destroy(mpf_termination_t *termination)
+{
+ if(termination->vtable && termination->vtable->destroy) {
+ termination->vtable->destroy(termination);
+ }
+ return TRUE;
+}
+
+/** Get associated object. */
+MPF_DECLARE(void*) mpf_termination_object_get(mpf_termination_t *termination)
+{
+ return termination->obj;
+}
+
+/** Get audio stream. */
+MPF_DECLARE(mpf_audio_stream_t*) mpf_termination_audio_stream_get(mpf_termination_t *termination)
+{
+ return termination->audio_stream;
+}
+
+/** Get video stream. */
+MPF_DECLARE(mpf_video_stream_t*) mpf_termination_video_stream_get(mpf_termination_t *termination)
+{
+ return termination->video_stream;
+}
+++ /dev/null
-/*
- * 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 <mmsystem.h>
-#include <windows.h>
-
-#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; i<MAX_MEDIA_TIMERS; i++) {
- if(!media_timer_set[i].timer_id) {
- timer = &media_timer_set[i];
- break;
- }
- }
-
- if(timer) {
- timer->timer_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 <apr_thread_proc.h>
-
-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
--- /dev/null
+/*
+ * 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 <apr_ring.h>
+#include "mpf_timer_manager.h"
+#include "mpf_scheduler.h"
+#include "apt_log.h"
+
+/** MPF timer manager */
+struct mpf_timer_manager_t {
+ /** Ring head */
+ APR_RING_HEAD(mpf_timer_head_t, mpf_timer_t) head;
+
+ /** Clock resolution */
+ apr_uint32_t resolution;
+ /** Elapsed time */
+ apr_uint32_t elapsed_time;
+};
+
+/** MPF timer */
+struct mpf_timer_t {
+ /** Ring entry */
+ APR_RING_ENTRY(mpf_timer_t) link;
+
+ /** Back pointer to manager */
+ mpf_timer_manager_t *manager;
+ /** Time next report is scheduled at */
+ apr_uint32_t scheduled_time;
+
+ /** Timer proc */
+ mpf_timer_proc_f proc;
+ /** Timer object */
+ void *obj;
+};
+
+static void mpf_scheduler_proc(mpf_scheduler_t *scheduler, void *obj);
+
+/** Create timer manager */
+MPF_DECLARE(mpf_timer_manager_t*) mpf_timer_manager_create(mpf_scheduler_t *scheduler, apr_pool_t *pool)
+{
+ mpf_timer_manager_t *timer_manager = apr_palloc(pool,sizeof(mpf_timer_manager_t));
+ APR_RING_INIT(&timer_manager->head, mpf_timer_t, link);
+ timer_manager->elapsed_time = 0;
+ timer_manager->resolution = 100; // 100 ms
+
+ mpf_scheduler_timer_clock_set(scheduler,timer_manager->resolution,mpf_scheduler_proc,timer_manager);
+ return timer_manager;
+}
+
+/** Destroy timer manager */
+MPF_DECLARE(void) mpf_timer_manager_destroy(mpf_timer_manager_t *timer_manager)
+{
+}
+
+
+/** Create timer */
+MPF_DECLARE(mpf_timer_t*) mpf_timer_create(mpf_timer_manager_t *timer_manager, mpf_timer_proc_f proc, void *obj, apr_pool_t *pool)
+{
+ mpf_timer_t *timer = apr_palloc(pool,sizeof(mpf_timer_t));
+ timer->manager = timer_manager;
+ timer->scheduled_time = 0;
+ timer->proc = proc;
+ timer->obj = obj;
+ return timer;
+}
+
+static APR_INLINE apt_bool_t mpf_timer_insert(mpf_timer_manager_t *manager, mpf_timer_t *timer)
+{
+ mpf_timer_t *it;
+ for(it = APR_RING_LAST(&manager->head);
+ it != APR_RING_SENTINEL(&manager->head, mpf_timer_t, link);
+ it = APR_RING_PREV(it, link)) {
+
+ if(it->scheduled_time <= timer->scheduled_time) {
+ APR_RING_INSERT_AFTER(it,timer,link);
+ return TRUE;
+ }
+ }
+ APR_RING_INSERT_HEAD(&manager->head,timer,mpf_timer_t,link);
+ return TRUE;
+}
+
+/** Set one-shot timer */
+MPF_DECLARE(apt_bool_t) mpf_timer_set(mpf_timer_t *timer, apr_uint32_t timeout)
+
+{
+ mpf_timer_manager_t *manager = timer->manager;
+
+ if(timeout <= 0 || !timer->proc) {
+ return FALSE;
+ }
+
+ /* calculate time to elapse */
+ timer->scheduled_time = manager->elapsed_time + timeout;
+ apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Set Timer 0x%x [%d]",timer,timer->scheduled_time);
+
+ if(APR_RING_EMPTY(&timer->manager->head, mpf_timer_t, link)) {
+ APR_RING_INSERT_TAIL(&manager->head,timer,mpf_timer_t,link);
+ return TRUE;
+ }
+
+ /* insert new node (timer) to sorted by scheduled time list */
+ return mpf_timer_insert(manager,timer);
+}
+
+/** Kill timer */
+MPF_DECLARE(apt_bool_t) mpf_timer_kill(mpf_timer_t *timer)
+{
+ if(!timer->scheduled_time) {
+ return FALSE;
+ }
+
+ apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Kill Timer 0x%x [%d]",timer,timer->scheduled_time);
+ /* remove node (timer) from the list */
+ APR_RING_REMOVE(timer,link);
+ timer->scheduled_time = 0;
+
+ if(APR_RING_EMPTY(&timer->manager->head, mpf_timer_t, link)) {
+ /* reset elapsed time if no timers set */
+ timer->manager->elapsed_time = 0;
+ }
+ return TRUE;
+}
+
+static void mpf_timers_reschedule(mpf_timer_manager_t *manager)
+{
+ mpf_timer_t *it;
+ for(it = APR_RING_LAST(&manager->head);
+ it != APR_RING_SENTINEL(&manager->head, mpf_timer_t, link);
+ it = APR_RING_PREV(it, link)) {
+
+ it->scheduled_time -= manager->elapsed_time;
+ }
+ manager->elapsed_time = 0;
+}
+
+static void mpf_scheduler_proc(mpf_scheduler_t *scheduler, void *obj)
+{
+ mpf_timer_manager_t *manager = obj;
+ mpf_timer_t *timer;
+
+ if(APR_RING_EMPTY(&manager->head, mpf_timer_t, link)) {
+ /* just return, nothing to do */
+ return;
+ }
+
+ /* increment elapsed time */
+ manager->elapsed_time += manager->resolution;
+ if(manager->elapsed_time >= 0xFFFF) {
+ apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Reschedule Timers [%d]",manager->elapsed_time);
+ mpf_timers_reschedule(manager);
+ }
+
+ /* process timers */
+ do {
+ /* get first node (timer) */
+ timer = APR_RING_FIRST(&manager->head);
+
+ if(timer->scheduled_time > manager->elapsed_time) {
+ /* scheduled time is not elapsed yet */
+ break;
+ }
+
+ apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Timer Elapsed 0x%x [%d]",timer,timer->scheduled_time);
+ /* remove the elapsed timer from the list */
+ APR_RING_REMOVE(timer, link);
+ timer->scheduled_time = 0;
+ /* process the elapsed timer */
+ timer->proc(timer,timer->obj);
+ }
+ while(!APR_RING_EMPTY(&manager->head, mpf_timer_t, link));
+}
*/
MRCP_DECLARE(mrcp_session_t*) mrcp_application_session_create(mrcp_application_t *application, const char *profile, void *obj);
+/**
+ * Get memory pool the session object is created out of.
+ * @param session the session to get pool from
+ */
+MRCP_DECLARE(apr_pool_t*) mrcp_application_session_pool_get(mrcp_session_t *session);
+
+/**
+ * Get session identifier.
+ * @param session the session to get identifier of
+ */
+MRCP_DECLARE(const apt_str_t*) mrcp_application_session_id_get(mrcp_session_t *session);
+
/**
* 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);
+/**
+ * Set (associate) external object to the session.
+ * @param session the session to set object for
+ * @param obj the object to set
+ */
+MRCP_DECLARE(void) mrcp_application_session_object_set(mrcp_session_t *session, void *obj);
+
/**
* Send session update request.
* @param session the session to update
*/
MRCP_DECLARE(mpf_rtp_termination_descriptor_t*) mrcp_application_rtp_descriptor_get(mrcp_channel_t *channel);
+/**
+ * Get codec descriptor of source stream.
+ * @param channel the channel to get descriptor from
+ */
+MRCP_DECLARE(const mpf_codec_descriptor_t*) mrcp_application_source_descriptor_get(mrcp_channel_t *channel);
+
+/**
+ * Get codec descriptor of sink stream.
+ * @param channel the channel to get descriptor from
+ */
+MRCP_DECLARE(const mpf_codec_descriptor_t*) mrcp_application_sink_descriptor_get(mrcp_channel_t *channel);
+
/**
* Send channel add request.
* @param session the session to create channel for
*/
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
+/**
+ * Create audio termination
+ * @param session the session to create termination for
+ * @param stream_vtable the virtual table of audio stream
+ * @param capabilities the capabilities of the stream
+ * @param obj the external object
+ */
+MRCP_DECLARE(mpf_termination_t*) mrcp_application_audio_termination_create(
+ mrcp_session_t *session,
+ const mpf_audio_stream_vtable_t *stream_vtable,
+ mpf_stream_capabilities_t *capabilities,
+ void *obj);
+/**
+ * Create source media termination
+ * @param session the session to create termination 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
+ * @deprecated @see mrcp_application_audio_termination_create()
*/
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
+/**
+ * Create sink media termination
+ * @param session the session to create termination 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
+ * @deprecated @see mrcp_application_audio_termination_create()
*/
MRCP_DECLARE(mpf_termination_t*) mrcp_application_sink_termination_create(
mrcp_session_t *session,
#include "mrcp_client_types.h"
#include "mrcp_application.h"
#include "mrcp_session.h"
-#include "mpf_message.h"
+#include "mpf_engine.h"
#include "apt_task_msg.h"
#include "apt_obj_list.h"
/** MRCP client session declaration */
typedef struct mrcp_client_session_t mrcp_client_session_t;
+/** Client session states */
+typedef enum {
+ SESSION_STATE_NONE,
+ SESSION_STATE_GENERATING_OFFER,
+ SESSION_STATE_PROCESSING_ANSWER,
+ SESSION_STATE_TERMINATING,
+ SESSION_STATE_DISCOVERING
+} mrcp_client_session_state_e;
+
/** MRCP client session */
struct mrcp_client_session_t {
/** Session base */
- mrcp_session_t base;
+ mrcp_session_t base;
/** Application session belongs to */
- mrcp_application_t *application;
+ mrcp_application_t *application;
/** External object associated with session */
- void *app_obj;
+ void *app_obj;
/** Profile to use */
- mrcp_profile_t *profile;
+ mrcp_profile_t *profile;
/** Media context */
- mpf_context_t *context;
+ mpf_context_t *context;
/** Codec manager */
- const mpf_codec_manager_t *codec_manager;
+ const mpf_codec_manager_t *codec_manager;
/** RTP termination array (mrcp_termination_slot_t) */
- apr_array_header_t *terminations;
+ apr_array_header_t *terminations;
/** MRCP control channel array (mrcp_channel_t*) */
- apr_array_header_t *channels;
+ apr_array_header_t *channels;
/** Indicates whether session is already added to session table */
- apt_bool_t registered;
+ apt_bool_t registered;
/** In-progress offer */
- mrcp_session_descriptor_t *offer;
+ mrcp_session_descriptor_t *offer;
/** In-progress answer */
- mrcp_session_descriptor_t *answer;
+ mrcp_session_descriptor_t *answer;
/** MRCP application active request */
- const mrcp_app_message_t *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;
+ apt_obj_list_t *request_queue;
+
+ /** MPF task message, which construction is in progress */
+ mpf_task_msg_t *mpf_task_msg;
+
+ /** Session state */
+ mrcp_client_session_state_e state;
+ /** Status code of the app response to be generated */
+ mrcp_sig_status_code_e status;
+ /** Number of in-progress sub requests */
+ apr_size_t subrequest_count;
};
/** MRCP channel */
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 */
mpf_termination_t *termination;
/** RTP termination descriptor */
mpf_rtp_termination_descriptor_t *descriptor;
+ /** Associated MRCP channel */
+ mrcp_channel_t *channel;
+ /** media descriptor id (index of media in session descriptor) */
+ apr_size_t id;
};
/** Create channel */
mrcp_channel_t* mrcp_client_channel_create(
mrcp_session_t *session,
- mrcp_resource_id resource_id,
+ mrcp_resource_t *resource,
mpf_termination_t *termination,
mpf_rtp_termination_descriptor_t *rtp_descriptor,
void *obj);
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);
+apt_bool_t mrcp_client_mpf_message_process(mpf_message_container_t *mpf_message_container);
/** Process session answer */
apt_bool_t mrcp_client_session_answer_process(mrcp_client_session_t *session, mrcp_session_descriptor_t *descriptor);
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);
+/** Process disconnect event */
+apt_bool_t mrcp_client_on_disconnect(mrcp_channel_t *channel);
APT_END_EXTERN_C
#include <apr_hash.h>
#include "mrcp_client.h"
#include "mrcp_resource_factory.h"
+#include "mrcp_resource.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_termination_factory.h"
#include "mpf_codec_manager.h"
#include "apt_pool.h"
#include "apt_consumer_task.h"
CONNECTION_AGENT_TASK_MSG_MODIFY_CHANNEL,
CONNECTION_AGENT_TASK_MSG_REMOVE_CHANNEL,
CONNECTION_AGENT_TASK_MSG_RECEIVE_MESSAGE,
+ CONNECTION_AGENT_TASK_MSG_DISCONNECT
} connection_agent_task_msg_type_e ;
typedef struct connection_agent_task_msg_data_t connection_agent_task_msg_data_t;
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 apt_bool_t mrcp_client_disconnect_signal(mrcp_control_channel_t *channel);
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
+ mrcp_client_message_signal,
+ mrcp_client_disconnect_signal
};
/* Task interface */
return &session->base;
}
+/** Get memory pool the session object is created out of */
+MRCP_DECLARE(apr_pool_t*) mrcp_application_session_pool_get(mrcp_session_t *session)
+{
+ if(!session) {
+ return NULL;
+ }
+ return session->pool;
+}
+
+/** Get session identifier */
+MRCP_DECLARE(const apt_str_t*) mrcp_application_session_id_get(mrcp_session_t *session)
+{
+ if(!session) {
+ return NULL;
+ }
+ return &session->id;
+}
+
/** Get external object associated with the session */
MRCP_DECLARE(void*) mrcp_application_session_object_get(mrcp_session_t *session)
{
return client_session->app_obj;
}
+/** Set (associate) external object to the session */
+MRCP_DECLARE(void) mrcp_application_session_object_set(mrcp_session_t *session, void *obj)
+{
+ mrcp_client_session_t *client_session = (mrcp_client_session_t*)session;
+ if(client_session) {
+ client_session->app_obj = obj;
+ }
+}
/** Send session update request */
MRCP_DECLARE(apt_bool_t) mrcp_application_session_update(mrcp_session_t *session)
mpf_rtp_termination_descriptor_t *rtp_descriptor,
void *obj)
{
+ mrcp_resource_t *resource;
+ mrcp_profile_t *profile;
mrcp_client_session_t *client_session = (mrcp_client_session_t*)session;
if(!client_session || !client_session->profile) {
/* Invalid params */
return FALSE;
}
+ profile = client_session->profile;
+
+ if(!profile->resource_factory) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Channel: invalid profile");
+ return FALSE;
+ }
+ resource = mrcp_resource_get(profile->resource_factory,resource_id);
+ if(!resource) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Channel: no such resource");
+ 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) {
+ if(!profile->media_engine || !profile->rtp_termination_factory) {
apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Channel: invalid profile");
return FALSE;
}
return FALSE;
}
}
- return mrcp_client_channel_create(session,resource_id,termination,rtp_descriptor,obj);
+
+ return mrcp_client_channel_create(session,resource,termination,rtp_descriptor,obj);
}
/** Get external object associated with the channel */
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 NULL;
}
return channel->rtp_termination_slot->descriptor;
}
+/** Get codec descriptor of source stream */
+MRCP_DECLARE(const mpf_codec_descriptor_t*) mrcp_application_source_descriptor_get(mrcp_channel_t *channel)
+{
+ mpf_audio_stream_t *audio_stream;
+ if(!channel || !channel->termination) {
+ return NULL;
+ }
+ audio_stream = mpf_termination_audio_stream_get(channel->termination);
+ if(!audio_stream) {
+ return NULL;
+ }
+ return audio_stream->rx_descriptor;
+}
+
+/** Get codec descriptor of sink stream */
+MRCP_DECLARE(const mpf_codec_descriptor_t*) mrcp_application_sink_descriptor_get(mrcp_channel_t *channel)
+{
+ mpf_audio_stream_t *audio_stream;
+ if(!channel || !channel->termination) {
+ return NULL;
+ }
+ audio_stream = mpf_termination_audio_stream_get(channel->termination);
+ if(!audio_stream) {
+ return NULL;
+ }
+ return audio_stream->tx_descriptor;
+}
+
+
/** Send channel add request */
MRCP_DECLARE(apt_bool_t) mrcp_application_channel_add(mrcp_session_t *session, mrcp_channel_t *channel)
{
mrcp_message_t *mrcp_message;
mrcp_profile_t *profile;
mrcp_client_session_t *client_session = (mrcp_client_session_t*)session;
- if(!client_session || !channel) {
+ if(!client_session || !channel || !channel->resource) {
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);
- }
+ mrcp_message = mrcp_request_create(
+ channel->resource,
+ profile->signaling_agent->mrcp_version,
+ method_id,
+ session->pool);
return mrcp_message;
}
return mrcp_app_control_task_msg_signal(session,channel,message);
}
+/**
+ * Create audio termination
+ * @param session the session to create termination for
+ * @param stream_vtable the virtual table of audio stream
+ * @param capabilities the capabilities of the stream
+ * @param obj the external object
+ */
+MRCP_DECLARE(mpf_termination_t*) mrcp_application_audio_termination_create(
+ mrcp_session_t *session,
+ const mpf_audio_stream_vtable_t *stream_vtable,
+ mpf_stream_capabilities_t *capabilities,
+ void *obj)
+{
+ mpf_audio_stream_t *audio_stream;
+
+ if(!capabilities) {
+ return NULL;
+ }
+
+ if(mpf_codec_capabilities_validate(&capabilities->codecs) == FALSE) {
+ return NULL;
+ }
+
+ /* create audio stream */
+ audio_stream = mpf_audio_stream_create(
+ obj, /* object to associate */
+ stream_vtable, /* virtual methods table of audio stream */
+ capabilities, /* stream capabilities */
+ session->pool); /* memory pool to allocate memory from */
+ if(!audio_stream) {
+ return NULL;
+ }
+
+ /* 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 */
+}
+
/** Create source media termination */
MRCP_DECLARE(mpf_termination_t*) mrcp_application_source_termination_create(
mrcp_session_t *session,
mpf_codec_descriptor_t *codec_descriptor,
void *obj)
{
+ mpf_stream_capabilities_t *capabilities;
mpf_audio_stream_t *audio_stream;
+
+ capabilities = mpf_source_stream_capabilities_create(session->pool);
+ if(codec_descriptor) {
+ mpf_codec_capabilities_add(
+ &capabilities->codecs,
+ mpf_sample_rate_mask_get(codec_descriptor->sampling_rate),
+ codec_descriptor->name.buf);
+ }
+ else {
+ mpf_codec_default_capabilities_add(&capabilities->codecs);
+ }
+
/* 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 */
+ capabilities, /* stream capabilities */
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);
+ if(!audio_stream) {
+ return NULL;
}
+ audio_stream->rx_descriptor = codec_descriptor;
+
/* create raw termination */
return mpf_raw_termination_create(
NULL, /* no object to associate */
mpf_codec_descriptor_t *codec_descriptor,
void *obj)
{
+ mpf_stream_capabilities_t *capabilities;
mpf_audio_stream_t *audio_stream;
+
+ capabilities = mpf_sink_stream_capabilities_create(session->pool);
+ if(codec_descriptor) {
+ mpf_codec_capabilities_add(
+ &capabilities->codecs,
+ mpf_sample_rate_mask_get(codec_descriptor->sampling_rate),
+ codec_descriptor->name.buf);
+ }
+ else {
+ mpf_codec_default_capabilities_add(&capabilities->codecs);
+ }
+
/* 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 */
+ capabilities, /* stream capabilities */
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);
+ if(!audio_stream) {
+ return NULL;
}
+ audio_stream->tx_descriptor = codec_descriptor;
+
/* create raw termination */
return mpf_raw_termination_create(
NULL, /* no object to associate */
case CONNECTION_AGENT_TASK_MSG_RECEIVE_MESSAGE:
mrcp_client_on_message_receive(data->channel,data->message);
break;
+ case CONNECTION_AGENT_TASK_MSG_DISCONNECT:
+ mrcp_client_on_disconnect(data->channel);
+ break;
default:
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);
+ mpf_message_container_t *container = (mpf_message_container_t*) msg->data;
+ apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Receive Media Task Message");
+ mrcp_client_mpf_message_process(container);
break;
}
case MRCP_CLIENT_APPLICATION_TASK_MSG:
mrcp_message,
TRUE);
}
+
+static apt_bool_t mrcp_client_disconnect_signal(mrcp_control_channel_t *channel)
+{
+ return mrcp_client_connection_task_msg_signal(
+ CONNECTION_AGENT_TASK_MSG_DISCONNECT,
+ channel->agent,
+ channel,
+ NULL,
+ NULL,
+ TRUE);
+}
*/
#include "mrcp_client_session.h"
-#include "mrcp_resource.h"
#include "mrcp_resource_factory.h"
+#include "mrcp_resource.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_termination_factory.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"
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_response_raise(mrcp_client_session_t *session, 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 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);
+static APR_INLINE mrcp_version_e mrcp_session_version_get(mrcp_client_session_t *session)
+{
+ return session->base.signaling_agent->mrcp_version;
+}
+
+static APR_INLINE void mrcp_client_session_state_set(mrcp_client_session_t *session, mrcp_client_session_state_e state)
+{
+ if(session->subrequest_count != 0) {
+ /* error case */
+ session->subrequest_count = 0;
+ }
+ session->state = state;
+}
+
+static APR_INLINE void mrcp_client_session_subrequest_add(mrcp_client_session_t *session)
+{
+ session->subrequest_count++;
+}
+
+static APR_INLINE apt_bool_t mrcp_client_session_subrequest_remove(mrcp_client_session_t *session)
+{
+ if(!session->subrequest_count) {
+ /* error case */
+ return FALSE;
+ }
+
+ session->subrequest_count--;
+ return (session->subrequest_count ? FALSE : TRUE);
+}
+
+static 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;
+}
+
mrcp_client_session_t* mrcp_client_session_create(mrcp_application_t *application, void *obj)
{
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;
+ session->mpf_task_msg = NULL;
+ session->subrequest_count = 0;
+ session->state = SESSION_STATE_NONE;
+ session->status = MRCP_SIG_STATUS_CODE_SUCCESS;
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,
+ mrcp_session_t *session,
+ mrcp_resource_t *resource,
+ 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->resource = resource;
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;
+ rtp_termination_slot_t *termination_slot = apr_palloc(session->pool,sizeof(rtp_termination_slot_t));
+ termination_slot->descriptor = rtp_descriptor;
+ termination_slot->termination = NULL;
+ termination_slot->waiting = FALSE;
+ termination_slot->channel = channel;
+ termination_slot->id = 0;
+ channel->rtp_termination_slot = termination_slot;
}
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;
+ session->status = MRCP_SIG_STATUS_CODE_FAILURE;
/* raise app response */
- return mrcp_app_sig_response_raise(session,status_code,TRUE);
+ return mrcp_app_sig_response_raise(session,TRUE);
}
apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Receive Answer "APT_PTRSID_FMT" [c:%d a:%d v:%d]",
descriptor->audio_media_arr->nelts,
descriptor->video_media_arr->nelts);
- if(session->base.signaling_agent->mrcp_version == MRCP_VERSION_1) {
+ mrcp_client_session_state_set(session,SESSION_STATE_PROCESSING_ANSWER);
+ if(session->context) {
+ /* first, reset/destroy existing associations and topology */
+ if(mpf_engine_topology_message_add(
+ session->profile->media_engine,
+ MPF_RESET_ASSOCIATIONS,session->context,
+ &session->mpf_task_msg) == TRUE){
+ mrcp_client_session_subrequest_add(session);
+ }
+ }
+
+ if(mrcp_session_version_get(session) == MRCP_VERSION_1) {
if(mrcp_client_resource_answer_process(session,descriptor) != TRUE) {
- status_code = MRCP_SIG_STATUS_CODE_FAILURE;
+ session->status = MRCP_SIG_STATUS_CODE_FAILURE;
}
}
else {
mrcp_client_av_media_answer_process(session,descriptor);
}
+ if(session->context) {
+ /* apply topology based on assigned associations */
+ if(mpf_engine_topology_message_add(
+ session->profile->media_engine,
+ MPF_APPLY_TOPOLOGY,session->context,
+ &session->mpf_task_msg) == TRUE) {
+ mrcp_client_session_subrequest_add(session);
+ }
+
+ mpf_engine_message_send(session->profile->media_engine,&session->mpf_task_msg);
+ }
+
/* store received answer */
session->answer = descriptor;
- if(!session->answer_flag_count) {
+ if(!session->subrequest_count) {
/* raise app response */
- mrcp_app_sig_response_raise(session,status_code,TRUE);
+ mrcp_app_sig_response_raise(session,TRUE);
}
return TRUE;
{
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) {
+ if(mrcp_client_session_subrequest_remove(session) == TRUE) {
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->state == SESSION_STATE_TERMINATING) {
+ /* session termination request has been sent, still waiting for the response,
+ all the events must be ignored at this stage */
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unexpected Event! "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base));
+ return FALSE;
+ }
+
if(session->active_request) {
/* raise app response */
- mrcp_app_sig_response_raise(session,MRCP_SIG_STATUS_CODE_TERMINATE,FALSE);
+ session->status = MRCP_SIG_STATUS_CODE_TERMINATE;
+ mrcp_app_sig_response_raise(session,FALSE);
- /* cancel remaing requests (if any) */
+ /* cancel remaing requests, but do process session termination request (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);
+ const mrcp_app_message_t *app_message = session->active_request;
+ if(app_message->message_type == MRCP_APP_MESSAGE_TYPE_SIGNALING &&
+ app_message->sig_message.command_id == MRCP_SIG_COMMAND_SESSION_TERMINATE) {
+ /* process session termination */
+ mrcp_app_request_dispatch(session,app_message);
+ break;
+ }
+
+ /* cancel pending request */
+ session->status = MRCP_SIG_STATUS_CODE_CANCEL;
+ mrcp_app_sig_response_raise(session,FALSE);
}
}
while(session->active_request);
if(!descriptor) {
/* raise app response */
- return mrcp_app_sig_response_raise(session,MRCP_SIG_STATUS_CODE_FAILURE,TRUE);
+ session->status = MRCP_SIG_STATUS_CODE_FAILURE;
+ return mrcp_app_sig_response_raise(session,TRUE);
}
- if(session->base.signaling_agent->mrcp_version == MRCP_VERSION_1) {
+ if(mrcp_session_version_get(session) == 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 = mrcp_control_descriptor_create(session->base.pool);
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) {
+ if(mrcp_client_session_subrequest_remove(session) == TRUE) {
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;
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);
- }
+ if(mrcp_client_session_subrequest_remove(session) == TRUE) {
+ /* send offer to server */
+ mrcp_client_session_offer_send(session);
}
return TRUE;
}
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);
+ if(mrcp_client_session_subrequest_remove(session) == TRUE) {
+ /* raise app response */
+ if(status != TRUE) {
+ session->status = MRCP_SIG_STATUS_CODE_FAILURE;
}
+ mrcp_app_sig_response_raise(session,TRUE);
}
return TRUE;
}
return FALSE;
}
channel->waiting_for_channel = FALSE;
- if(session->terminate_flag_count) {
- session->terminate_flag_count--;
- if(!session->terminate_flag_count) {
+ if(mrcp_client_session_subrequest_remove(session) == TRUE) {
mrcp_app_session_terminate_raise(
session,
status == TRUE ? MRCP_SIG_STATUS_CODE_SUCCESS : MRCP_SIG_STATUS_CODE_FAILURE);
- }
}
return TRUE;
}
return mrcp_app_control_message_raise(session,channel,message);
}
+apt_bool_t mrcp_client_on_disconnect(mrcp_channel_t *channel)
+{
+ mrcp_client_session_t *session = (mrcp_client_session_t*)channel->session;
+ return mrcp_client_session_terminate_event_process(session);
+}
+
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));
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;
int i;
mrcp_channel_t *channel;
for(i=0; i<session->channels->nelts; i++) {
- channel = ((mrcp_channel_t**)session->channels->elts)[i];
+ channel = APR_ARRAY_IDX(session->channels,i,mrcp_channel_t*);
if(!channel) continue;
if(channel->control_channel) {
mrcp_client_session_remove(session->application->client,session);
/* raise app response */
- return mrcp_app_sig_response_raise(session,status,FALSE);
+ if(status != MRCP_SIG_STATUS_CODE_SUCCESS) {
+ session->status = status;
+ }
+ return mrcp_app_sig_response_raise(session,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)
+static apt_bool_t mrcp_app_sig_response_raise(mrcp_client_session_t *session, apt_bool_t process_pending_requests)
{
mrcp_app_message_t *response;
const mrcp_app_message_t *request = session->active_request;
return FALSE;
}
session->active_request = NULL;
- response = mrcp_client_app_response_create(request,status,session->base.pool);
+ response = mrcp_client_app_response_create(request,session->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->status == MRCP_SIG_STATUS_CODE_SUCCESS ? "SUCCESS" : "FAILURE",
+ session->status);
session->application->handler(response);
if(process_pending_requests) {
static apt_bool_t mrcp_client_channel_find(mrcp_client_session_t *session, mrcp_channel_t *channel, int *index)
{
int i;
+ mrcp_channel_t *existing_channel;
for(i=0; i<session->channels->nelts; i++) {
- mrcp_channel_t *existing_channel = ((mrcp_channel_t**)session->channels->elts)[i];
+ existing_channel = APR_ARRAY_IDX(session->channels,i,mrcp_channel_t*);
if(existing_channel == channel) {
if(index) {
*index = i;
int i;
rtp_termination_slot_t *slot;
for(i=0; i<session->terminations->nelts; i++) {
- slot = &((rtp_termination_slot_t*)session->terminations->elts)[i];
+ slot = &APR_ARRAY_IDX(session->terminations,i,rtp_termination_slot_t);
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; i<descriptor->audio_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; i<session->channels->nelts; i++) {
- channel = ((mrcp_channel_t**)session->channels->elts)[i];
+ channel = APR_ARRAY_IDX(session->channels,i,mrcp_channel_t*);
if(!channel) continue;
if(channel->termination == termination) {
int i;
mrcp_channel_t *channel;
for(i=0; i<session->channels->nelts; i++) {
- channel = ((mrcp_channel_t**)session->channels->elts)[i];
- if(!channel) continue;
+ channel = APR_ARRAY_IDX(session->channels,i,mrcp_channel_t*);
+ if(!channel || !channel->resource) continue;
- if(apt_string_compare(channel->resource_name,resource_name) == TRUE) {
+ if(apt_string_compare(&channel->resource->name,resource_name) == TRUE) {
return channel;
}
}
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]",
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Send MRCP Request "APT_PTRSIDRES_FMT" [%"MRCP_REQUEST_ID_FMT"]",
MRCP_SESSION_PTRSID(&session->base),
- channel->resource_name->buf,
+ channel->resource->name.buf,
message->start_line.request_id);
if(channel->control_channel) {
if(!session->offer) {
return FALSE;
}
- if(!channel->resource_name) {
+ if(!channel->resource) {
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,
+ 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;
- }
+ control_media->port = (enable == TRUE) ? TCP_DISCARD_PORT : 0;
+ }
+ if(channel->termination && channel->rtp_termination_slot) {
+ mpf_audio_stream_t *audio_stream = mpf_termination_audio_stream_get(
+ channel->termination);
+ mpf_rtp_media_descriptor_t *audio_media = mrcp_session_audio_media_get(
+ session->offer,
+ channel->rtp_termination_slot->id);
+ if(audio_media && audio_stream) {
+ mpf_stream_direction_e direction = mpf_stream_reverse_direction_get(audio_stream->direction);
+ if(enable == TRUE) {
+ audio_media->direction |= direction;
+ }
+ else {
+ audio_media->direction &= ~direction;
}
+ audio_media->state = (audio_media->direction != STREAM_DIRECTION_NONE) ? MPF_MEDIA_ENABLED : MPF_MEDIA_DISABLED;
}
}
}
- session->offer->resource_name = *channel->resource_name;
+ 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;
+ rtp_termination_slot_t *slot;
apr_pool_t *pool = session->base.pool;
mrcp_profile_t *profile = session->profile;
if(mrcp_client_channel_find(session,channel,NULL) == 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;
+
+ mrcp_client_session_state_set(session,SESSION_STATE_GENERATING_OFFER);
+
+ if(mrcp_session_version_get(session) == MRCP_VERSION_1) {
+ session->offer->resource_name = channel->resource->name;
session->offer->resource_state = TRUE;
}
else {
+ mrcp_control_descriptor_t *control_media;
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;
+ mrcp_cmid_add(control_media->cmid_arr,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++;
+ mrcp_client_session_subrequest_add(session);
}
}
- /* 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;
+ channel->resource->name.buf);
+ /* add control channel */
+ APR_ARRAY_PUSH(session->channels,mrcp_channel_t*) = channel;
+
+ /* add rtp termination slot */
+ slot = apr_array_push(session->terminations);
+ slot->waiting = FALSE;
+ slot->termination = NULL;
+ slot->descriptor = NULL;
+ slot->channel = channel;
+ slot->id = 0;
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++;
- }
- }
+ /* media termination mode */
+ mpf_termination_t *termination;
+ mpf_audio_stream_t *audio_stream;
- 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;
+ if(!session->context) {
+ /* create media context first */
+ session->context = mpf_engine_context_create(profile->media_engine,session,5,pool);
+ }
+ if(mpf_engine_termination_message_add(
+ profile->media_engine,
+ MPF_ADD_TERMINATION,session->context,channel->termination,NULL,
+ &session->mpf_task_msg) == TRUE) {
+ channel->waiting_for_termination = TRUE;
+ mrcp_client_session_subrequest_add(session);
}
- }
- 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) {
+ audio_stream = mpf_termination_audio_stream_get(channel->termination);
+ if(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);
+ media->state = MPF_MEDIA_ENABLED;
+ media->direction = mpf_stream_reverse_direction_get(audio_stream->direction);
rtp_descriptor->audio.local = media;
}
+
+ apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Add RTP Termination "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base));
+ /* create rtp termination */
+ termination = mpf_termination_create(profile->rtp_termination_factory,session,pool);
+ slot->termination = termination;
+
/* 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++;
+ if(mpf_engine_termination_message_add(
+ profile->media_engine,
+ MPF_ADD_TERMINATION,session->context,termination,rtp_descriptor,
+ &session->mpf_task_msg) == TRUE) {
+ slot->waiting = TRUE;
+ mrcp_client_session_subrequest_add(session);
}
+ mpf_engine_message_send(profile->media_engine,&session->mpf_task_msg);
+ }
+ else {
+ /* bypass media mode */
+ if(channel->rtp_termination_slot) {
+ rtp_descriptor = channel->rtp_termination_slot->descriptor;
+ if(rtp_descriptor) {
+ if(rtp_descriptor->audio.local) {
+ session->offer->ip = rtp_descriptor->audio.local->ip;
+ session->offer->ext_ip = rtp_descriptor->audio.local->ext_ip;
+ rtp_descriptor->audio.local->id = mrcp_session_audio_media_add(session->offer,rtp_descriptor->audio.local);
+ rtp_descriptor->audio.local->mid = session->offer->audio_media_arr->nelts;
+ slot->id = session->offer->audio_media_arr->nelts - 1;
+ }
+ }
+ }
}
- termination_slot->descriptor = rtp_descriptor;
- channel->rtp_termination_slot = termination_slot;
- if(!session->offer_flag_count) {
+ slot->descriptor = rtp_descriptor;
+ channel->rtp_termination_slot = slot;
+
+ if(!session->subrequest_count) {
/* send offer to server */
mrcp_client_session_offer_send(session);
}
apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Terminate Session "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base));
profile = session->profile;
+
+ mrcp_client_session_state_set(session,SESSION_STATE_TERMINATING);
+ if(session->context) {
+ /* first destroy existing topology */
+ if(mpf_engine_topology_message_add(
+ session->profile->media_engine,
+ MPF_DESTROY_TOPOLOGY,session->context,
+ &session->mpf_task_msg) == TRUE){
+ mrcp_client_session_subrequest_add(session);
+ }
+ }
/* remove existing control channels */
for(i=0; i<session->channels->nelts; i++) {
/* get existing channel */
- channel = *((mrcp_channel_t**)session->channels->elts + i);
+ channel = APR_ARRAY_IDX(session->channels,i,mrcp_channel_t*);
if(!channel) continue;
if(channel->control_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++;
+ mrcp_client_session_subrequest_add(session);
}
}
- 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++;
- }
+ /* 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(mpf_engine_termination_message_add(
+ profile->media_engine,
+ MPF_SUBTRACT_TERMINATION,session->context,channel->termination,NULL,
+ &session->mpf_task_msg) == TRUE) {
+ channel->waiting_for_termination = TRUE;
+ mrcp_client_session_subrequest_add(session);
}
}
}
- /* subtract existing terminations */
- for(i=0; i<session->terminations->nelts; i++) {
- /* get existing termination */
- slot = &((rtp_termination_slot_t*)session->terminations->elts)[i];
- if(!slot || !slot->termination) continue;
+ if(session->context) {
+ /* subtract existing terminations */
+ for(i=0; i<session->terminations->nelts; i++) {
+ /* get existing termination */
+ slot = &APR_ARRAY_IDX(session->terminations,i,rtp_termination_slot_t);
+ 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++;
+ /* send subtract termination request */
+ apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Subtract Termination "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base));
+ if(mpf_engine_termination_message_add(
+ profile->media_engine,
+ MPF_SUBTRACT_TERMINATION,session->context,slot->termination,NULL,
+ &session->mpf_task_msg) == TRUE) {
+ slot->waiting = TRUE;
+ mrcp_client_session_subrequest_add(session);
+ }
}
+
+ mpf_engine_message_send(profile->media_engine,&session->mpf_task_msg);
}
- session->terminate_flag_count++;
+ mrcp_client_session_subrequest_add(session);
mrcp_session_terminate_request(&session->base);
return TRUE;
}
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_client_session_state_set(session,SESSION_STATE_DISCOVERING);
+
+ if(mrcp_session_version_get(session) == MRCP_VERSION_1) {
+ mrcp_resource_t *resource;
mrcp_resource_id i;
for(i=0; i<MRCP_RESOURCE_TYPE_COUNT; i++) {
- resource_name = mrcp_resource_name_get(session->profile->resource_factory,i);
- if(!resource_name) continue;
+ resource = mrcp_resource_get(session->profile->resource_factory,i);
+ if(!resource) continue;
descriptor = mrcp_session_descriptor_create(session->base.pool);
- apt_string_copy(&descriptor->resource_name,resource_name,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++;
+ mrcp_client_session_subrequest_add(session);
}
}
}
else {
if(mrcp_session_discover_request(&session->base,descriptor) == TRUE) {
- session->answer_flag_count++;
+ mrcp_client_session_subrequest_add(session);
}
}
- if(session->answer_flag_count == 0) {
- mrcp_app_sig_response_raise(session,MRCP_SIG_STATUS_CODE_FAILURE,TRUE);
+ if(session->subrequest_count == 0) {
+ session->status = MRCP_SIG_STATUS_CODE_FAILURE;
+ mrcp_app_sig_response_raise(session,TRUE);
}
return TRUE;
}
-static apt_bool_t mrcp_client_on_termination_add(mrcp_client_session_t *session, mpf_message_t *mpf_message)
+static apt_bool_t mrcp_client_on_termination_add(mrcp_client_session_t *session, const mpf_message_t *mpf_message)
{
rtp_termination_slot_t *termination_slot;
if(!session) {
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);
+ session->offer->ip = rtp_descriptor->audio.local->ip;
+ session->offer->ext_ip = rtp_descriptor->audio.local->ext_ip;
+ rtp_descriptor->audio.local->id = mrcp_session_audio_media_add(session->offer,rtp_descriptor->audio.local);
rtp_descriptor->audio.local->mid = session->offer->audio_media_arr->nelts;
+ termination_slot->id = session->offer->audio_media_arr->nelts - 1;
}
- 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(mrcp_client_session_subrequest_remove(session) == TRUE) {
+ /* send offer to server */
+ mrcp_client_session_offer_send(session);
}
}
else {
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);
- }
+ if(mrcp_client_session_subrequest_remove(session) == TRUE) {
+ /* 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)
+static apt_bool_t mrcp_client_on_termination_modify(mrcp_client_session_t *session, const mpf_message_t *mpf_message)
{
rtp_termination_slot_t *termination_slot;
if(!session) {
termination_slot->waiting = FALSE;
termination_slot->descriptor = mpf_message->descriptor;;
- if(session->offer_flag_count) {
- session->offer_flag_count--;
- if(!session->offer_flag_count) {
+ if(mrcp_client_session_subrequest_remove(session) == TRUE) {
+ if(session->state == SESSION_STATE_GENERATING_OFFER) {
/* send offer to server */
mrcp_client_session_offer_send(session);
}
- }
- if(session->answer_flag_count) {
- session->answer_flag_count--;
- if(!session->answer_flag_count) {
+ else if(session->state == SESSION_STATE_PROCESSING_ANSWER) {
/* raise app response */
- mrcp_app_sig_response_raise(session,MRCP_SIG_STATUS_CODE_SUCCESS,TRUE);
+ mrcp_app_sig_response_raise(session,TRUE);
}
}
}
return TRUE;
}
-static apt_bool_t mrcp_client_on_termination_subtract(mrcp_client_session_t *session, mpf_message_t *mpf_message)
+static apt_bool_t mrcp_client_on_termination_subtract(mrcp_client_session_t *session, const mpf_message_t *mpf_message)
{
rtp_termination_slot_t *termination_slot;
if(!session) {
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);
- }
+ if(mrcp_client_session_subrequest_remove(session) == TRUE) {
+ mrcp_app_session_terminate_raise(session,MRCP_SIG_STATUS_CODE_SUCCESS);
}
}
else {
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);
- }
+ if(mrcp_client_session_subrequest_remove(session) == TRUE) {
+ /* raise app response */
+ mrcp_app_sig_response_raise(session,TRUE);
}
}
}
return TRUE;
}
-apt_bool_t mrcp_client_mpf_message_process(mpf_message_t *mpf_message)
+apt_bool_t mrcp_client_mpf_message_process(mpf_message_container_t *mpf_message_container)
{
- 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;
+ apr_size_t i;
+ mrcp_client_session_t *session;
+ const mpf_message_t *mpf_message;
+ for(i=0; i<mpf_message_container->count; i++) {
+ mpf_message = &mpf_message_container->messages[i];
+ if(mpf_message->context) {
+ session = mpf_engine_context_object_get(mpf_message->context);
+ }
+ else {
+ session = NULL;
+ }
+ if(mpf_message->message_type == MPF_MESSAGE_TYPE_RESPONSE) {
+ switch(mpf_message->command_id) {
+ case MPF_ADD_TERMINATION:
+ 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_MODIFY_TERMINATION:
+ 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_SUBTRACT_TERMINATION:
+ 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;
+ case MPF_ADD_ASSOCIATION:
+ case MPF_REMOVE_ASSOCIATION:
+ case MPF_RESET_ASSOCIATIONS:
+ case MPF_APPLY_TOPOLOGY:
+ case MPF_DESTROY_TOPOLOGY:
+ if(mrcp_client_session_subrequest_remove(session) == TRUE) {
+ if(session->state == SESSION_STATE_GENERATING_OFFER) {
+ /* send offer to server */
+ mrcp_client_session_offer_send(session);
+ }
+ else if(session->state == SESSION_STATE_PROCESSING_ANSWER) {
+ /* raise app response */
+ mrcp_app_sig_response_raise(session,TRUE);
+ }
+ }
+ 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));
}
- }
- 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;
}
/* update existing control channels */
for(i=0; i<count; i++) {
/* get existing channel */
- channel = *((mrcp_channel_t**)session->channels->elts + i);
+ channel = APR_ARRAY_IDX(session->channels,i,mrcp_channel_t*);
if(!channel) continue;
/* get control descriptor */
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++;
+ mrcp_client_session_subrequest_add(session);
}
}
return TRUE;
mpf_rtp_media_descriptor_t *remote_media;
mpf_rtp_termination_descriptor_t *rtp_descriptor;
/* get existing termination */
- slot = &((rtp_termination_slot_t*)session->terminations->elts)[i];
+ slot = &APR_ARRAY_IDX(session->terminations,i,rtp_termination_slot_t);
if(!slot) continue;
remote_media = mrcp_session_audio_media_get(descriptor,i);
/* 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) {
+ if(mpf_engine_termination_message_add(
+ session->profile->media_engine,
+ MPF_MODIFY_TERMINATION,session->context,slot->termination,rtp_descriptor,
+ &session->mpf_task_msg) == TRUE) {
slot->waiting = TRUE;
- session->answer_flag_count++;
+ mrcp_client_session_subrequest_add(session);
+ }
+ if(slot->channel && slot->channel->termination) {
+ if(mpf_engine_assoc_message_add(
+ session->profile->media_engine,
+ MPF_ADD_ASSOCIATION,session->context,slot->termination,slot->channel->termination,
+ &session->mpf_task_msg) == TRUE) {
+ mrcp_client_session_subrequest_add(session);
+ }
}
}
}
static apt_bool_t mrcp_app_request_dispatch(mrcp_client_session_t *session, const mrcp_app_message_t *app_message)
{
+ if(session->state == SESSION_STATE_TERMINATING) {
+ /* no more requests are allowed, as session is being terminated!
+ just return, it is horribly wrong and can crash anytime here */
+ apt_log(APT_LOG_MARK,APT_PRIO_ERROR,"Inappropriate Application Request "APT_PTRSID_FMT" [%d]",
+ MRCP_SESSION_PTRSID(&session->base),
+ app_message->sig_message.command_id);
+ return FALSE;
+ }
+
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;
}
+ session->status = MRCP_SIG_STATUS_CODE_SUCCESS;
switch(app_message->message_type) {
case MRCP_APP_MESSAGE_TYPE_SIGNALING:
{
}
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);
-}
noinst_LTLIBRARIES = libmrcpengine.la
-include_HEADERS = include/mrcp_resource_plugin.h \
- include/mrcp_resource_engine.h
+include_HEADERS = include/mrcp_engine_types.h \
+ include/mrcp_engine_plugin.h \
+ include/mrcp_engine_iface.h \
+ include/mrcp_engine_impl.h \
+ include/mrcp_synth_engine.h \
+ include/mrcp_recog_engine.h \
+ include/mrcp_recorder_engine.h \
+ include/mrcp_resource_engine.h \
+ include/mrcp_engine_factory.h \
+ include/mrcp_engine_loader.h \
+ include/mrcp_state_machine.h \
+ include/mrcp_synth_state_machine.h \
+ include/mrcp_recog_state_machine.h \
+ include/mrcp_recorder_state_machine.h
-libmrcpengine_la_SOURCES = src/mrcp_resource_engine.c
+libmrcpengine_la_SOURCES = src/mrcp_engine_iface.c \
+ src/mrcp_engine_impl.c \
+ src/mrcp_engine_factory.c \
+ src/mrcp_engine_loader.c \
+ src/mrcp_synth_state_machine.c \
+ src/mrcp_recog_state_machine.c \
+ src/mrcp_recorder_state_machine.c
--- /dev/null
+/*
+ * 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_ENGINE_FACTORY_H__
+#define __MRCP_ENGINE_FACTORY_H__
+
+/**
+ * @file mrcp_engine_factory.h
+ * @brief Factory of MRCP Engines
+ */
+
+#include "mrcp_engine_iface.h"
+
+APT_BEGIN_EXTERN_C
+
+/** Opaque engine factory declaration */
+typedef struct mrcp_engine_factory_t mrcp_engine_factory_t;
+
+/** Create engine factory */
+MRCP_DECLARE(mrcp_engine_factory_t*) mrcp_engine_factory_create(apr_pool_t *pool);
+
+/** Destroy registered engines and the factory */
+MRCP_DECLARE(apt_bool_t) mrcp_engine_factory_destroy(mrcp_engine_factory_t *factory);
+
+/** Open registered engines */
+MRCP_DECLARE(apt_bool_t) mrcp_engine_factory_open(mrcp_engine_factory_t *factory);
+
+/** Close registered engines */
+MRCP_DECLARE(apt_bool_t) mrcp_engine_factory_close(mrcp_engine_factory_t *factory);
+
+
+/** Register engine */
+MRCP_DECLARE(apt_bool_t) mrcp_engine_factory_engine_register(mrcp_engine_factory_t *factory, mrcp_engine_t *engine, const char *name);
+
+/** Get engine by name */
+MRCP_DECLARE(mrcp_engine_t*) mrcp_engine_factory_engine_get(mrcp_engine_factory_t *factory, const char *name);
+
+/** Find engine by resource identifier */
+MRCP_DECLARE(mrcp_engine_t*) mrcp_engine_factory_engine_find(mrcp_engine_factory_t *factory, mrcp_resource_id resource_id);
+
+
+APT_END_EXTERN_C
+
+#endif /*__MRCP_ENGINE_FACTORY_H__*/
--- /dev/null
+/*
+ * 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_ENGINE_IFACE_H__
+#define __MRCP_ENGINE_IFACE_H__
+
+/**
+ * @file mrcp_engine_iface.h
+ * @brief MRCP Engine User Interface (typically user is an MRCP server)
+ */
+
+#include "mrcp_engine_types.h"
+
+APT_BEGIN_EXTERN_C
+
+/** Destroy engine */
+static APR_INLINE apt_bool_t mrcp_engine_virtual_destroy(mrcp_engine_t *engine)
+{
+ return engine->method_vtable->destroy(engine);
+}
+
+/** Open engine */
+static APR_INLINE apt_bool_t mrcp_engine_virtual_open(mrcp_engine_t *engine)
+{
+ if(engine->is_open == FALSE) {
+ engine->is_open = engine->method_vtable->open(engine);
+ return engine->is_open;
+ }
+ return FALSE;
+}
+
+/** Close engine */
+static APR_INLINE apt_bool_t mrcp_engine_virtual_close(mrcp_engine_t *engine)
+{
+ if(engine->is_open == TRUE) {
+ engine->is_open = FALSE;
+ return engine->method_vtable->close(engine);
+ }
+ return FALSE;
+}
+
+/** Create engine channel */
+mrcp_engine_channel_t* mrcp_engine_channel_virtual_create(mrcp_engine_t *engine, mrcp_version_e mrcp_version, apr_pool_t *pool);
+
+/** Destroy engine channel */
+apt_bool_t mrcp_engine_channel_virtual_destroy(mrcp_engine_channel_t *channel);
+
+/** Open engine channel */
+static APR_INLINE apt_bool_t mrcp_engine_channel_virtual_open(mrcp_engine_channel_t *channel)
+{
+ if(channel->is_open == FALSE) {
+ channel->is_open = channel->method_vtable->open(channel);
+ return channel->is_open;
+ }
+ return FALSE;
+}
+
+/** Close engine channel */
+static APR_INLINE apt_bool_t mrcp_engine_channel_virtual_close(mrcp_engine_channel_t *channel)
+{
+ if(channel->is_open == TRUE) {
+ channel->is_open = FALSE;
+ return channel->method_vtable->close(channel);
+ }
+ return FALSE;
+}
+
+/** 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);
+}
+
+/** Allocate engine config */
+mrcp_engine_config_t* mrcp_engine_config_alloc(apr_pool_t *pool);
+
+
+APT_END_EXTERN_C
+
+#endif /*__MRCP_ENGINE_IFACE_H__*/
--- /dev/null
+/*
+ * 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_ENGINE_IMPL_H__
+#define __MRCP_ENGINE_IMPL_H__
+
+/**
+ * @file mrcp_engine_impl.h
+ * @brief MRCP Engine Realization Interface (typically should be implemented in plugins)
+ */
+
+#include "mrcp_engine_types.h"
+#include "mpf_stream.h"
+
+APT_BEGIN_EXTERN_C
+
+/** Create engine */
+mrcp_engine_t* mrcp_engine_create(
+ mrcp_resource_id resource_id,
+ void *obj,
+ const mrcp_engine_method_vtable_t *vtable,
+ apr_pool_t *pool);
+
+
+/** Get engine config */
+const mrcp_engine_config_t* mrcp_engine_config_get(const mrcp_engine_t *engine);
+
+/** Get engine param by name */
+const char* mrcp_engine_param_get(const mrcp_engine_t *engine, const char *name);
+
+
+/** Create engine channel */
+mrcp_engine_channel_t* mrcp_engine_channel_create(
+ mrcp_engine_t *engine,
+ const mrcp_engine_channel_method_vtable_t *method_vtable,
+ void *method_obj,
+ mpf_termination_t *termination,
+ apr_pool_t *pool);
+
+/** Create audio termination */
+mpf_termination_t* mrcp_engine_audio_termination_create(
+ void *obj,
+ const mpf_audio_stream_vtable_t *stream_vtable,
+ mpf_stream_capabilities_t *capabilities,
+ apr_pool_t *pool);
+
+/** Create engine channel and source media termination
+ * @deprecated @see mrcp_engine_channel_create() and mrcp_engine_audio_termination_create()
+ */
+mrcp_engine_channel_t* mrcp_engine_source_channel_create(
+ mrcp_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
+ * @deprecated @see mrcp_engine_channel_create() and mrcp_engine_audio_termination_create()
+ */
+mrcp_engine_channel_t* mrcp_engine_sink_channel_create(
+ mrcp_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);
+
+/** 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 channel identifier */
+static APR_INLINE const char* mrcp_engine_channel_id_get(mrcp_engine_channel_t *channel)
+{
+ return channel->id.buf;
+}
+
+/** Get MRCP version channel is created in the scope of */
+static APR_INLINE mrcp_version_e mrcp_engine_channel_version_get(mrcp_engine_channel_t *channel)
+{
+ return channel->mrcp_version;
+}
+
+/** Get codec descriptor of the audio source stream */
+const mpf_codec_descriptor_t* mrcp_engine_source_stream_codec_get(mrcp_engine_channel_t *channel);
+
+/** Get codec descriptor of the audio sink stream */
+const mpf_codec_descriptor_t* mrcp_engine_sink_stream_codec_get(mrcp_engine_channel_t *channel);
+
+
+APT_END_EXTERN_C
+
+#endif /*__MRCP_ENGINE_IMPL_H__*/
--- /dev/null
+/*
+ * 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_ENGINE_LOADER_H__
+#define __MRCP_ENGINE_LOADER_H__
+
+/**
+ * @file mrcp_engine_loader.h
+ * @brief Loader of plugins for MRCP engines
+ */
+
+#include "mrcp_engine_iface.h"
+
+APT_BEGIN_EXTERN_C
+
+/** Opaque engine loader declaration */
+typedef struct mrcp_engine_loader_t mrcp_engine_loader_t;
+
+/** Create engine loader */
+MRCP_DECLARE(mrcp_engine_loader_t*) mrcp_engine_loader_create(apr_pool_t *pool);
+
+/** Destroy engine loader */
+MRCP_DECLARE(apt_bool_t) mrcp_engine_loader_destroy(mrcp_engine_loader_t *loader);
+
+/** Unload loaded plugins */
+MRCP_DECLARE(apt_bool_t) mrcp_engine_loader_plugins_unload(mrcp_engine_loader_t *loader);
+
+
+/** Load engine plugin */
+MRCP_DECLARE(mrcp_engine_t*) mrcp_engine_loader_plugin_load(mrcp_engine_loader_t *loader, const char *path, const char *name);
+
+
+APT_END_EXTERN_C
+
+#endif /*__MRCP_ENGINE_LOADER_H__*/
* limitations under the License.
*/
-#ifndef __MRCP_RESOURCE_PLUGIN_H__
-#define __MRCP_RESOURCE_PLUGIN_H__
+#ifndef __MRCP_ENGINE_PLUGIN_H__
+#define __MRCP_ENGINE_PLUGIN_H__
/**
- * @file mrcp_resource_plugin.h
- * @brief MRCP Resource Engine Plugin
+ * @file mrcp_engine_plugin.h
+ * @brief MRCP Engine Plugin
*/
#include "apr_version.h"
#include "apt_log.h"
+#include "mrcp_engine_types.h"
APT_BEGIN_EXTERN_C
#define MRCP_PLUGIN_DECLARE(type) type
#endif
-/** Symbol name of the main entry point in plugin DSO */
+/** [REQUIRED] 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 */
+/** [REQUIRED] Symbol name of the vesrion number entry point in plugin DSO */
+#define MRCP_PLUGIN_VERSION_SYM_NAME "mrcp_plugin_version"
+/** [IMPLIED] 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 engine creator (entry point of plugin DSO) */
+typedef mrcp_engine_t* (*mrcp_plugin_creator_f)(apr_pool_t *pool);
-/** Prototype of resource engine creator (entry point of plugin DSO) */
+/** Prototype of log accessor (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 */
MRCP_PLUGIN_DECLARE(apt_bool_t) mrcp_plugin_logger_set(apt_logger_t *logger) \
{ return apt_log_instance_set(logger); }
+/** Declare this macro in plugins to set plugin version */
+#define MRCP_PLUGIN_VERSION_DECLARE \
+ MRCP_PLUGIN_DECLARE(mrcp_plugin_version_t) mrcp_plugin_version = \
+ {PLUGIN_MAJOR_VERSION, PLUGIN_MINOR_VERSION, PLUGIN_PATCH_VERSION};
+
/** major version
* Major API changes that could cause compatibility problems for older
* Minor API changes that do not cause binary compatibility problems.
* Reset to 0 when upgrading PLUGIN_MAJOR_VERSION
*/
-#define PLUGIN_MINOR_VERSION 4
+#define PLUGIN_MINOR_VERSION 5
/** patch level
* The Patch Level never includes API changes, simply bug fixes.
APT_END_EXTERN_C
-#endif /*__MRCP_RESOURCE_PLUGIN_H__*/
+#endif /*__MRCP_ENGINE_PLUGIN_H__*/
--- /dev/null
+/*
+ * 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_ENGINE_TYPES_H__
+#define __MRCP_ENGINE_TYPES_H__
+
+/**
+ * @file mrcp_engine_types.h
+ * @brief MRCP Engine Types
+ */
+
+#include <apr_tables.h>
+#include "mrcp_state_machine.h"
+#include "mpf_types.h"
+#include "apt_string.h"
+
+APT_BEGIN_EXTERN_C
+
+/** MRCP engine declaration */
+typedef struct mrcp_engine_t mrcp_engine_t;
+/** MRCP engine config declaration */
+typedef struct mrcp_engine_config_t mrcp_engine_config_t;
+/** MRCP 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 engine */
+ mrcp_engine_t *engine;
+ /** Unique identifier to be used in traces */
+ apt_str_t id;
+ /** MRCP version */
+ mrcp_version_e mrcp_version;
+ /** Is channel successfully opened */
+ apt_bool_t is_open;
+ /** 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_engine_t *engine);
+ /** Virtual open */
+ apt_bool_t (*open)(mrcp_engine_t *engine);
+ /** Virtual close */
+ apt_bool_t (*close)(mrcp_engine_t *engine);
+ /** Virtual channel create */
+ mrcp_engine_channel_t* (*create_channel)(mrcp_engine_t *engine, apr_pool_t *pool);
+};
+
+/** MRCP engine */
+struct mrcp_engine_t {
+ /** 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;
+ /** Config of engine */
+ mrcp_engine_config_t *config;
+ /** Number of simultaneous channels currently in use */
+ apr_size_t cur_channel_count;
+ /** Is engine successfully opened */
+ apt_bool_t is_open;
+ /** Pool to allocate memory from */
+ apr_pool_t *pool;
+
+
+ /** Create state machine */
+ mrcp_state_machine_t* (*create_state_machine)(void *obj, mrcp_version_e version, apr_pool_t *pool);
+};
+
+/** MRCP engine config */
+struct mrcp_engine_config_t {
+ /** Name of the engine */
+ const char *name;
+ /** Max number of simultaneous channels */
+ apr_size_t max_channel_count;
+ /** Table of name/value string params */
+ apr_table_t *params;
+};
+
+APT_END_EXTERN_C
+
+#endif /*__MRCP_ENGINE_TYPES_H__*/
--- /dev/null
+/*
+ * 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_ENGINE_H__
+#define __MRCP_RECOG_ENGINE_H__
+
+/**
+ * @file mrcp_recog_engine.h
+ * @brief Recognizer Engine Includes
+ */
+
+#include "mrcp_engine_plugin.h"
+#include "mrcp_engine_impl.h"
+
+#include "mrcp_recog_resource.h"
+#include "mrcp_recog_header.h"
+#include "mrcp_generic_header.h"
+#include "mrcp_message.h"
+
+#endif /*__MRCP_RECOG_ENGINE_H__*/
APT_BEGIN_EXTERN_C
-/** Create MRCP recognizer server state machine */
-mrcp_state_machine_t* mrcp_recog_server_state_machine_create(void *obj, 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_version_e version, apr_pool_t *pool);
+/** Create MRCP recognizer state machine */
+mrcp_state_machine_t* mrcp_recog_state_machine_create(void *obj, mrcp_version_e version, apr_pool_t *pool);
APT_END_EXTERN_C
--- /dev/null
+/*
+ * 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_RECORDER_ENGINE_H__
+#define __MRCP_RECORDER_ENGINE_H__
+
+/**
+ * @file mrcp_recorder_engine.h
+ * @brief Recorder Engine Includes
+ */
+
+#include "mrcp_engine_plugin.h"
+#include "mrcp_engine_impl.h"
+
+#include "mrcp_recorder_resource.h"
+#include "mrcp_recorder_header.h"
+#include "mrcp_generic_header.h"
+#include "mrcp_message.h"
+
+#endif /*__MRCP_RECORDER_ENGINE_H__*/
* limitations under the License.
*/
-#ifndef __MRCP_DEFAULT_FACTORY_H__
-#define __MRCP_DEFAULT_FACTORY_H__
+#ifndef __MRCP_RECORDER_STATE_MACHINE_H__
+#define __MRCP_RECORDER_STATE_MACHINE_H__
/**
- * @file mrcp_default_factory.h
- * @brief Default MRCP Resource Factory
+ * @file mrcp_recorder_state_machine.h
+ * @brief MRCP Recorder State Machine
*/
-#include "mrcp_resource_factory.h"
+#include "mrcp_state_machine.h"
APT_BEGIN_EXTERN_C
-/** Create default MRCP resource factory */
-MRCP_DECLARE(mrcp_resource_factory_t*) mrcp_default_factory_create(apr_pool_t *pool);
-
+/** Create MRCP recorder state machine */
+mrcp_state_machine_t* mrcp_recorder_state_machine_create(void *obj, mrcp_version_e version, apr_pool_t *pool);
APT_END_EXTERN_C
-#endif /*__MRCP_DEFAULT_FACTORY_H__*/
+#endif /*__MRCP_RECORDER_STATE_MACHINE_H__*/
/**
* @file mrcp_resource_engine.h
- * @brief MRCP Resource Engine Interface
+ * @brief Legacy MRCP Resource Engine
+ * @deprecated @see mrcp_engine_plugin.h and mrcp_engine_impl.h
*/
-#include "mrcp_types.h"
-#include "mpf_termination.h"
-#include "mpf_stream.h"
-#include "mrcp_resource_plugin.h"
+#include "mrcp_engine_plugin.h"
+#include "mrcp_engine_impl.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;
+/** Termorary define legacy mrcp_resource_engine_t as mrcp_engine_t */
+typedef mrcp_engine_t mrcp_resource_engine_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;
- /** Unique identifier (useful for traces) */
- apt_str_t id;
- /** 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)
+/**
+ * Create resource engine
+ * @deprecated @see mrcp_engine_create
+ */
+static APR_INLINE mrcp_engine_t* mrcp_resource_engine_create(
+ mrcp_resource_id resource_id,
+ void *obj,
+ const mrcp_engine_method_vtable_t *vtable,
+ apr_pool_t *pool)
{
- return channel->event_vtable->on_message(channel,message);
+ return mrcp_engine_create(resource_id,obj,vtable,pool);
}
-/** 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__*/
--- /dev/null
+/*
+ * 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_ENGINE_H__
+#define __MRCP_SYNTH_ENGINE_H__
+
+/**
+ * @file mrcp_synth_engine.h
+ * @brief Synthesizer Engine Includes
+ */
+
+#include "mrcp_engine_plugin.h"
+#include "mrcp_engine_impl.h"
+
+#include "mrcp_synth_resource.h"
+#include "mrcp_synth_header.h"
+#include "mrcp_generic_header.h"
+#include "mrcp_message.h"
+
+#endif /*__MRCP_SYNTH_ENGINE_H__*/
APT_BEGIN_EXTERN_C
-/** Create MRCP synthesizer server state machine */
-mrcp_state_machine_t* mrcp_synth_server_state_machine_create(void *obj, 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_version_e version, apr_pool_t *pool);
+/** Create MRCP synthesizer state machine */
+mrcp_state_machine_t* mrcp_synth_state_machine_create(void *obj, mrcp_version_e version, apr_pool_t *pool);
APT_END_EXTERN_C
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
+ <File
+ RelativePath=".\include\mrcp_engine_factory.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\mrcp_engine_iface.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\mrcp_engine_impl.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\mrcp_engine_loader.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\mrcp_engine_plugin.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\mrcp_engine_types.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\mrcp_recog_engine.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\mrcp_recog_state_machine.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\mrcp_recorder_engine.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\mrcp_recorder_state_machine.h"
+ >
+ </File>
<File
RelativePath=".\include\mrcp_resource_engine.h"
>
</File>
<File
- RelativePath=".\include\mrcp_resource_plugin.h"
+ RelativePath=".\include\mrcp_state_machine.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\mrcp_synth_engine.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\mrcp_synth_state_machine.h"
>
</File>
</Filter>
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
>
<File
- RelativePath=".\src\mrcp_resource_engine.c"
+ RelativePath=".\src\mrcp_engine_factory.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\mrcp_engine_iface.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\mrcp_engine_impl.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\mrcp_engine_loader.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\mrcp_recog_state_machine.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\mrcp_recorder_state_machine.c"
+ >
+ </File>
+ <File
+ RelativePath=".\src\mrcp_synth_state_machine.c"
>
</File>
</Filter>
--- /dev/null
+/*
+ * 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 <apr_hash.h>
+#include "mrcp_engine_factory.h"
+#include "mrcp_synth_state_machine.h"
+#include "mrcp_recog_state_machine.h"
+#include "mrcp_recorder_state_machine.h"
+#include "apt_log.h"
+
+/** Engine factory declaration */
+struct mrcp_engine_factory_t {
+ apr_hash_t *engines;
+ apr_pool_t *pool;
+};
+
+
+MRCP_DECLARE(mrcp_engine_factory_t*) mrcp_engine_factory_create(apr_pool_t *pool)
+{
+ mrcp_engine_factory_t *factory = apr_palloc(pool,sizeof(mrcp_engine_factory_t));
+ factory->pool = pool;
+ factory->engines = apr_hash_make(pool);
+ return factory;
+}
+
+/** Destroy registered engines and the factory */
+MRCP_DECLARE(apt_bool_t) mrcp_engine_factory_destroy(mrcp_engine_factory_t *factory)
+{
+ mrcp_engine_t *engine;
+ apr_hash_index_t *it;
+ void *val;
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Destroy MRCP Engines");
+ it=apr_hash_first(factory->pool,factory->engines);
+ for(; it; it = apr_hash_next(it)) {
+ apr_hash_this(it,NULL,NULL,&val);
+ engine = val;
+ if(engine) {
+ mrcp_engine_virtual_destroy(engine);
+ }
+ }
+ apr_hash_clear(factory->engines);
+ return TRUE;
+}
+
+/** Open registered engines */
+MRCP_DECLARE(apt_bool_t) mrcp_engine_factory_open(mrcp_engine_factory_t *factory)
+{
+ mrcp_engine_t *engine;
+ apr_hash_index_t *it;
+ void *val;
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Open MRCP Engines");
+ it = apr_hash_first(factory->pool,factory->engines);
+ for(; it; it = apr_hash_next(it)) {
+ apr_hash_this(it,NULL,NULL,&val);
+ engine = val;
+ if(engine) {
+ mrcp_engine_virtual_open(engine);
+ }
+ }
+ return TRUE;
+}
+
+/** Close registered engines */
+MRCP_DECLARE(apt_bool_t) mrcp_engine_factory_close(mrcp_engine_factory_t *factory)
+{
+ mrcp_engine_t *engine;
+ apr_hash_index_t *it;
+ void *val;
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Close MRCP Engines");
+ it=apr_hash_first(factory->pool,factory->engines);
+ for(; it; it = apr_hash_next(it)) {
+ apr_hash_this(it,NULL,NULL,&val);
+ engine = val;
+ if(engine) {
+ mrcp_engine_virtual_close(engine);
+ }
+ }
+ return TRUE;
+}
+
+/** Register new engine */
+MRCP_DECLARE(apt_bool_t) mrcp_engine_factory_engine_register(mrcp_engine_factory_t *factory, mrcp_engine_t *engine, const char *name)
+{
+ if(!engine || !name) {
+ return FALSE;
+ }
+
+ switch(engine->resource_id) {
+ case MRCP_SYNTHESIZER_RESOURCE:
+ engine->create_state_machine = mrcp_synth_state_machine_create;
+ break;
+ case MRCP_RECOGNIZER_RESOURCE:
+ engine->create_state_machine = mrcp_recog_state_machine_create;
+ break;
+ case MRCP_RECORDER_RESOURCE:
+ engine->create_state_machine = mrcp_recorder_state_machine_create;
+ break;
+ default:
+ break;
+ }
+
+ if(!engine->create_state_machine) {
+ return FALSE;
+ }
+
+ apr_hash_set(factory->engines,name,APR_HASH_KEY_STRING,engine);
+ return TRUE;
+}
+
+/** Get engine by name */
+MRCP_DECLARE(mrcp_engine_t*) mrcp_engine_factory_engine_get(mrcp_engine_factory_t *factory, const char *name)
+{
+ if(!name) {
+ return NULL;
+ }
+ return apr_hash_get(factory->engines,name,APR_HASH_KEY_STRING);
+}
+
+/** Find engine by resource identifier */
+MRCP_DECLARE(mrcp_engine_t*) mrcp_engine_factory_engine_find(mrcp_engine_factory_t *factory, mrcp_resource_id resource_id)
+{
+ mrcp_engine_t *engine;
+ void *val;
+ apr_hash_index_t *it = apr_hash_first(factory->pool,factory->engines);
+ /* walk through the engines */
+ for(; it; it = apr_hash_next(it)) {
+ apr_hash_this(it,NULL,NULL,&val);
+ engine = val;
+ if(engine && engine->resource_id == resource_id) {
+ return engine;
+ }
+ }
+ return NULL;
+}
--- /dev/null
+/*
+ * 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_engine_iface.h"
+
+/** Create engine channel */
+mrcp_engine_channel_t* mrcp_engine_channel_virtual_create(mrcp_engine_t *engine, mrcp_version_e mrcp_version, apr_pool_t *pool)
+{
+ mrcp_engine_channel_t *channel;
+ if(engine->is_open != TRUE) {
+ return NULL;
+ }
+ if(engine->config->max_channel_count && engine->cur_channel_count >= engine->config->max_channel_count) {
+ return NULL;
+ }
+ channel = engine->method_vtable->create_channel(engine,pool);
+ if(channel) {
+ channel->mrcp_version = mrcp_version;
+ engine->cur_channel_count++;
+ }
+ return channel;
+}
+
+/** Destroy engine channel */
+apt_bool_t mrcp_engine_channel_virtual_destroy(mrcp_engine_channel_t *channel)
+{
+ mrcp_engine_t *engine = channel->engine;
+ if(engine->cur_channel_count) {
+ engine->cur_channel_count--;
+ }
+ return channel->method_vtable->destroy(channel);
+}
+
+/** Allocate engine config */
+mrcp_engine_config_t* mrcp_engine_config_alloc(apr_pool_t *pool)
+{
+ mrcp_engine_config_t *config = apr_palloc(pool,sizeof(mrcp_engine_config_t));
+ config->name = NULL;
+ config->max_channel_count = 0;
+ config->params = NULL;
+ return config;
+}
--- /dev/null
+/*
+ * 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_engine_impl.h"
+#include "mpf_termination_factory.h"
+
+/** Create engine */
+mrcp_engine_t* mrcp_engine_create(
+ mrcp_resource_id resource_id,
+ void *obj,
+ const mrcp_engine_method_vtable_t *vtable,
+ apr_pool_t *pool)
+{
+ mrcp_engine_t *engine = apr_palloc(pool,sizeof(mrcp_engine_t));
+ engine->resource_id = resource_id;
+ engine->obj = obj;
+ engine->method_vtable =vtable;
+ engine->config = NULL;
+ engine->codec_manager = NULL;
+ engine->dir_layout = NULL;
+ engine->cur_channel_count = 0;
+ engine->is_open = FALSE;
+ engine->pool = pool;
+ engine->create_state_machine = NULL;
+ return engine;
+}
+
+/** Get engine config */
+const mrcp_engine_config_t* mrcp_engine_config_get(const mrcp_engine_t *engine)
+{
+ return engine->config;
+}
+
+/** Get engine param by name */
+const char* mrcp_engine_param_get(const mrcp_engine_t *engine, const char *name)
+{
+ if(!engine->config || !engine->config->params) {
+ return NULL;
+ }
+ return apr_table_get(engine->config->params,name);
+}
+
+/** Create engine channel */
+mrcp_engine_channel_t* mrcp_engine_channel_create(
+ mrcp_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->is_open = FALSE;
+ channel->pool = pool;
+ apt_string_reset(&channel->id);
+ return channel;
+}
+
+/** Create audio termination */
+mpf_termination_t* mrcp_engine_audio_termination_create(
+ void *obj,
+ const mpf_audio_stream_vtable_t *stream_vtable,
+ mpf_stream_capabilities_t *capabilities,
+ apr_pool_t *pool)
+{
+ mpf_audio_stream_t *audio_stream;
+ if(!capabilities) {
+ return NULL;
+ }
+
+ if(mpf_codec_capabilities_validate(&capabilities->codecs) == FALSE) {
+ return NULL;
+ }
+
+ /* create audio stream */
+ audio_stream = mpf_audio_stream_create(
+ obj, /* object to associate */
+ stream_vtable, /* virtual methods table of audio stream */
+ capabilities, /* stream capabilities */
+ pool); /* pool to allocate memory from */
+
+ if(!audio_stream) {
+ return NULL;
+ }
+
+ /* create media termination */
+ return 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 and source media termination */
+mrcp_engine_channel_t* mrcp_engine_source_channel_create(
+ mrcp_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_stream_capabilities_t *capabilities;
+ mpf_audio_stream_t *audio_stream;
+ mpf_termination_t *termination;
+
+ capabilities = mpf_source_stream_capabilities_create(pool);
+ if(codec_descriptor) {
+ mpf_codec_capabilities_add(
+ &capabilities->codecs,
+ mpf_sample_rate_mask_get(codec_descriptor->sampling_rate),
+ codec_descriptor->name.buf);
+ }
+ else {
+ mpf_codec_default_capabilities_add(&capabilities->codecs);
+ }
+
+ /* create audio stream */
+ audio_stream = mpf_audio_stream_create(
+ method_obj, /* object to associate */
+ stream_vtable, /* virtual methods table of audio stream */
+ capabilities, /* stream capabilities */
+ pool); /* pool to allocate memory from */
+
+ if(!audio_stream) {
+ return NULL;
+ }
+
+ audio_stream->rx_descriptor = codec_descriptor;
+
+ /* 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, /* 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_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_stream_capabilities_t *capabilities;
+ mpf_audio_stream_t *audio_stream;
+ mpf_termination_t *termination;
+
+ capabilities = mpf_sink_stream_capabilities_create(pool);
+ if(codec_descriptor) {
+ mpf_codec_capabilities_add(
+ &capabilities->codecs,
+ mpf_sample_rate_mask_get(codec_descriptor->sampling_rate),
+ codec_descriptor->name.buf);
+ }
+ else {
+ mpf_codec_default_capabilities_add(&capabilities->codecs);
+ }
+
+ /* create audio stream */
+ audio_stream = mpf_audio_stream_create(
+ method_obj, /* object to associate */
+ stream_vtable, /* virtual methods table of audio stream */
+ capabilities, /* stream capabilities */
+ pool); /* pool to allocate memory from */
+
+ if(!audio_stream) {
+ return NULL;
+ }
+
+ audio_stream->tx_descriptor = codec_descriptor;
+
+ /* 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, /* 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 descriptor of the audio source stream */
+const mpf_codec_descriptor_t* mrcp_engine_source_stream_codec_get(mrcp_engine_channel_t *channel)
+{
+ if(channel && channel->termination) {
+ mpf_audio_stream_t *audio_stream = mpf_termination_audio_stream_get(channel->termination);
+ if(audio_stream) {
+ return audio_stream->rx_descriptor;
+ }
+ }
+ return NULL;
+}
+
+/** Get codec descriptor of the audio sink stream */
+const mpf_codec_descriptor_t* mrcp_engine_sink_stream_codec_get(mrcp_engine_channel_t *channel)
+{
+ if(channel && channel->termination) {
+ mpf_audio_stream_t *audio_stream = mpf_termination_audio_stream_get(channel->termination);
+ if(audio_stream) {
+ return audio_stream->tx_descriptor;
+ }
+ }
+ return NULL;
+}
--- /dev/null
+/*
+ * 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 <apr_dso.h>
+#include <apr_hash.h>
+#include "mrcp_engine_loader.h"
+#include "mrcp_engine_plugin.h"
+#include "apt_log.h"
+
+/** Engine loader declaration */
+struct mrcp_engine_loader_t {
+ /** Table of plugins (apr_dso_handle_t*) */
+ apr_hash_t *plugins;
+ apr_pool_t *pool;
+};
+
+
+/** Create engine loader */
+MRCP_DECLARE(mrcp_engine_loader_t*) mrcp_engine_loader_create(apr_pool_t *pool)
+{
+ mrcp_engine_loader_t *loader = apr_palloc(pool,sizeof(mrcp_engine_loader_t));
+ loader->pool = pool;
+ loader->plugins = apr_hash_make(pool);
+ return loader;
+}
+
+/** Destroy engine loader */
+MRCP_DECLARE(apt_bool_t) mrcp_engine_loader_destroy(mrcp_engine_loader_t *loader)
+{
+ return mrcp_engine_loader_plugins_unload(loader);
+}
+
+/** Unload loaded plugins */
+MRCP_DECLARE(apt_bool_t) mrcp_engine_loader_plugins_unload(mrcp_engine_loader_t *loader)
+{
+ apr_hash_index_t *it;
+ void *val;
+ apr_dso_handle_t *plugin;
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Unload Plugins");
+ it=apr_hash_first(loader->pool,loader->plugins);
+ for(; it; it = apr_hash_next(it)) {
+ apr_hash_this(it,NULL,NULL,&val);
+ plugin = val;
+ if(plugin) {
+ apr_dso_unload(plugin);
+ }
+ }
+ apr_hash_clear(loader->plugins);
+ return TRUE;
+}
+
+static apt_bool_t plugin_version_load(apr_dso_handle_t *plugin)
+{
+ apr_dso_handle_sym_t version_handle = NULL;
+ if(apr_dso_sym(&version_handle,plugin,MRCP_PLUGIN_VERSION_SYM_NAME) != APR_SUCCESS) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No Version Info Found: %s", MRCP_PLUGIN_VERSION_SYM_NAME);
+ return FALSE;
+ }
+
+ if(version_handle) {
+ mrcp_plugin_version_t *version = (mrcp_plugin_version_t*)version_handle;
+ if(mrcp_plugin_version_check(version)) {
+ return TRUE;
+ }
+ else {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Incompatible Plugin Version Found [%d.%d.%d] < ["PLUGIN_VERSION_STRING"]",
+ version->major,
+ version->minor,
+ version->patch);
+ }
+ }
+ return FALSE;
+}
+
+static mrcp_plugin_creator_f plugin_creator_load(apr_dso_handle_t *plugin)
+{
+ apr_dso_handle_sym_t func_handle = NULL;
+ mrcp_plugin_creator_f plugin_creator = NULL;
+
+ 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);
+ return NULL;
+ }
+
+ return plugin_creator;
+}
+
+static apt_bool_t plugin_logger_load(apr_dso_handle_t *plugin)
+{
+ apr_dso_handle_sym_t func_handle = NULL;
+ if(apr_dso_sym(&func_handle,plugin,MRCP_PLUGIN_LOGGER_SYM_NAME) != APR_SUCCESS) {
+ return FALSE;
+ }
+
+ 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);
+ }
+ return TRUE;
+}
+
+
+/** Load engine plugin */
+MRCP_DECLARE(mrcp_engine_t*) mrcp_engine_loader_plugin_load(mrcp_engine_loader_t *loader, const char *path, const char *name)
+{
+ apr_dso_handle_t *plugin = NULL;
+ mrcp_plugin_creator_f plugin_creator = NULL;
+ mrcp_engine_t *engine = NULL;
+ if(!path || !name) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Load Plugin: invalid params");
+ return NULL;
+ }
+
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Load Plugin [%s] [%s]",path,name);
+ if(apr_dso_load(&plugin,path,loader->pool) != APR_SUCCESS) {
+ 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 NULL;
+ }
+
+ if(plugin_version_load(plugin) != TRUE) {
+ apr_dso_unload(plugin);
+ return NULL;
+ }
+
+ plugin_creator = plugin_creator_load(plugin);
+ if(!plugin_creator) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No Entry Point Found for Plugin");
+ apr_dso_unload(plugin);
+ return NULL;
+ }
+
+ plugin_logger_load(plugin);
+
+ apr_hash_set(loader->plugins,name,APR_HASH_KEY_STRING,plugin);
+
+ engine = plugin_creator(loader->pool);
+ if(!engine) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create MRCP Engine");
+ }
+
+ return engine;
+}
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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process SET-PARAMS Request [%"MRCP_REQUEST_ID_FMT"]",
+ 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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process SET-PARAMS Response [%"MRCP_REQUEST_ID_FMT"]",
+ 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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process GET-PARAMS Request [%"MRCP_REQUEST_ID_FMT"]",
+ 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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process GET-PARAMS Response [%"MRCP_REQUEST_ID_FMT"]",
+ 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);
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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process DEFINE-GRAMMAR Request [%"MRCP_REQUEST_ID_FMT"]",
+ 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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process DEFINE-GRAMMAR Response [%"MRCP_REQUEST_ID_FMT"]",
+ 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_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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Queue Up RECOGNIZE Request [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
message->start_line.request_state = MRCP_REQUEST_STATE_PENDING;
apt_list_push_back(state_machine->queue,message,message->pool);
return recog_response_dispatch(state_machine,response);
}
- apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process RECOGNIZE Request [%d]",message->start_line.request_id);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process RECOGNIZE Request [%"MRCP_REQUEST_ID_FMT"]",
+ 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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process RECOGNIZE Response [%"MRCP_REQUEST_ID_FMT"]",
+ 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);
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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process GET-RESULT Request [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
return recog_request_dispatch(state_machine,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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process GET-RESULT Response [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
return recog_response_dispatch(state_machine,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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process START-INPUT-TIMERS Request [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
return recog_request_dispatch(state_machine,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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process START-INPUT-TIMERS Response [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
return recog_response_dispatch(state_machine,message);
}
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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process STOP Request [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
return recog_request_dispatch(state_machine,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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process STOP Response [%"MRCP_REQUEST_ID_FMT"]",
+ 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);
return FALSE;
}
- apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process START-OF-INPUT Event [%d]",message->start_line.request_id);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process START-OF-INPUT Event [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
message->start_line.request_state = MRCP_REQUEST_STATE_INPROGRESS;
return recog_event_dispatch(state_machine,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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Unexpected RECOGNITION-COMPLETE Event [%"MRCP_REQUEST_ID_FMT"]",
+ 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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Unexpected RECOGNITION-COMPLETE Event [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
return FALSE;
}
return FALSE;
}
- apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process RECOGNITION-COMPLETE Event [%d]",message->start_line.request_id);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process RECOGNITION-COMPLETE Event [%"MRCP_REQUEST_ID_FMT"]",
+ 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;
/* create internal STOP request */
message = mrcp_request_create(
- source->channel_id.resource_id,
+ source->resource,
+ source->start_line.version,
RECOGNIZER_STOP,
source->pool);
message->channel_id = source->channel_id;
message->start_line.request_id = source->start_line.request_id + 1;
apt_string_set(&message->start_line.method_name,"DEACTIVATE"); /* informative only */
message->header = source->header;
- apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Create and Process STOP Request [%d]",message->start_line.request_id);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Create and Process STOP Request [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
return recog_request_dispatch(state_machine,message);
}
-/** Create MRCP recognizer server state machine */
-mrcp_state_machine_t* mrcp_recog_server_state_machine_create(void *obj, mrcp_version_e version, apr_pool_t *pool)
+/** Create MRCP recognizer state machine */
+mrcp_state_machine_t* mrcp_recog_state_machine_create(void *obj, 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));
--- /dev/null
+/*
+ * 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_recorder_state_machine.h"
+#include "mrcp_generic_header.h"
+#include "mrcp_recorder_header.h"
+#include "mrcp_recorder_resource.h"
+#include "mrcp_message.h"
+
+/** MRCP recorder states */
+typedef enum {
+ RECORDER_STATE_IDLE,
+ RECORDER_STATE_RECORDING,
+
+ RECORDER_STATE_COUNT
+} mrcp_recorder_state_e;
+
+static const char * state_names[RECORDER_STATE_COUNT] = {
+ "IDLE",
+ "RECORDING",
+};
+
+typedef struct mrcp_recorder_state_machine_t mrcp_recorder_state_machine_t;
+
+struct mrcp_recorder_state_machine_t {
+ /** state machine base */
+ mrcp_state_machine_t base;
+ /** recorder state */
+ mrcp_recorder_state_e state;
+ /** request sent to recorder engine and waiting for the response to be received */
+ mrcp_message_t *active_request;
+ /** in-progress record request */
+ mrcp_message_t *record;
+ /** properties used in set/get params */
+ mrcp_message_header_t properties;
+};
+
+typedef apt_bool_t (*recorder_method_f)(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message);
+
+static APR_INLINE apt_bool_t recorder_request_dispatch(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
+{
+ state_machine->active_request = message;
+ return state_machine->base.on_dispatch(&state_machine->base,message);
+}
+
+static APR_INLINE apt_bool_t recorder_response_dispatch(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
+{
+ state_machine->active_request = NULL;
+ if(state_machine->base.active == FALSE) {
+ /* this is the response to deactivation (STOP) request */
+ return state_machine->base.on_deactivate(&state_machine->base);
+ }
+ return state_machine->base.on_dispatch(&state_machine->base,message);
+}
+
+static APR_INLINE apt_bool_t recorder_event_dispatch(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
+{
+ if(state_machine->base.active == FALSE) {
+ /* do nothing, state machine has already been deactivated */
+ return FALSE;
+ }
+ return state_machine->base.on_dispatch(&state_machine->base,message);
+}
+
+static APR_INLINE void recorder_state_change(mrcp_recorder_state_machine_t *state_machine, mrcp_recorder_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 == RECORDER_STATE_IDLE) {
+ state_machine->record = NULL;
+ }
+}
+
+
+static apt_bool_t recorder_request_set_params(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
+{
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process SET-PARAMS Request [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
+ mrcp_message_header_set(&state_machine->properties,&message->header,message->pool);
+ return recorder_request_dispatch(state_machine,message);
+}
+
+static apt_bool_t recorder_response_set_params(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
+{
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process SET-PARAMS Response [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
+ return recorder_response_dispatch(state_machine,message);
+}
+
+static apt_bool_t recorder_request_get_params(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
+{
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process GET-PARAMS Request [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
+ return recorder_request_dispatch(state_machine,message);
+}
+
+static apt_bool_t recorder_response_get_params(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
+{
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process GET-PARAMS Response [%"MRCP_REQUEST_ID_FMT"]",
+ 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 recorder_response_dispatch(state_machine,message);
+}
+
+static apt_bool_t recorder_request_record(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
+{
+ mrcp_message_header_inherit(&message->header,&state_machine->properties,message->pool);
+ if(state_machine->state == RECORDER_STATE_RECORDING) {
+ mrcp_message_t *response;
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Reject RECORD Request [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
+
+ /* there is in-progress request, reject this one */
+ response = mrcp_response_create(message,message->pool);
+ response->start_line.status_code = MRCP_STATUS_CODE_METHOD_NOT_VALID;
+ return recorder_response_dispatch(state_machine,response);
+ }
+
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process RECORD Request [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
+ return recorder_request_dispatch(state_machine,message);
+}
+
+static apt_bool_t recorder_response_record(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
+{
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process RECORD Response [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
+ if(message->start_line.request_state == MRCP_REQUEST_STATE_INPROGRESS) {
+ state_machine->record = state_machine->active_request;
+ recorder_state_change(state_machine,RECORDER_STATE_RECORDING);
+ }
+ return recorder_response_dispatch(state_machine,message);
+}
+
+static apt_bool_t recorder_request_stop(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
+{
+ mrcp_message_t *response;
+ if(state_machine->state == RECORDER_STATE_RECORDING) {
+ /* found in-progress RECORDER request, stop it */
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process STOP Request [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
+ return recorder_request_dispatch(state_machine,message);
+ }
+
+ /* found no in-progress RECORDER request, sending immediate response */
+ response = mrcp_response_create(message,message->pool);
+ return recorder_response_dispatch(state_machine,response);
+}
+
+static apt_bool_t recorder_response_stop(mrcp_recorder_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 STOP Response [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
+ /* append active id list */
+ active_request_id_list_append(generic_header,state_machine->record->start_line.request_id);
+ mrcp_generic_header_property_add(message,GENERIC_HEADER_ACTIVE_REQUEST_ID_LIST);
+ recorder_state_change(state_machine,RECORDER_STATE_IDLE);
+ return recorder_response_dispatch(state_machine,message);
+}
+
+static apt_bool_t recorder_request_start_timers(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
+{
+ mrcp_message_t *response;
+ if(state_machine->state == RECORDER_STATE_RECORDING) {
+ /* found in-progress request */
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process START-INPUT-TIMERS Request [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
+ return recorder_request_dispatch(state_machine,message);
+ }
+
+ /* found no in-progress request */
+ response = mrcp_response_create(message,message->pool);
+ response->start_line.status_code = MRCP_STATUS_CODE_METHOD_NOT_VALID;
+ return recorder_response_dispatch(state_machine,response);
+}
+
+static apt_bool_t recorder_response_start_timers(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
+{
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process START-INPUT-TIMERS Response [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
+ return recorder_response_dispatch(state_machine,message);
+}
+
+static apt_bool_t recorder_event_start_of_input(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
+{
+ if(!state_machine->record) {
+ /* unexpected event, no in-progress record request */
+ return FALSE;
+ }
+
+ if(state_machine->record->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 [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
+ message->start_line.request_state = MRCP_REQUEST_STATE_INPROGRESS;
+ return recorder_event_dispatch(state_machine,message);
+}
+
+static apt_bool_t recorder_event_record_complete(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
+{
+ if(!state_machine->record) {
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Unexpected RECORD-COMPLETE Event [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
+ return FALSE;
+ }
+
+ if(state_machine->record->start_line.request_id != message->start_line.request_id) {
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Unexpected RECORD-COMPLETE Event [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
+ return FALSE;
+ }
+
+ if(state_machine->active_request && state_machine->active_request->start_line.method_id == RECORDER_STOP) {
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Ignore RECORD-COMPLETE Event [%d]: waiting for STOP response",message->start_line.request_id);
+ return FALSE;
+ }
+
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process RECORD-COMPLETE Event [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
+ if(mrcp_resource_header_property_check(message,RECORDER_HEADER_COMPLETION_CAUSE) != TRUE) {
+ mrcp_recorder_header_t *recorder_header = mrcp_resource_header_prepare(message);
+ recorder_header->completion_cause = RECORDER_COMPLETION_CAUSE_SUCCESS_SILENCE;
+ mrcp_resource_header_property_add(message,RECORDER_HEADER_COMPLETION_CAUSE);
+ }
+ recorder_state_change(state_machine,RECORDER_STATE_IDLE);
+ return recorder_event_dispatch(state_machine,message);
+}
+
+static recorder_method_f recorder_request_method_array[RECORDER_METHOD_COUNT] = {
+ recorder_request_set_params,
+ recorder_request_get_params,
+ recorder_request_record,
+ recorder_request_stop,
+ recorder_request_start_timers
+};
+
+static recorder_method_f recorder_response_method_array[RECORDER_METHOD_COUNT] = {
+ recorder_response_set_params,
+ recorder_response_get_params,
+ recorder_response_record,
+ recorder_response_stop,
+ recorder_response_start_timers
+};
+
+static recorder_method_f recorder_event_method_array[RECORDER_EVENT_COUNT] = {
+ recorder_event_start_of_input,
+ recorder_event_record_complete
+};
+
+/** Update state according to received incoming request from MRCP client */
+static apt_bool_t recorder_request_state_update(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
+{
+ recorder_method_f method;
+ if(message->start_line.method_id >= RECORDER_METHOD_COUNT) {
+ return FALSE;
+ }
+
+ method = recorder_request_method_array[message->start_line.method_id];
+ if(method) {
+ return method(state_machine,message);
+ }
+ return recorder_request_dispatch(state_machine,message);
+}
+
+/** Update state according to received outgoing response from recorder engine */
+static apt_bool_t recorder_response_state_update(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
+{
+ recorder_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 >= RECORDER_METHOD_COUNT) {
+ return FALSE;
+ }
+
+ method = recorder_response_method_array[message->start_line.method_id];
+ if(method) {
+ return method(state_machine,message);
+ }
+ return recorder_response_dispatch(state_machine,message);
+}
+
+/** Update state according to received outgoing event from recorder engine */
+static apt_bool_t recorder_event_state_update(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
+{
+ recorder_method_f method;
+ if(message->start_line.method_id >= RECORDER_EVENT_COUNT) {
+ return FALSE;
+ }
+
+ method = recorder_event_method_array[message->start_line.method_id];
+ if(method) {
+ return method(state_machine,message);
+ }
+ return recorder_event_dispatch(state_machine,message);
+}
+
+/** Update state according to request received from MRCP client or response/event received from recorder engine */
+static apt_bool_t recorder_state_update(mrcp_state_machine_t *base, mrcp_message_t *message)
+{
+ mrcp_recorder_state_machine_t *state_machine = (mrcp_recorder_state_machine_t*)base;
+ apt_bool_t status = TRUE;
+ switch(message->start_line.message_type) {
+ case MRCP_MESSAGE_TYPE_REQUEST:
+ status = recorder_request_state_update(state_machine,message);
+ break;
+ case MRCP_MESSAGE_TYPE_RESPONSE:
+ status = recorder_response_state_update(state_machine,message);
+ break;
+ case MRCP_MESSAGE_TYPE_EVENT:
+ status = recorder_event_state_update(state_machine,message);
+ break;
+ default:
+ status = FALSE;
+ break;
+ }
+ return status;
+}
+
+/** Deactivate state machine */
+static apt_bool_t recorder_state_deactivate(mrcp_state_machine_t *base)
+{
+ mrcp_recorder_state_machine_t *state_machine = (mrcp_recorder_state_machine_t*)base;
+ mrcp_message_t *message;
+ mrcp_message_t *source;
+ if(state_machine->state != RECORDER_STATE_RECORDING) {
+ /* no in-progress RECORD request to deactivate */
+ return FALSE;
+ }
+ source = state_machine->record;
+ if(!source) {
+ return FALSE;
+ }
+
+ /* create internal STOP request */
+ message = mrcp_request_create(
+ source->resource,
+ source->start_line.version,
+ RECORDER_STOP,
+ source->pool);
+ message->channel_id = source->channel_id;
+ message->start_line.request_id = source->start_line.request_id + 1;
+ apt_string_set(&message->start_line.method_name,"DEACTIVATE"); /* informative only */
+ message->header = source->header;
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Create and Process STOP Request [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
+ return recorder_request_dispatch(state_machine,message);
+}
+
+/** Create MRCP recorder state machine */
+mrcp_state_machine_t* mrcp_recorder_state_machine_create(void *obj, mrcp_version_e version, apr_pool_t *pool)
+{
+ mrcp_message_header_t *properties;
+ mrcp_recorder_state_machine_t *state_machine = apr_palloc(pool,sizeof(mrcp_recorder_state_machine_t));
+ mrcp_state_machine_init(&state_machine->base,obj);
+ state_machine->base.update = recorder_state_update;
+ state_machine->base.deactivate = recorder_state_deactivate;
+ state_machine->state = RECORDER_STATE_IDLE;
+ state_machine->active_request = NULL;
+ state_machine->record = NULL;
+ 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_recorder_header_vtable_get(version);
+ return &state_machine->base;
+}
+++ /dev/null
-/*
- * 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;
- apt_string_reset(&channel->id);
- 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;
-}
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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process SET-PARAMS Request [%"MRCP_REQUEST_ID_FMT"]",
+ 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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process SET-PARAMS Response [%"MRCP_REQUEST_ID_FMT"]",
+ 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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process GET-PARAMS Request [%"MRCP_REQUEST_ID_FMT"]",
+ 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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process GET-PARAMS Response [%"MRCP_REQUEST_ID_FMT"]",
+ 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);
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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Queue Up SPEAK Request [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
message->start_line.request_state = MRCP_REQUEST_STATE_PENDING;
apt_list_push_back(state_machine->queue,message,message->pool);
return synth_response_dispatch(state_machine,response);
}
- apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process SPEAK Request [%d]",message->start_line.request_id);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process SPEAK Request [%"MRCP_REQUEST_ID_FMT"]",
+ 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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process SPEAK Response [%"MRCP_REQUEST_ID_FMT"]",
+ 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(!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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process STOP Request [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
return synth_request_dispatch(state_machine,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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process STOP Response [%"MRCP_REQUEST_ID_FMT"]",
+ 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);
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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process PAUSE Response [%"MRCP_REQUEST_ID_FMT"]",
+ 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 */
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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process RESUME Response [%"MRCP_REQUEST_ID_FMT"]",
+ 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 */
}
if(kill_on_barge_in == TRUE) {
- apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process BARGE-IN Request [%d]",message->start_line.request_id);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process BARGE-IN Request [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
return synth_request_dispatch(state_machine,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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process BARGE-IN Response [%"MRCP_REQUEST_ID_FMT"]",
+ 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);
{
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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process CONTROL Request [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
return synth_request_dispatch(state_machine,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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process CONTROL Response [%"MRCP_REQUEST_ID_FMT"]",
+ 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);
{
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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process DEFINE-LEXICON Request [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
return synth_request_dispatch(state_machine,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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process DEFINE-LEXICON Response [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
return synth_response_dispatch(state_machine,message);
}
return FALSE;
}
- apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process SPEECH-MARKER Event [%d]",message->start_line.request_id);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process SPEECH-MARKER Event [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
message->start_line.request_state = MRCP_REQUEST_STATE_INPROGRESS;
return synth_event_dispatch(state_machine,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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Unexpected SPEAK-COMPLETE Event [%"MRCP_REQUEST_ID_FMT"]",
+ 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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Unexpected SPEAK-COMPLETE Event [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
return FALSE;
}
return FALSE;
}
- apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process SPEAK-COMPLETE Event [%d]",message->start_line.request_id);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process SPEAK-COMPLETE Event [%"MRCP_REQUEST_ID_FMT"]",
+ 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;
/* create internal STOP request */
message = mrcp_request_create(
- source->channel_id.resource_id,
+ source->resource,
+ source->start_line.version,
SYNTHESIZER_STOP,
source->pool);
message->channel_id = source->channel_id;
message->start_line.request_id = source->start_line.request_id + 1;
apt_string_set(&message->start_line.method_name,"DEACTIVATE"); /* informative only */
message->header = source->header;
- apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Create and Process STOP Request [%d]",message->start_line.request_id);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Create and Process STOP Request [%"MRCP_REQUEST_ID_FMT"]",
+ message->start_line.request_id);
return synth_request_dispatch(state_machine,message);
}
-/** Create MRCP synthesizer server state machine */
-mrcp_state_machine_t* mrcp_synth_server_state_machine_create(void *obj, mrcp_version_e version, apr_pool_t *pool)
+/** Create MRCP synthesizer state machine */
+mrcp_state_machine_t* mrcp_synth_state_machine_create(void *obj, 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));
*/
#include "mrcp_server_types.h"
-#include "mrcp_resource_engine.h"
+#include "mrcp_engine_iface.h"
#include "apt_task.h"
APT_BEGIN_EXTERN_C
MRCP_DECLARE(apt_bool_t) mrcp_server_resource_factory_register(mrcp_server_t *server, mrcp_resource_factory_t *resource_factory);
/**
- * Register MRCP resource engine.
+ * Register MRCP 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
+ * @param engine the engine to set
+ * @param config the config of the engine
*/
-MRCP_DECLARE(apt_bool_t) mrcp_server_resource_engine_register(mrcp_server_t *server, mrcp_resource_engine_t *engine, const char *name);
+MRCP_DECLARE(apt_bool_t) mrcp_server_engine_register(mrcp_server_t *server, mrcp_engine_t *engine, mrcp_engine_config_t *config);
/**
* Register codec manager.
* 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 plugin_map the map of engines (plugins)
* @param name the name of the profile
*/
MRCP_DECLARE(apt_bool_t) mrcp_server_profile_register(
const char *name);
/**
- * Register resource engine plugin.
+ * Register MRCP engine plugin.
* @param server the MRCP server to set engine for
* @param path the path to plugin
- * @param name the name of the plugin
+ * @param config the config of the plugin
*/
-MRCP_DECLARE(apt_bool_t) mrcp_server_plugin_register(mrcp_server_t *server, const char *path, const char *name);
+MRCP_DECLARE(apt_bool_t) mrcp_server_plugin_register(mrcp_server_t *server, const char *path, mrcp_engine_config_t *config);
/**
* Get memory pool.
*/
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
#include <apr_hash.h>
#include "mrcp_session.h"
-#include "mpf_message.h"
+#include "mpf_engine.h"
#include "apt_task.h"
#include "apt_obj_list.h"
mrcp_message_t *message;
};
+/** Server session states */
+typedef enum {
+ SESSION_STATE_NONE, /**< initial state */
+ SESSION_STATE_GENERATING_ANSWER, /**< received offer, generating answer now */
+ SESSION_STATE_INITIALIZING, /**< answer is ready, finally initializing channels now */
+ SESSION_STATE_DEACTIVATING, /**< received session termination request, deinitializing channels now */
+ SESSION_STATE_TERMINATING /**< finally terminating session */
+} mrcp_server_session_state_e;
+
/** MRCP server session */
struct mrcp_server_session_t {
/** Session base */
- mrcp_session_t base;
+ mrcp_session_t base;
/** MRCP server */
- mrcp_server_t *server;
+ mrcp_server_t *server;
/** MRCP profile */
- mrcp_profile_t *profile;
+ mrcp_profile_t *profile;
/** Media context */
- mpf_context_t *context;
+ mpf_context_t *context;
/** Media termination array */
- apr_array_header_t *terminations;
+ apr_array_header_t *terminations;
/** MRCP control channel array */
- apr_array_header_t *channels;
+ apr_array_header_t *channels;
/** In-progress signaling request */
- mrcp_signaling_message_t *active_request;
+ mrcp_signaling_message_t *active_request;
/** Signaling request queue */
- apt_obj_list_t *request_queue;
+ apt_obj_list_t *request_queue;
/** In-progress offer */
- mrcp_session_descriptor_t *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;
- /** Number of in-progress deactivare requests (flags) */
- apr_size_t deactivate_flag_count;
+ mrcp_session_descriptor_t *answer;
+
+ /** MPF task message, which construction is in progress */
+ mpf_task_msg_t *mpf_task_msg;
+
+ /** Session state */
+ mrcp_server_session_state_e state;
+ /** Number of in-progress sub requests */
+ apr_size_t subrequest_count;
};
/** MRCP profile */
struct mrcp_profile_t {
- /** Table of resource engines (mrcp_resource_engine_t*) */
+ /** Table of engines (mrcp_engine_t*) */
apr_hash_t *engine_table;
/** MRCP resource factory */
mrcp_resource_factory_t *resource_factory;
};
/** Create server session */
-mrcp_server_session_t* mrcp_server_session_create();
+mrcp_server_session_t* mrcp_server_session_create(void);
/** 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);
+apt_bool_t mrcp_server_mpf_message_process(mpf_message_container_t *mpf_message_container);
/** 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);
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 connection disconnect event */
+apt_bool_t mrcp_server_on_disconnect(mrcp_channel_t *channel);
/** Process channel open event */
apt_bool_t mrcp_server_on_engine_channel_open(mrcp_channel_t *channel, apt_bool_t status);
* limitations under the License.
*/
-#include <apr_dso.h>
#include "mrcp_server.h"
#include "mrcp_server_session.h"
#include "mrcp_message.h"
#include "mrcp_resource_factory.h"
+#include "mrcp_resource.h"
+#include "mrcp_engine_factory.h"
+#include "mrcp_engine_loader.h"
#include "mrcp_sig_agent.h"
#include "mrcp_server_connection.h"
#include "mpf_engine.h"
/** MRCP resource factory */
mrcp_resource_factory_t *resource_factory;
+ /** MRCP engine factory */
+ mrcp_engine_factory_t *engine_factory;
+ /** Loader of plugins for MRCP engines */
+ mrcp_engine_loader_t *engine_loader;
+
/** 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 *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;
+ /** Engine task message pool */
+ apt_task_msg_pool_t *engine_msg_pool;
/** Dir layout structure */
apt_dir_layout_t *dir_layout;
typedef enum {
MRCP_SERVER_SIGNALING_TASK_MSG = TASK_MSG_USER,
MRCP_SERVER_CONNECTION_TASK_MSG,
- MRCP_SERVER_RESOURCE_ENGINE_TASK_MSG,
+ MRCP_SERVER_ENGINE_TASK_MSG,
MRCP_SERVER_MEDIA_TASK_MSG
} mrcp_server_task_msg_type_e;
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_DISCONNECT
} connection_agent_task_msg_type_e;
typedef struct connection_agent_task_msg_data_t connection_agent_task_msg_data_t;
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 apt_bool_t mrcp_server_disconnect_signal(mrcp_control_channel_t *channel);
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
+ mrcp_server_message_signal,
+ mrcp_server_disconnect_signal
};
-/* Resource engine interface */
+/* MRCP 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;
+ ENGINE_TASK_MSG_OPEN_CHANNEL,
+ ENGINE_TASK_MSG_CLOSE_CHANNEL,
+ ENGINE_TASK_MSG_MESSAGE
+} 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 {
+typedef struct engine_task_msg_data_t engine_task_msg_data_t;
+struct engine_task_msg_data_t {
mrcp_channel_t *channel;
apt_bool_t status;
mrcp_message_t *mrcp_message;
server->pool = pool;
server->dir_layout = dir_layout;
server->resource_factory = NULL;
- server->resource_engine_table = NULL;
+ server->engine_factory = NULL;
+ server->engine_loader = 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;
+ server->engine_msg_pool = NULL;
msg_pool = apt_task_msg_pool_create_dynamic(0,pool);
vtable->on_terminate_complete = mrcp_server_on_terminate_complete;
}
- server->resource_engine_table = apr_hash_make(server->pool);
+ server->engine_factory = mrcp_engine_factory_create(server->pool);
+ server->engine_loader = mrcp_engine_loader_create(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;
apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Invalid Server");
return FALSE;
}
+
+ mrcp_engine_factory_destroy(server->engine_factory);
+ mrcp_engine_loader_destroy(server->engine_loader);
+
task = apt_consumer_task_base_get(server->task);
apt_task_destroy(task);
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)
+/** Register MRCP engine */
+MRCP_DECLARE(apt_bool_t) mrcp_server_engine_register(mrcp_server_t *server, mrcp_engine_t *engine, mrcp_engine_config_t *config)
{
- if(!engine || !name) {
+ if(!engine || !config || !config->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);
+ if(!server->engine_msg_pool) {
+ server->engine_msg_pool = apt_task_msg_pool_create_dynamic(sizeof(engine_task_msg_data_t),server->pool);
}
+ engine->config = config;
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);
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Register MRCP Engine [%s]",config->name);
+ return mrcp_engine_factory_engine_register(server->engine_factory,engine,config->name);
}
/** Register codec manager */
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;
+ mrcp_resource_t *resource;
const char *plugin_name = NULL;
- mrcp_resource_engine_t *resource_engine;
+ mrcp_engine_t *engine;
profile->engine_table = apr_hash_make(server->pool);
for(i=0; i<MRCP_RESOURCE_TYPE_COUNT; i++) {
- resource_name = mrcp_resource_name_get(server->resource_factory,i);
- if(!resource_name) continue;
+ resource = mrcp_resource_get(server->resource_factory,i);
+ if(!resource) continue;
- resource_engine = NULL;
+ 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);
+ plugin_name = apr_table_get(plugin_map,resource->name.buf);
if(plugin_name) {
- resource_engine = mrcp_server_resource_engine_get(server,plugin_name);
+ engine = mrcp_engine_factory_engine_get(server->engine_factory,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;
- }
- }
+ /* next, if no engine found or specified, try to find the first available one */
+ if(!engine) {
+ engine = mrcp_engine_factory_engine_find(server->engine_factory,i);
}
- if(resource_engine) {
- if(plugin_name) {
- apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Assign Resource Engine [%s] [%s]",resource_name->buf,plugin_name);
+ if(engine) {
+ if(engine->config->name) {
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Assign MRCP Engine [%s] [%s]",resource->name.buf,engine->config->name);
}
- apr_hash_set(profile->engine_table,resource_name->buf,resource_name->length,resource_engine);
+ apr_hash_set(profile->engine_table,resource->name.buf,resource->name.length,engine);
}
else {
- apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No Resource Engine Available [%s]",resource_name->buf);
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No MRCP Engine Available [%s]",resource->name.buf);
}
}
return FALSE;
}
if(!profile->resource_factory) {
+ if(!server->resource_factory) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Register Profile: no resources");
+ return FALSE;
+ }
profile->resource_factory = server->resource_factory;
}
mrcp_server_engine_table_make(server,profile,plugin_map);
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)
+/** Register MRCP engine plugin */
+MRCP_DECLARE(apt_bool_t) mrcp_server_plugin_register(mrcp_server_t *server, const char *path, mrcp_engine_config_t *config)
{
- 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");
+ mrcp_engine_t *engine;
+ if(!config) {
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);
+
+ engine = mrcp_engine_loader_plugin_load(server->engine_loader,path,config->name);
+ if(!engine) {
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");
- }
+ if(!server->engine_msg_pool) {
+ server->engine_msg_pool = apt_task_msg_pool_create_dynamic(sizeof(engine_task_msg_data_t),server->pool);
}
- else {
- apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No Entry Point Found for Plugin");
- }
-
- if(status == FALSE) {
- apr_dso_unload(plugin);
- }
- return status;
+ engine->config = config;
+ engine->codec_manager = server->codec_manager;
+ engine->dir_layout = server->dir_layout;
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Register MRCP Engine [%s]",config->name);
+ return mrcp_engine_factory_engine_register(server->engine_factory,engine,config->name);
}
MRCP_DECLARE(apr_pool_t*) mrcp_server_memory_pool_get(mrcp_server_t *server)
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);
- }
- }
+
+ mrcp_engine_factory_open(server->engine_factory);
apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,SERVER_TASK_NAME" Started");
}
{
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);
- }
- }
+
+ mrcp_engine_factory_close(server->engine_factory);
apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,SERVER_TASK_NAME" Terminated");
}
mrcp_server_on_channel_message(connection_message->channel, connection_message->message);
break;
}
+ case CONNECTION_AGENT_TASK_MSG_DISCONNECT:
+ {
+ mrcp_server_on_channel_message(connection_message->channel, connection_message->message);
+ break;
+ }
default:
break;
}
break;
}
- case MRCP_SERVER_RESOURCE_ENGINE_TASK_MSG:
+ case MRCP_SERVER_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);
+ engine_task_msg_data_t *data = (engine_task_msg_data_t*)msg->data;
+ apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Receive Engine Task Message [%d]", msg->sub_type);
switch(msg->sub_type) {
- case RESOURCE_ENGINE_TASK_MSG_OPEN_CHANNEL:
+ case ENGINE_TASK_MSG_OPEN_CHANNEL:
mrcp_server_on_engine_channel_open(data->channel,data->status);
break;
- case RESOURCE_ENGINE_TASK_MSG_CLOSE_CHANNEL:
+ case ENGINE_TASK_MSG_CLOSE_CHANNEL:
mrcp_server_on_engine_channel_close(data->channel);
break;
- case RESOURCE_ENGINE_TASK_MSG_MESSAGE:
+ case ENGINE_TASK_MSG_MESSAGE:
mrcp_server_on_engine_channel_message(data->channel,data->mrcp_message);
break;
default:
}
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);
+ mpf_message_container_t *mpf_message_container = (mpf_message_container_t*) msg->data;
+ apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Receive Media Task Message");
+ mrcp_server_mpf_message_process(mpf_message_container);
break;
}
default:
}
static apt_bool_t mrcp_server_engine_task_msg_signal(
- resource_engine_task_msg_type_e type,
+ engine_task_msg_type_e type,
mrcp_engine_channel_t *engine_channel,
apt_bool_t status,
mrcp_message_t *message)
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;
+ engine_task_msg_data_t *data;
+ apt_task_msg_t *task_msg = apt_task_msg_acquire(server->engine_msg_pool);
+ task_msg->type = MRCP_SERVER_ENGINE_TASK_MSG;
task_msg->sub_type = type;
- data = (resource_engine_task_msg_data_t*) task_msg->data;
+ data = (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");
+ apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Signal Engine Task Message");
return apt_task_msg_signal(task,task_msg);
}
TRUE);
}
+static apt_bool_t mrcp_server_disconnect_signal(mrcp_control_channel_t *channel)
+{
+ mrcp_connection_agent_t *agent = channel->agent;
+ return mrcp_server_connection_task_msg_signal(
+ CONNECTION_AGENT_TASK_MSG_DISCONNECT,
+ agent,
+ channel,
+ NULL,
+ NULL,
+ 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,
+ 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,
+ 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,
+ ENGINE_TASK_MSG_MESSAGE,
channel,
TRUE,
message);
#include "mrcp_server_session.h"
#include "mrcp_resource.h"
#include "mrcp_resource_factory.h"
-#include "mrcp_resource_engine.h"
+#include "mrcp_engine_iface.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 "mpf_termination_factory.h"
+#include "mpf_stream.h"
#include "apt_consumer_task.h"
#include "apt_log.h"
/** 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 */
mrcp_engine_channel_t *engine_channel;
/** MRCP resource state machine */
mrcp_state_machine_t *state_machine;
- /** media descriptor id */
+ /** media descriptor id (position in session descriptor) */
apr_size_t id;
+ /** array of cmid attributes (used for resource grouping) */
+ apr_array_header_t *cmid_arr;
/** waiting state of control media */
apt_bool_t waiting_for_channel;
/** waiting state of media termination */
struct mrcp_termination_slot_t {
/** RTP termination */
- mpf_termination_t *termination;
- /** media descriptor id */
- apr_size_t id;
+ mpf_termination_t *termination;
+ /** media descriptor id (position in SDP message) */
+ apr_size_t id;
+ /** media id (used for resource grouping) */
+ apr_size_t mid;
+ /** Array of associated MRCP channels (mrcp_channel_t*) */
+ apr_array_header_t *channels;
+
/** waiting state */
- apt_bool_t waiting;
+ apt_bool_t waiting;
};
extern const mrcp_engine_channel_event_vtable_t engine_channel_vtable;
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_engine_channels_update(mrcp_server_session_t *session);
static apt_bool_t mrcp_server_session_answer_send(mrcp_server_session_t *session);
+static apt_bool_t mrcp_server_session_terminate_process(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);
-
static apt_bool_t state_machine_on_message_dispatch(mrcp_state_machine_t *state_machine, mrcp_message_t *message);
static apt_bool_t state_machine_on_deactivate(mrcp_state_machine_t *state_machine);
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;
- session->deactivate_flag_count = 0;
+ session->mpf_task_msg = NULL;
+ session->subrequest_count = 0;
+ session->state = SESSION_STATE_NONE;
return session;
}
-static mrcp_engine_channel_t* mrcp_server_engine_channel_create(mrcp_server_session_t *session, const apt_str_t *resource_name)
+static APR_INLINE mrcp_version_e mrcp_session_version_get(mrcp_server_session_t *session)
{
- 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 session->base.signaling_agent->mrcp_version;
+}
+
+static mrcp_engine_channel_t* mrcp_server_engine_channel_create(
+ mrcp_server_session_t *session,
+ mrcp_channel_t *channel,
+ const apt_str_t *resource_name)
+{
+ mrcp_engine_t *engine = apr_hash_get(
+ session->profile->engine_table,
+ resource_name->buf,
+ resource_name->length);
+ if(!engine) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Find MRCP Engine [%s]",resource_name->buf);
return NULL;
}
- return resource_engine->method_vtable->create_channel(resource_engine,session->base.pool);
+ channel->state_machine = engine->create_state_machine(
+ channel,
+ mrcp_session_version_get(session),
+ channel->pool);
+ if(channel->state_machine) {
+ channel->state_machine->on_dispatch = state_machine_on_message_dispatch;
+ channel->state_machine->on_deactivate = state_machine_on_deactivate;
+ }
+
+ return mrcp_engine_channel_virtual_create(engine,mrcp_session_version_get(session),session->base.pool);
}
-static mrcp_channel_t* mrcp_server_channel_create(mrcp_server_session_t *session, const apt_str_t *resource_name, apr_size_t id)
+static mrcp_channel_t* mrcp_server_channel_create(mrcp_server_session_t *session, const apt_str_t *resource_name, apr_size_t id, apr_array_header_t *cmid_arr)
{
mrcp_channel_t *channel;
apr_pool_t *pool = session->base.pool;
channel->state_machine = NULL;
channel->engine_channel = NULL;
channel->id = id;
+ channel->cmid_arr = cmid_arr;
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);
+ resource = mrcp_resource_find(session->profile->resource_factory,resource_name);
if(resource) {
channel->resource = resource;
- if(session->base.signaling_agent->mrcp_version == MRCP_VERSION_2) {
+ if(mrcp_session_version_get(session) == 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,
- session->base.signaling_agent->mrcp_version,
- pool);
- if(channel->state_machine) {
- channel->state_machine->on_dispatch = state_machine_on_message_dispatch;
- channel->state_machine->on_deactivate = state_machine_on_deactivate;
- }
-
- engine_channel = mrcp_server_engine_channel_create(session,resource_name);
+ engine_channel = mrcp_server_engine_channel_create(session,channel,resource_name);
if(engine_channel) {
engine_channel->id = session->base.id;
engine_channel->event_obj = channel;
channel->engine_channel = engine_channel;
}
else {
- apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Resource Engine Channel [%s]",resource_name->buf);
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Engine Channel [%s]",resource_name->buf);
session->answer->status = MRCP_SESSION_STATUS_UNACCEPTABLE_RESOURCE;
}
}
return channel;
}
+static APR_INLINE void mrcp_server_session_state_set(mrcp_server_session_t *session, mrcp_server_session_state_e state)
+{
+ if(session->subrequest_count != 0) {
+ /* error case */
+ session->subrequest_count = 0;
+ }
+ session->state = state;
+}
+
+static APR_INLINE void mrcp_server_session_subrequest_add(mrcp_server_session_t *session)
+{
+ session->subrequest_count++;
+}
+
+static void mrcp_server_session_subrequest_remove(mrcp_server_session_t *session)
+{
+ if(!session->subrequest_count) {
+ /* error case */
+ return;
+ }
+ session->subrequest_count--;
+ if(!session->subrequest_count) {
+ switch(session->state) {
+ case SESSION_STATE_GENERATING_ANSWER:
+ mrcp_server_engine_channels_update(session);
+ break;
+ case SESSION_STATE_INITIALIZING:
+ /* send answer to client */
+ mrcp_server_session_answer_send(session);
+ break;
+ case SESSION_STATE_DEACTIVATING:
+ mrcp_server_session_terminate_process(session);
+ break;
+ case SESSION_STATE_TERMINATING:
+ mrcp_server_session_terminate_send(session);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
mrcp_session_t* mrcp_server_channel_session_get(mrcp_channel_t *channel)
{
return channel->session;
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);
- }
- }
+ mrcp_server_session_subrequest_remove(session);
return TRUE;
}
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);
- }
- }
+ mrcp_server_session_subrequest_remove(session);
return TRUE;
}
return mrcp_server_signaling_message_process(signaling_message);
}
+apt_bool_t mrcp_server_on_disconnect(mrcp_channel_t *channel)
+{
+ /* to be processed */
+ return TRUE;
+}
+
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;
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);
- }
- }
+ mrcp_server_session_subrequest_remove(session);
return TRUE;
}
{
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);
- }
- }
+ mrcp_server_session_subrequest_remove(session);
return TRUE;
}
}
-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);
answer->status = offer->status;
answer->control_media_arr = apr_array_make(pool,offer->control_media_arr->nelts,sizeof(void*));
for(i=0; i<offer->control_media_arr->nelts; i++) {
- control_slot = apr_array_push(answer->control_media_arr);
- *control_slot = NULL;
+ APR_ARRAY_PUSH(answer->control_media_arr,void*) = NULL;
}
answer->audio_media_arr = apr_array_make(pool,offer->audio_media_arr->nelts,sizeof(mpf_rtp_media_descriptor_t*));
for(i=0; i<offer->audio_media_arr->nelts; i++) {
- av_slot = apr_array_push(answer->audio_media_arr);
- *av_slot = NULL;
+ APR_ARRAY_PUSH(answer->audio_media_arr,mpf_rtp_media_descriptor_t*) = NULL;
}
answer->video_media_arr = apr_array_make(pool,offer->video_media_arr->nelts,sizeof(mpf_rtp_media_descriptor_t*));
for(i=0; i<offer->video_media_arr->nelts; i++) {
- av_slot = apr_array_push(answer->video_media_arr);
- *av_slot = NULL;
+ APR_ARRAY_PUSH(answer->video_media_arr,mpf_rtp_media_descriptor_t*) = NULL;
}
return answer;
}
}
mrcp_server_session_add(session);
- session->context = mpf_context_create(session,5,session->base.pool);
+ session->context = mpf_engine_context_create(session->profile->media_engine,session,5,session->base.pool);
}
apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Receive Offer "APT_SID_FMT" [c:%d a:%d v:%d]",
MRCP_SESSION_SID(&session->base),
session->offer = descriptor;
session->answer = mrcp_session_answer_create(descriptor,session->base.pool);
- if(session->base.signaling_agent->mrcp_version == MRCP_VERSION_1) {
+ mrcp_server_session_state_set(session,SESSION_STATE_GENERATING_ANSWER);
+
+ /* first, reset/destroy existing associations and topology */
+ if(mpf_engine_topology_message_add(
+ session->profile->media_engine,
+ MPF_RESET_ASSOCIATIONS,session->context,
+ &session->mpf_task_msg) == TRUE){
+ mrcp_server_session_subrequest_add(session);
+ }
+
+ if(mrcp_session_version_get(session) == MRCP_VERSION_1) {
if(mrcp_server_resource_offer_process(session,descriptor) == TRUE) {
mrcp_server_av_media_offer_process(session,descriptor);
}
mrcp_server_control_media_offer_process(session,descriptor);
mrcp_server_av_media_offer_process(session,descriptor);
}
-
- if(!session->answer_flag_count) {
+
+ /* apply topology based on assigned associations */
+ if(mpf_engine_topology_message_add(
+ session->profile->media_engine,
+ MPF_APPLY_TOPOLOGY,session->context,
+ &session->mpf_task_msg) == TRUE) {
+ mrcp_server_session_subrequest_add(session);
+ }
+ mpf_engine_message_send(session->profile->media_engine,&session->mpf_task_msg);
+
+ if(!session->subrequest_count) {
/* send answer to client */
mrcp_server_session_answer_send(session);
}
mrcp_termination_slot_t *slot;
int i;
apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Receive Terminate Request "APT_SID_FMT,MRCP_SESSION_SID(&session->base));
+
+ mrcp_server_session_state_set(session,SESSION_STATE_TERMINATING);
+
+ if(session->context) {
+ /* first, destroy existing topology */
+ if(mpf_engine_topology_message_add(
+ session->profile->media_engine,
+ MPF_RESET_ASSOCIATIONS,session->context,
+ &session->mpf_task_msg) == TRUE){
+ mrcp_server_session_subrequest_add(session);
+ }
+ }
+
for(i=0; i<session->channels->nelts; i++) {
- channel = ((mrcp_channel_t**)session->channels->elts)[i];
+ channel = APR_ARRAY_IDX(session->channels,i,mrcp_channel_t*);
if(!channel) continue;
/* send remove channel request */
if(channel->control_channel) {
if(mrcp_server_control_channel_remove(channel->control_channel) == TRUE) {
channel->waiting_for_channel = TRUE;
- session->terminate_flag_count++;
+ mrcp_server_session_subrequest_add(session);
}
}
/* 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) {
+ if(mpf_engine_termination_message_add(
+ session->profile->media_engine,
+ MPF_SUBTRACT_TERMINATION,session->context,termination,NULL,
+ &session->mpf_task_msg) == TRUE) {
channel->waiting_for_termination = TRUE;
- session->terminate_flag_count++;
+ mrcp_server_session_subrequest_add(session);
}
}
- /* close resource engine channel */
- if(mrcp_engine_channel_close(channel->engine_channel) == TRUE) {
- session->terminate_flag_count++;
+ /* close engine channel */
+ if(mrcp_engine_channel_virtual_close(channel->engine_channel) == TRUE) {
+ mrcp_server_session_subrequest_add(session);
}
}
}
for(i=0; i<session->terminations->nelts; i++) {
/* get existing termination */
- slot = &((mrcp_termination_slot_t*)session->terminations->elts)[i];
+ slot = &APR_ARRAY_IDX(session->terminations,i,mrcp_termination_slot_t);
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) {
+ if(mpf_engine_termination_message_add(
+ session->profile->media_engine,
+ MPF_SUBTRACT_TERMINATION,session->context,slot->termination,NULL,
+ &session->mpf_task_msg) == TRUE) {
slot->waiting = TRUE;
- session->terminate_flag_count++;
+ mrcp_server_session_subrequest_add(session);
}
}
+
+ if(session->context) {
+ mpf_engine_message_send(session->profile->media_engine,&session->mpf_task_msg);
+ }
+
mrcp_server_session_remove(session);
- if(!session->terminate_flag_count) {
+ if(!session->subrequest_count) {
mrcp_server_session_terminate_send(session);
}
mrcp_channel_t *channel;
int i;
apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Deactivate Session "APT_SID_FMT,MRCP_SESSION_SID(&session->base));
+ mrcp_server_session_state_set(session,SESSION_STATE_DEACTIVATING);
for(i=0; i<session->channels->nelts; i++) {
- channel = ((mrcp_channel_t**)session->channels->elts)[i];
+ channel = APR_ARRAY_IDX(session->channels,i,mrcp_channel_t*);
if(!channel || !channel->state_machine) continue;
if(mrcp_state_machine_deactivate(channel->state_machine) == TRUE) {
- session->deactivate_flag_count++;
+ mrcp_server_session_subrequest_add(session);
}
}
- if(!session->deactivate_flag_count) {
+ if(!session->subrequest_count) {
mrcp_server_session_terminate_process(session);
}
return TRUE;
}
+static apt_bool_t mrcp_server_engine_channels_update(mrcp_server_session_t *session)
+{
+ mrcp_channel_t *channel;
+ mrcp_session_descriptor_t *descriptor = session->offer;
+ if(!descriptor) {
+ return FALSE;
+ }
+
+ mrcp_server_session_state_set(session,SESSION_STATE_INITIALIZING);
+
+ if(mrcp_session_version_get(session) == MRCP_VERSION_1) {
+ if(session->offer) {
+ channel = mrcp_server_channel_find(session,&descriptor->resource_name);
+ if(channel && channel->engine_channel) {
+ /* open engine channel */
+ if(mrcp_engine_channel_virtual_open(channel->engine_channel) == TRUE) {
+ mrcp_server_session_subrequest_add(session);
+ }
+ }
+ }
+ }
+ else {
+ int i;
+ mrcp_control_descriptor_t *control_descriptor;
+ for(i=0; i<session->channels->nelts; i++) {
+ channel = APR_ARRAY_IDX(session->channels,i,mrcp_channel_t*);
+ if(!channel || !channel->engine_channel) continue;
+
+ control_descriptor = mrcp_session_control_media_get(descriptor,i);
+ if(!control_descriptor) continue;
+
+ if(control_descriptor->port) {
+ /* open engine channel */
+ if(mrcp_engine_channel_virtual_open(channel->engine_channel) == TRUE) {
+ mrcp_server_session_subrequest_add(session);
+ }
+ }
+ else {
+ /* close engine channel */
+ if(mrcp_engine_channel_virtual_close(channel->engine_channel) == TRUE) {
+ mrcp_server_session_subrequest_add(session);
+ }
+ }
+ }
+ }
+
+ if(!session->subrequest_count) {
+ mrcp_server_session_answer_send(session);
+ }
+ 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) {
return TRUE;
}
/* create new MRCP channel instance */
- channel = mrcp_server_channel_create(session,&descriptor->resource_name,count);
+ channel = mrcp_server_channel_create(session,&descriptor->resource_name,count,NULL);
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;
- }
- }
- }
+ APR_ARRAY_PUSH(session->channels,mrcp_channel_t*) = channel;
+ if(channel->engine_channel && channel->engine_channel->termination) {
+ mpf_termination_t *termination = channel->engine_channel->termination;
+ /* send add termination request (add to media context) */
+ if(mpf_engine_termination_message_add(
+ session->profile->media_engine,
+ MPF_ADD_TERMINATION,session->context,termination,NULL,
+ &session->mpf_task_msg) == TRUE) {
+ channel->waiting_for_termination = TRUE;
+ mrcp_server_session_subrequest_add(session);
}
}
}
/* update existing control channels */
for(i=0; i<count; i++) {
- /* get existing termination */
- channel = *((mrcp_channel_t**)session->channels->elts + i);
+ channel = APR_ARRAY_IDX(session->channels,i,mrcp_channel_t*);
if(!channel) continue;
channel->waiting_for_channel = FALSE;
/* send offer */
if(mrcp_server_control_channel_modify(channel->control_channel,control_descriptor) == TRUE) {
channel->waiting_for_channel = TRUE;
- session->answer_flag_count++;
+ mrcp_server_session_subrequest_add(session);
}
}
/* add new control channels */
for(; i<descriptor->control_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);
+ channel = mrcp_server_channel_create(session,&control_descriptor->resource_name,i,control_descriptor->cmid_arr);
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;
+ APR_ARRAY_PUSH(session->channels,mrcp_channel_t*) = 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++;
+ mrcp_server_session_subrequest_add(session);
}
}
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++;
+ if(channel->engine_channel && channel->engine_channel->termination) {
+ mpf_termination_t *termination = channel->engine_channel->termination;
+ /* send add termination request (add to media context) */
+ if(mpf_engine_termination_message_add(
+ session->profile->media_engine,
+ MPF_ADD_TERMINATION,session->context,termination,NULL,
+ &session->mpf_task_msg) == TRUE) {
+ channel->waiting_for_termination = TRUE;
+ mrcp_server_session_subrequest_add(session);
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static mpf_rtp_termination_descriptor_t* mrcp_server_associations_build(mrcp_server_session_t *session, mrcp_session_descriptor_t *descriptor, mrcp_termination_slot_t *slot)
+{
+ int i;
+ mrcp_channel_t *channel;
+ mpf_audio_stream_t *audio_stream;
+ mpf_stream_capabilities_t *capabilities = NULL;
+ mpf_rtp_termination_descriptor_t *rtp_descriptor;
+ mpf_rtp_media_descriptor_t *media_descriptor = mrcp_session_audio_media_get(descriptor,slot->id);
+ if(!media_descriptor) {
+ return NULL;
+ }
+ /* 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 = media_descriptor;
+
+ slot->mid = media_descriptor->mid;
+ slot->channels = apr_array_make(session->base.pool,1,sizeof(mrcp_channel_t*));
+ for(i=0; i<session->channels->nelts; i++) {
+ channel = APR_ARRAY_IDX(session->channels,i,mrcp_channel_t*);
+ if(!channel) continue;
+
+ if(!channel->cmid_arr || mrcp_cmid_find(channel->cmid_arr,slot->mid) == TRUE) {
+ APR_ARRAY_PUSH(slot->channels, mrcp_channel_t*) = channel;
+
+ audio_stream = NULL;
+ if(channel->engine_channel && channel->engine_channel->termination) {
+ audio_stream = mpf_termination_audio_stream_get(channel->engine_channel->termination);
+ }
+ if(!audio_stream) continue;
+
+ if(audio_stream->capabilities) {
+ /* set descriptor according to media termination(s)
+ of associated control channel(s) */
+ if(capabilities) {
+ mpf_stream_capabilities_merge(
+ capabilities,
+ audio_stream->capabilities,
+ session->base.pool);
}
+ else {
+ capabilities = mpf_stream_capabilities_clone(
+ audio_stream->capabilities,
+ session->base.pool);
+ }
+ }
+
+ if(mrcp_session_version_get(session) == MRCP_VERSION_1) {
+ /* implicitly modify the descriptor, if needed */
+ mpf_stream_direction_e direction = audio_stream->direction;
+ media_descriptor->direction |= direction;
+ if(media_descriptor->state == MPF_MEDIA_DISABLED) {
+ media_descriptor->state = MPF_MEDIA_ENABLED;
}
}
}
}
+ if(capabilities) {
+ capabilities->direction = mpf_stream_reverse_direction_get(capabilities->direction);
+ rtp_descriptor->audio.capabilities = capabilities;
+ }
+ return rtp_descriptor;
+}
+static apt_bool_t mrcp_server_associations_set(mrcp_server_session_t *session, mrcp_session_descriptor_t *descriptor, mrcp_termination_slot_t *slot)
+{
+ int i;
+ mrcp_channel_t *channel;
+ for(i=0; i<slot->channels->nelts; i++) {
+ channel = ((mrcp_channel_t**)slot->channels->elts)[i];
+ if(!channel || !channel->engine_channel) continue;
+
+ if(mpf_engine_assoc_message_add(
+ session->profile->media_engine,
+ MPF_ADD_ASSOCIATION,session->context,slot->termination,channel->engine_channel->termination,
+ &session->mpf_task_msg) == TRUE) {
+ mrcp_server_session_subrequest_add(session);
+ }
+ }
return TRUE;
}
static apt_bool_t mrcp_server_av_media_offer_process(mrcp_server_session_t *session, mrcp_session_descriptor_t *descriptor)
{
+ mpf_rtp_termination_descriptor_t *rtp_descriptor;
mrcp_termination_slot_t *slot;
int i;
int count = session->terminations->nelts;
/* update existing terminations */
for(i=0; i<count; i++) {
- mpf_rtp_termination_descriptor_t *rtp_descriptor;
/* get existing termination */
- slot = &((mrcp_termination_slot_t*)session->terminations->elts)[i];
+ slot = &APR_ARRAY_IDX(session->terminations,i,mrcp_termination_slot_t);
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);
+ /* build associations between specified RTP termination and control channels */
+ rtp_descriptor = mrcp_server_associations_build(session,descriptor,slot);
+ if(!rtp_descriptor) continue;
/* 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) {
+ if(mpf_engine_termination_message_add(
+ session->profile->media_engine,
+ MPF_MODIFY_TERMINATION,session->context,slot->termination,rtp_descriptor,
+ &session->mpf_task_msg) == TRUE) {
slot->waiting = TRUE;
- session->answer_flag_count++;
+ mrcp_server_session_subrequest_add(session);
}
+
+ /* set built associations */
+ mrcp_server_associations_set(session,descriptor,slot);
}
/* add new terminations */
for(; i<descriptor->audio_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);
apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Add RTP Termination [%d]",i);
slot = apr_array_push(session->terminations);
slot->id = i;
+ slot->mid = 0;
slot->waiting = FALSE;
slot->termination = termination;
+ slot->channels = NULL;
- /* 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);
+ /* build associations between specified RTP termination and control channels */
+ rtp_descriptor = mrcp_server_associations_build(session,descriptor,slot);
+ if(!rtp_descriptor) continue;
/* send add termination request (add to media context) */
- if(mrcp_server_mpf_request_send(session,MPF_COMMAND_ADD,session->context,termination,rtp_descriptor) == TRUE) {
+ if(mpf_engine_termination_message_add(
+ session->profile->media_engine,
+ MPF_ADD_TERMINATION,session->context,termination,rtp_descriptor,
+ &session->mpf_task_msg) == TRUE) {
slot->waiting = TRUE;
- session->answer_flag_count++;
+ mrcp_server_session_subrequest_add(session);
}
+
+ /* set built associations */
+ mrcp_server_associations_set(session,descriptor,slot);
}
return TRUE;
}
int i;
mrcp_channel_t *channel;
for(i=0; i<session->channels->nelts; i++) {
- channel = ((mrcp_channel_t**)session->channels->elts)[i];
+ channel = APR_ARRAY_IDX(session->channels,i,mrcp_channel_t*);
if(!channel) continue;
if(channel->control_channel) {
channel->control_channel = NULL;
}
if(channel->engine_channel) {
- mrcp_engine_channel_destroy(channel->engine_channel);
+ mrcp_engine_channel_virtual_destroy(channel->engine_channel);
channel->engine_channel = NULL;
}
}
int i;
mrcp_termination_slot_t *slot;
for(i=0; i<session->terminations->nelts; i++) {
- slot = &((mrcp_termination_slot_t*)session->terminations->elts)[i];
+ slot = &APR_ARRAY_IDX(session->terminations,i,mrcp_termination_slot_t);
if(slot && slot->termination == termination) {
return slot;
}
int i;
mrcp_channel_t *channel;
for(i=0; i<session->channels->nelts; i++) {
- channel = ((mrcp_channel_t**)session->channels->elts)[i];
+ channel = APR_ARRAY_IDX(session->channels,i,mrcp_channel_t*);
if(!channel) continue;
if(channel->engine_channel && channel->engine_channel->termination == termination) {
int i;
mrcp_channel_t *channel;
for(i=0; i<session->channels->nelts; i++) {
- channel = ((mrcp_channel_t**)session->channels->elts)[i];
+ channel = APR_ARRAY_IDX(session->channels,i,mrcp_channel_t*);
if(!channel) continue;
- if(apt_string_compare(&channel->resource_name,resource_name) == TRUE) {
+ if(apt_string_compare(&channel->resource->name,resource_name) == TRUE) {
return channel;
}
}
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;
+ session->answer->ip = rtp_descriptor->audio.local->ip;
+ session->answer->ext_ip = rtp_descriptor->audio.local->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);
- }
- }
+ mrcp_server_session_subrequest_remove(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);
- }
- }
+ mrcp_server_session_subrequest_remove(session);
}
}
return TRUE;
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);
- }
- }
+ mrcp_server_session_subrequest_remove(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);
- }
+ mrcp_server_session_subrequest_remove(session);
+ }
+ }
+ return TRUE;
+}
+
+apt_bool_t mrcp_server_mpf_message_process(mpf_message_container_t *mpf_message_container)
+{
+ apr_size_t i;
+ mrcp_server_session_t *session;
+ const mpf_message_t *mpf_message;
+ for(i=0; i<mpf_message_container->count; i++) {
+ mpf_message = &mpf_message_container->messages[i];
+ if(mpf_message->context) {
+ session = mpf_engine_context_object_get(mpf_message->context);
+ }
+ else {
+ session = NULL;
+ }
+
+ if(mpf_message->message_type == MPF_MESSAGE_TYPE_RESPONSE) {
+ switch(mpf_message->command_id) {
+ case MPF_ADD_TERMINATION:
+ apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"On Termination Add");
+ mrcp_server_on_termination_modify(session,mpf_message);
+ break;
+ case MPF_MODIFY_TERMINATION:
+ apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"On Termination Modify");
+ mrcp_server_on_termination_modify(session,mpf_message);
+ break;
+ case MPF_SUBTRACT_TERMINATION:
+ apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"On Termination Subtract");
+ mrcp_server_on_termination_subtract(session,mpf_message);
+ break;
+ case MPF_ADD_ASSOCIATION:
+ case MPF_REMOVE_ASSOCIATION:
+ case MPF_RESET_ASSOCIATIONS:
+ case MPF_APPLY_TOPOLOGY:
+ case MPF_DESTROY_TOPOLOGY:
+ mrcp_server_session_subrequest_remove(session);
+ 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;
}
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 */
+ /* send request message to engine for actual processing */
if(channel->engine_channel) {
mrcp_engine_channel_request_process(channel->engine_channel,message);
}
{
mrcp_channel_t *channel = state_machine->obj;
mrcp_server_session_t *session = (mrcp_server_session_t*)channel->session;
- if(session->deactivate_flag_count) {
- session->deactivate_flag_count --;
- if(!session->deactivate_flag_count) {
- mrcp_server_session_terminate_process(session);
- }
- }
+ mrcp_server_session_subrequest_remove(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);
-}
APT_BEGIN_EXTERN_C
+/** Macro to log session pointers */
#define MRCP_SESSION_PTR(session) (session)
+/** Macro to log session string identifiers */
#define MRCP_SESSION_SID(session) \
(session)->id.buf ? (session)->id.buf : "new"
+/** Macro to log session pointers and string identifiers */
#define MRCP_SESSION_PTRSID(session) \
MRCP_SESSION_PTR(session), MRCP_SESSION_SID(session)
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;
+ APR_ARRAY_PUSH(descriptor->control_media_arr,void*) = media;
return mrcp_session_media_count_get(descriptor) - 1;
}
if((int)id >= descriptor->control_media_arr->nelts) {
return NULL;
}
- return ((void**)descriptor->control_media_arr->elts)[id];
+ return APR_ARRAY_IDX(descriptor->control_media_arr,id,void*);
}
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;
+ APR_ARRAY_IDX(descriptor->control_media_arr,id,void*) = 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;
+ APR_ARRAY_PUSH(descriptor->audio_media_arr,mpf_rtp_media_descriptor_t*) = media;
return mrcp_session_media_count_get(descriptor) - 1;
}
if((int)id >= descriptor->audio_media_arr->nelts) {
return NULL;
}
- return ((mpf_rtp_media_descriptor_t**)descriptor->audio_media_arr->elts)[id];
+ return APR_ARRAY_IDX(descriptor->audio_media_arr,id,mpf_rtp_media_descriptor_t*);
}
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;
+ APR_ARRAY_IDX(descriptor->audio_media_arr,id,mpf_rtp_media_descriptor_t*) = 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;
+ APR_ARRAY_PUSH(descriptor->video_media_arr,mpf_rtp_media_descriptor_t*) = media;
return mrcp_session_media_count_get(descriptor) - 1;
}
if((int)id >= descriptor->video_media_arr->nelts) {
return NULL;
}
- return ((mpf_rtp_media_descriptor_t**)descriptor->video_media_arr->elts)[id];
+ return APR_ARRAY_IDX(descriptor->video_media_arr,id,mpf_rtp_media_descriptor_t*);
}
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;
+ APR_ARRAY_IDX(descriptor->video_media_arr,id,mpf_rtp_media_descriptor_t*) = media;
return TRUE;
}
include_HEADERS = include/mrcp.h \
include/mrcp_types.h \
+ message/include/mrcp_start_line.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_resource_loader.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
+ resources/include/mrcp_recorder_header.h \
+ resources/include/mrcp_recorder_resource.h
libmrcp_la_SOURCES = message/src/mrcp_header_accessor.c \
message/src/mrcp_generic_header.c \
+ message/src/mrcp_start_line.c \
message/src/mrcp_message.c \
control/src/mrcp_resource_factory.c \
+ control/src/mrcp_resource_loader.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
+ resources/src/mrcp_recorder_header.c \
+ resources/src/mrcp_recorder_resource.c
#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;
+ mrcp_resource_id id;
+ /** MRCP resource name */
+ apt_str_t name;
- /** 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);
+ /** Get string table of methods */
+ const apt_str_table_item_t* (*get_method_str_table)(mrcp_version_e version);
+ /** Number of methods */
+ apr_size_t method_count;
- /** Create client side state machine */
- mrcp_state_machine_t* (*create_client_state_machine)(void *obj, mrcp_version_e version, apr_pool_t *pool);
- /** Create server side state machine */
- mrcp_state_machine_t* (*create_server_state_machine)(void *obj, mrcp_version_e version, apr_pool_t *pool);
+ /** Get string table of events */
+ const apt_str_table_item_t* (*get_event_str_table)(mrcp_version_e version);
+ /** Number of events */
+ apr_size_t event_count;
+
+ /** Get vtable of resource header */
+ const mrcp_header_vtable_t* (*get_resource_header_vtable)(mrcp_version_e version);
};
/** Initialize MRCP resource */
-static APR_INLINE void mrcp_resource_init(mrcp_resource_t *resource)
+static APR_INLINE mrcp_resource_t* mrcp_resource_create(apr_pool_t *pool)
{
- resource->resourcify_message_by_id = NULL;
- resource->resourcify_message_by_name = NULL;
- resource->create_client_state_machine = NULL;
- resource->create_server_state_machine = NULL;
+ mrcp_resource_t *resource = (mrcp_resource_t*) apr_palloc(pool, sizeof(mrcp_resource_t));
+ resource->id = 0;
+ apt_string_reset(&resource->name);
+ resource->method_count = 0;
+ resource->event_count = 0;
+ resource->get_method_str_table = NULL;
+ resource->get_event_str_table = NULL;
+ resource->get_resource_header_vtable = NULL;
+ return resource;
}
/** 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;
+ if(resource->method_count && resource->event_count &&
+ resource->get_method_str_table && resource->get_event_str_table &&
+ resource->get_resource_header_vtable &&
+ resource->name.buf && resource->name.length) {
+ return TRUE;
+ }
+ return FALSE;
}
APT_END_EXTERN_C
* @brief Aggregation of MRCP Resources
*/
-#include "apt_string_table.h"
#include "apt_text_stream.h"
#include "mrcp_types.h"
/** 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);
+MRCP_DECLARE(apt_bool_t) mrcp_resource_register(mrcp_resource_factory_t *resource_factory, mrcp_resource_t *resource);
/** 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);
+/** Find MRCP resource by resource name */
+MRCP_DECLARE(mrcp_resource_t*) mrcp_resource_find(mrcp_resource_factory_t *resource_factory, const apt_str_t *name);
APT_END_EXTERN_C
--- /dev/null
+/*
+ * 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_LOADER_H__
+#define __MRCP_RESOURCE_LOADER_H__
+
+/**
+ * @file mrcp_resource_loader.h
+ * @brief MRCP Resource Loader
+ */
+
+#include "apt_string.h"
+#include "mrcp_types.h"
+
+APT_BEGIN_EXTERN_C
+
+/** Opaque resource loader declaration */
+typedef struct mrcp_resource_loader_t mrcp_resource_loader_t;
+
+
+/** Create MRCP resource loader */
+MRCP_DECLARE(mrcp_resource_loader_t*) mrcp_resource_loader_create(apt_bool_t load_all_resources, apr_pool_t *pool);
+
+/** Load all MRCP resources */
+MRCP_DECLARE(apt_bool_t) mrcp_resources_load(mrcp_resource_loader_t *loader);
+
+/** Load MRCP resource by resource name */
+MRCP_DECLARE(apt_bool_t) mrcp_resource_load(mrcp_resource_loader_t *loader, const apt_str_t *name);
+
+/** Load MRCP resource by resource identifier */
+MRCP_DECLARE(apt_bool_t) mrcp_resource_load_by_id(mrcp_resource_loader_t *loader, mrcp_resource_id id);
+
+/** Get MRCP resource factory */
+MRCP_DECLARE(mrcp_resource_factory_t*) mrcp_resource_factory_get(mrcp_resource_loader_t *loader);
+
+APT_END_EXTERN_C
+
+#endif /*__MRCP_RESOURCE_LOADER_H__*/
* limitations under the License.
*/
+#include <apr_hash.h>
#include "mrcp_resource_factory.h"
#include "mrcp_message.h"
#include "mrcp_resource.h"
/** Resource factory definition (aggregation of resources) */
struct mrcp_resource_factory_t {
- /** Array of MRCP resources */
- mrcp_resource_t **resource_array;
+ /** Array of MRCP resources (reference by id) */
+ 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;
+ apr_size_t resource_count;
+ /** Hash of MRCP resources (reference by name) */
+ apr_hash_t *resource_hash;
};
/** Create MRCP resource factory */
for(i=0; i<resource_count; i++) {
resource_factory->resource_array[i] = NULL;
}
- resource_factory->string_table = NULL;
-
+ resource_factory->resource_hash = apr_hash_make(pool);
return resource_factory;
}
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)
+MRCP_DECLARE(apt_bool_t) mrcp_resource_register(mrcp_resource_factory_t *resource_factory, mrcp_resource_t *resource)
{
- if(!resource || resource_id >= resource_factory->resource_count) {
+ if(!resource || resource->id >= resource_factory->resource_count) {
/* invalid params */
return FALSE;
}
- if(resource_factory->resource_array[resource_id]) {
+ 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;
+ apr_hash_set(resource_factory->resource_hash,resource->name.buf,resource->name.length,resource);
return TRUE;
}
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)
+/** Find MRCP resource by resource name */
+MRCP_DECLARE(mrcp_resource_t*) mrcp_resource_find(mrcp_resource_factory_t *resource_factory, const apt_str_t *name)
{
- 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;
+ if(!name->buf || !name->length) {
+ return NULL;
}
- 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);
+ return apr_hash_get(resource_factory->resource_hash,name->buf,name->length);
}
--- /dev/null
+/*
+ * 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_loader.h"
+#include "mrcp_resource_factory.h"
+#include "mrcp_resource.h"
+#include "mrcp_synth_resource.h"
+#include "mrcp_recog_resource.h"
+#include "mrcp_recorder_resource.h"
+#include "apt_log.h"
+
+/** Resource loader */
+struct mrcp_resource_loader_t {
+ mrcp_resource_factory_t *factory;
+ apr_pool_t *pool;
+};
+
+/** 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},
+ {{"recorder", 8},0}
+};
+
+static mrcp_resource_t* mrcp_resource_create_by_id(mrcp_resource_id id, apr_pool_t *pool);
+
+/** Create default MRCP resource factory */
+MRCP_DECLARE(mrcp_resource_loader_t*) mrcp_resource_loader_create(apt_bool_t load_all_resources, apr_pool_t *pool)
+{
+ mrcp_resource_loader_t *loader;
+ mrcp_resource_factory_t *resource_factory;
+ resource_factory = mrcp_resource_factory_create(MRCP_RESOURCE_TYPE_COUNT,pool);
+ if(!resource_factory) {
+ return NULL;
+ }
+
+ loader = apr_palloc(pool,sizeof(mrcp_resource_loader_t));
+ loader->factory = resource_factory;
+ loader->pool = pool;
+
+ if(load_all_resources == TRUE) {
+ mrcp_resources_load(loader);
+ }
+
+ return loader;
+}
+
+/** Load all MRCP resources */
+MRCP_DECLARE(apt_bool_t) mrcp_resources_load(mrcp_resource_loader_t *loader)
+{
+ mrcp_resource_id id;
+ for(id=0; id<MRCP_RESOURCE_TYPE_COUNT; id++) {
+ mrcp_resource_load_by_id(loader,id);
+ }
+ return TRUE;
+}
+
+/** Load MRCP resource by resource name */
+MRCP_DECLARE(apt_bool_t) mrcp_resource_load(mrcp_resource_loader_t *loader, const apt_str_t *name)
+{
+ mrcp_resource_id id = apt_string_table_id_find(
+ mrcp_resource_string_table,
+ MRCP_RESOURCE_TYPE_COUNT,
+ name);
+
+ mrcp_resource_t *resource = mrcp_resource_create_by_id(id,loader->pool);
+ if(!resource) {
+ apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Failed to Load Resource [%d]",id);
+ return FALSE;
+ }
+
+ apt_string_copy(&resource->name,name,loader->pool);
+ apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Register Resource [%s]",name->buf);
+ return mrcp_resource_register(loader->factory,resource);
+}
+
+/** Load MRCP resource by resource identifier */
+MRCP_DECLARE(apt_bool_t) mrcp_resource_load_by_id(mrcp_resource_loader_t *loader, mrcp_resource_id id)
+{
+ const apt_str_t *name = apt_string_table_str_get(
+ mrcp_resource_string_table,
+ MRCP_RESOURCE_TYPE_COUNT,
+ id);
+ mrcp_resource_t *resource = mrcp_resource_create_by_id(id,loader->pool);
+ if(!resource || !name) {
+ apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Failed to Load Resource [%d]",id);
+ return FALSE;
+ }
+
+ resource->name = *name;
+ apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Register Resource [%s]",resource->name.buf);
+ return mrcp_resource_register(loader->factory,resource);
+}
+
+/** Get MRCP resource factory */
+MRCP_DECLARE(mrcp_resource_factory_t*) mrcp_resource_factory_get(mrcp_resource_loader_t *loader)
+{
+ return loader->factory;
+}
+
+static mrcp_resource_t* mrcp_resource_create_by_id(mrcp_resource_id id, apr_pool_t *pool)
+{
+ mrcp_resource_t *resource = NULL;
+ switch(id) {
+ case MRCP_SYNTHESIZER_RESOURCE:
+ resource = mrcp_synth_resource_create(pool);
+ break;
+ case MRCP_RECOGNIZER_RESOURCE:
+ resource = mrcp_recog_resource_create(pool);
+ break;
+ case MRCP_RECORDER_RESOURCE:
+ resource = mrcp_recorder_resource_create(pool);
+ break;
+ }
+
+ if(resource) {
+ resource->id = id;
+ }
+ return resource;
+}
/** 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)
{
+ mrcp_resource_t *resource;
+
/* parse start-line */
if(mrcp_start_line_parse(&message->start_line,stream,message->pool) == FALSE) {
return FALSE;
mrcp_channel_id_parse(&message->channel_id,stream,message->pool);
}
- if(mrcp_message_resourcify_by_name(resource_factory,message) == FALSE) {
+ /* find resource */
+ resource = mrcp_resource_find(resource_factory,&message->channel_id.resource_name);
+ if(!resource) {
+ return FALSE;
+ }
+
+ if(mrcp_message_resource_set(message,resource) == FALSE) {
return FALSE;
}
/** 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;
}
/* finalize start-line generation */
- mrcp_start_line_finalize(&message->start_line,message->body.length,stream);
+ if(mrcp_start_line_finalize(&message->start_line,message->body.length,stream) == FALSE) {
+ return FALSE;
+ }
+
return TRUE;
}
MRCP_VERSION_UNKNOWN = 0, /**< Unknown version */
MRCP_VERSION_1 = 1, /**< MRCPv1 (RFC4463) */
- MRCP_VERSION_2 = 2 /**< MRCPv2 (draft-ietf-speechsc-mrcpv2-15) */
+ MRCP_VERSION_2 = 2 /**< MRCPv2 (draft-ietf-speechsc-mrcpv2-20) */
} mrcp_version_e;
/** Enumeration of MRCP resource types */
typedef enum {
MRCP_SYNTHESIZER_RESOURCE, /**< Synthesizer resource */
MRCP_RECOGNIZER_RESOURCE, /**< Recognizer resource */
+ MRCP_RECORDER_RESOURCE, /**< Recorder 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. */
+/* MRCPv2 specifies request-id as 32bit unsigned integer,
+ * while MRCPv1 doesn't limit this value (1 * DIGIT).
+ * Some MRCPv1 clients use too long request-id.
+ * To support them #define TOO_LONG_MRCP_REQUEST_ID
+ */
+#ifdef TOO_LONG_MRCP_REQUEST_ID
+/** MRCP request identifier */
+typedef apr_uint64_t mrcp_request_id;
+/** Format to log MRCP request identifier */
+#define MRCP_REQUEST_ID_FMT APR_UINT64_T_FMT
+#else
+/** MRCP request identifier */
+typedef apr_uint32_t mrcp_request_id;
+/** Format to log MRCP request identifier */
+#define MRCP_REQUEST_ID_FMT "d"
+#endif
+
+
+/** Method identifier associated with method name */
typedef apr_size_t mrcp_method_id;
-/** Resource identifier associated with resource name. */
+/** Resource identifier associated with resource name */
typedef apr_size_t mrcp_resource_id;
*/
#include "mrcp_types.h"
+#include "mrcp_start_line.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 message */
struct mrcp_message_t {
/** Start-line of MRCP message */
- mrcp_start_line_t start_line;
+ mrcp_start_line_t start_line;
/** Channel-identifier of MRCP message */
- mrcp_channel_id channel_id;
+ mrcp_channel_id channel_id;
/** Header of MRCP message */
- mrcp_message_header_t header;
+ mrcp_message_header_t header;
/** Body of MRCP message */
- apt_str_t body;
+ apt_str_t body;
+ /** Associated MRCP resource */
+ mrcp_resource_t *resource;
/** Memory pool MRCP message is allocated from */
- apr_pool_t *pool;
+ 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);
+MRCP_DECLARE(mrcp_message_t*) mrcp_request_create(mrcp_resource_t *resource, mrcp_version_e version, 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);
+/** Associate MRCP resource specific data by resource name */
+MRCP_DECLARE(apt_bool_t) mrcp_message_resource_set(mrcp_message_t *message, mrcp_resource_t *resource);
+
/** Validate MRCP message */
MRCP_DECLARE(apt_bool_t) mrcp_message_validate(mrcp_message_t *message);
--- /dev/null
+/*
+ * 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_START_LINE_H__
+#define __MRCP_START_LINE_H__
+
+/**
+ * @file mrcp_start_line.h
+ * @brief MRCP Start Line Definition
+ */
+
+#include "mrcp_types.h"
+#include "apt_text_stream.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;
+
+/** 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) */
+ apr_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);
+
+/** Parse MRCP request-id */
+MRCP_DECLARE(mrcp_request_id) mrcp_request_id_parse(const apt_str_t *field);
+/** Generate MRCP request-id */
+MRCP_DECLARE(apt_bool_t) mrcp_request_id_generate(mrcp_request_id request_id, apt_text_stream_t *stream);
+
+
+APT_END_EXTERN_C
+
+#endif /*__MRCP_START_LINE_H__*/
*/
#include "mrcp_generic_header.h"
+#include "mrcp_start_line.h"
/** String table of mrcp generic-header fields (mrcp_generic_header_id) */
static const apt_str_table_item_t generic_header_string_table[] = {
{{"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)
{
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->ids[request_id_list->count] = mrcp_request_id_parse(&field);
request_id_list->count++;
}
return TRUE;
{
size_t i;
for(i=0; i<request_id_list->count; i++) {
- apt_size_value_generate(request_id_list->ids[i],stream);
+ mrcp_request_id_generate(request_id_list->ids[i],stream);
if(i < request_id_list->count-1) {
*stream->pos++ = ',';
}
MRCP_DECLARE(void) mrcp_header_property_add(mrcp_header_accessor_t *accessor, apr_size_t id)
{
+ if(!accessor->vtable) {
+ return;
+ }
+
if(id < accessor->vtable->field_count) {
char *prop = &accessor->properties[id];
if((*prop & MRCP_HEADER_FIELD_NAME) != MRCP_HEADER_FIELD_NAME) {
MRCP_DECLARE(void) mrcp_header_name_property_add(mrcp_header_accessor_t *accessor, apr_size_t id)
{
+ if(!accessor->vtable) {
+ return;
+ }
+
if(id < accessor->vtable->field_count) {
char *prop = &accessor->properties[id];
if((*prop & MRCP_HEADER_FIELD_NAME) != MRCP_HEADER_FIELD_NAME) {
MRCP_DECLARE(void) mrcp_header_property_remove(mrcp_header_accessor_t *accessor, apr_size_t id)
{
+ if(!accessor->vtable) {
+ return;
+ }
+
if(id < accessor->vtable->field_count) {
char *prop = &accessor->properties[id];
if((*prop & MRCP_HEADER_FIELD_NAME) == MRCP_HEADER_FIELD_NAME) {
MRCP_DECLARE(apt_bool_t) mrcp_header_property_check(mrcp_header_accessor_t *accessor, apr_size_t id)
{
+ if(!accessor->vtable) {
+ return FALSE;
+ }
+
if((id < accessor->vtable->field_count) && accessor->properties) {
if((accessor->properties[id] & MRCP_HEADER_FIELD_NAME) == MRCP_HEADER_FIELD_NAME) {
return TRUE;
for(i=0, j=0; i<parent->vtable->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);
+ if((accessor->properties[i] & MRCP_HEADER_FIELD_NAME) != MRCP_HEADER_FIELD_NAME) {
+ 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);
+ }
}
}
}
#include "mrcp_message.h"
#include "mrcp_generic_header.h"
+#include "mrcp_resource.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 */
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)
+static 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->resource = NULL;
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)
+/** 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_resource_set_by_id(mrcp_message_t *message, mrcp_resource_t *resource)
+{
+ if(!resource) {
+ return FALSE;
+ }
+ message->resource = resource;
+
+ message->channel_id.resource_name = resource->name;
+
+ mrcp_generic_header_accessor_set(message);
+ message->header.resource_header_accessor.vtable =
+ resource->get_resource_header_vtable(message->start_line.version);
+
+ /* 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(
+ resource->get_method_str_table(message->start_line.version),
+ resource->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(
+ resource->get_event_str_table(message->start_line.version),
+ resource->event_count,
+ message->start_line.method_id);
+ if(!name) {
+ return FALSE;
+ }
+ message->start_line.method_name = *name;
+ }
+
+ return TRUE;
+}
+
+/** Associate MRCP resource specific data by resource name */
+MRCP_DECLARE(apt_bool_t) mrcp_message_resource_set(mrcp_message_t *message, mrcp_resource_t *resource)
+{
+ if(!resource) {
+ return FALSE;
+ }
+ message->resource = resource;
+
+ mrcp_generic_header_accessor_set(message);
+ message->header.resource_header_accessor.vtable =
+ resource->get_resource_header_vtable(message->start_line.version);
+
+ /* 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(
+ resource->get_method_str_table(message->start_line.version),
+ resource->method_count,
+ &message->start_line.method_name);
+ if(message->start_line.method_id >= resource->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(
+ resource->get_event_str_table(message->start_line.version),
+ resource->event_count,
+ &message->start_line.method_name);
+ if(message->start_line.method_id >= resource->event_count) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/** Create MRCP message */
+MRCP_DECLARE(mrcp_message_t*) mrcp_message_create(apr_pool_t *pool)
{
- 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;
+ mrcp_message_t *message = apr_palloc(pool,sizeof(mrcp_message_t));
+ mrcp_message_init(message,pool);
+ return 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)
+MRCP_DECLARE(mrcp_message_t*) mrcp_request_create(mrcp_resource_t *resource, mrcp_version_e version, 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.version = version;
request_message->start_line.method_id = method_id;
- request_message->channel_id.resource_id = resource_id;
+ mrcp_message_resource_set_by_id(request_message,resource);
return request_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;
+ if(request_message) {
+ response_message->channel_id = request_message->channel_id;
+ response_message->start_line.request_id = request_message->start_line.request_id;
+ response_message->start_line.version = request_message->start_line.version;
+ response_message->start_line.method_id = request_message->start_line.method_id;
+ mrcp_message_resource_set_by_id(response_message,request_message->resource);
+ }
return response_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;
+ if(request_message) {
+ event_message->channel_id = request_message->channel_id;
+ event_message->start_line.request_id = request_message->start_line.request_id;
+ event_message->start_line.version = request_message->start_line.version;
+ mrcp_message_resource_set_by_id(event_message,request_message->resource);
+ }
return event_message;
}
--- /dev/null
+/*
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include "mrcp_start_line.h"
+#include "apt_string_table.h"
+#include "apt_log.h"
+
+/** Protocol name used in version string */
+#define MRCP_NAME "MRCP"
+#define MRCP_NAME_LENGTH (sizeof(MRCP_NAME)-1)
+
+/** Separators used in MRCP version string parse/generate */
+#define MRCP_NAME_VERSION_SEPARATOR '/'
+#define MRCP_VERSION_MAJOR_MINOR_SEPARATOR '.'
+
+/** Max number of digits message length consists of */
+#define MAX_DIGIT_COUNT 6
+
+
+/** 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 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 temporary used to store offset */
+ /* reserving MAX_DIGIT_COUNT space for start_line->length */
+ memset(stream->pos,APT_TOKEN_SP,MAX_DIGIT_COUNT+1);
+ stream->pos += MAX_DIGIT_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);
+ }
+ 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 temporary used to store offset */
+ length -= MAX_DIGIT_COUNT;
+ if(apt_var_length_value_generate(&length,MAX_DIGIT_COUNT,&field) == FALSE) {
+ return FALSE;
+ }
+ field.buf[field.length] = APT_TOKEN_SP;
+ start_line->length += field.length;
+
+ field.length = MAX_DIGIT_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;
+}
+
+/** Parse MRCP request-id */
+MRCP_DECLARE(mrcp_request_id) mrcp_request_id_parse(const apt_str_t *field)
+{
+ if(field->buf) {
+#ifdef TOO_LONG_MRCP_REQUEST_ID
+ return apr_atoi64(field->buf);
+#else
+ return atol(field->buf);
+#endif
+ }
+ return 0;
+}
+
+/** Generate MRCP request-id */
+MRCP_DECLARE(apt_bool_t) mrcp_request_id_generate(mrcp_request_id request_id, apt_text_stream_t *stream)
+{
+ int length = sprintf(stream->pos, "%"MRCP_REQUEST_ID_FMT, request_id);
+ if(length <= 0) {
+ return FALSE;
+ }
+ stream->pos += length;
+ return TRUE;
+}
RelativePath=".\message\include\mrcp_message.h"
>
</File>
+ <File
+ RelativePath=".\message\include\mrcp_start_line.h"
+ >
+ </File>
</Filter>
<Filter
Name="src"
RelativePath=".\message\src\mrcp_message.c"
>
</File>
+ <File
+ RelativePath=".\message\src\mrcp_start_line.c"
+ >
+ </File>
</Filter>
</Filter>
<Filter
>
</File>
<File
- RelativePath=".\control\include\mrcp_state_machine.h"
+ RelativePath=".\control\include\mrcp_resource_loader.h"
>
</File>
<File
RelativePath=".\control\src\mrcp_resource_factory.c"
>
</File>
+ <File
+ RelativePath=".\control\src\mrcp_resource_loader.c"
+ >
+ </File>
<File
RelativePath=".\control\src\mrcp_stream.c"
>
Name="include"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
>
- <File
- RelativePath=".\resources\include\mrcp_default_factory.h"
- >
- </File>
<File
RelativePath=".\resources\include\mrcp_recog_header.h"
>
>
</File>
<File
- RelativePath=".\resources\include\mrcp_recog_state_machine.h"
+ RelativePath=".\resources\include\mrcp_recorder_header.h"
>
</File>
<File
- RelativePath=".\resources\include\mrcp_synth_header.h"
+ RelativePath=".\resources\include\mrcp_recorder_resource.h"
>
</File>
<File
- RelativePath=".\resources\include\mrcp_synth_resource.h"
+ RelativePath=".\resources\include\mrcp_synth_header.h"
>
</File>
<File
- RelativePath=".\resources\include\mrcp_synth_state_machine.h"
+ RelativePath=".\resources\include\mrcp_synth_resource.h"
>
</File>
</Filter>
Name="src"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
>
- <File
- RelativePath=".\resources\src\mrcp_default_factory.c"
- >
- </File>
- <File
- RelativePath=".\resources\src\mrcp_recog_client_state_machine.c"
- >
- </File>
<File
RelativePath=".\resources\src\mrcp_recog_header.c"
>
>
</File>
<File
- RelativePath=".\resources\src\mrcp_recog_server_state_machine.c"
+ RelativePath=".\resources\src\mrcp_recorder_header.c"
>
</File>
<File
- RelativePath=".\resources\src\mrcp_synth_client_state_machine.c"
+ RelativePath=".\resources\src\mrcp_recorder_resource.c"
>
</File>
<File
RelativePath=".\resources\src\mrcp_synth_resource.c"
>
</File>
- <File
- RelativePath=".\resources\src\mrcp_synth_server_state_machine.c"
- >
- </File>
</Filter>
</Filter>
</Files>
/** Get recognizer header vtable */
-MRCP_DECLARE(const mrcp_header_vtable_t*) mrcp_recog_header_vtable_get(mrcp_version_e version);
+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);
--- /dev/null
+/*
+ * 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_RECORDER_HEADER_H__
+#define __MRCP_RECORDER_HEADER_H__
+
+/**
+ * @file mrcp_recorder_header.h
+ * @brief MRCP Recorder Header
+ */
+
+#include "mrcp_types.h"
+#include "mrcp_header_accessor.h"
+
+APT_BEGIN_EXTERN_C
+
+/** MRCP recorder header fields */
+typedef enum {
+ RECORDER_HEADER_SENSITIVITY_LEVEL,
+ RECORDER_HEADER_NO_INPUT_TIMEOUT,
+ RECORDER_HEADER_COMPLETION_CAUSE,
+ RECORDER_HEADER_COMPLETION_REASON,
+ RECORDER_HEADER_FAILED_URI,
+ RECORDER_HEADER_FAILED_URI_CAUSE,
+ RECORDER_HEADER_RECORD_URI,
+ RECORDER_HEADER_MEDIA_TYPE,
+ RECORDER_HEADER_MAX_TIME,
+ RECORDER_HEADER_TRIM_LENGTH,
+ RECORDER_HEADER_FINAL_SILENCE,
+ RECORDER_HEADER_CAPTURE_ON_SPEECH,
+ RECORDER_HEADER_VER_BUFFER_UTTERANCE,
+ RECORDER_HEADER_START_INPUT_TIMERS,
+ RECORDER_HEADER_NEW_AUDIO_CHANNEL,
+
+ RECORDER_HEADER_COUNT
+} mrcp_recorder_header_id;
+
+
+/** MRCP recorder completion-cause */
+typedef enum {
+ RECORDER_COMPLETION_CAUSE_SUCCESS_SILENCE = 0,
+ RECORDER_COMPLETION_CAUSE_SUCCESS_MAXTIME = 1,
+ RECORDER_COMPLETION_CAUSE_NO_INPUT_TIMEOUT = 2,
+ RECORDER_COMPLETION_CAUSE_URI_FAILURE = 3,
+ RECORDER_COMPLETION_CAUSE_ERROR = 4,
+
+ RECORDER_COMPLETION_CAUSE_COUNT = 5,
+ RECORDER_COMPLETION_CAUSE_UNKNOWN = RECORDER_COMPLETION_CAUSE_COUNT
+} mrcp_recorder_completion_cause_e;
+
+
+
+/** MRCP recorder-header declaration */
+typedef struct mrcp_recorder_header_t mrcp_recorder_header_t;
+
+/** MRCP recorder-header */
+struct mrcp_recorder_header_t {
+ /** To filter out background noise and not mistake it for speech */
+ float sensitivity_level;
+ /** When recording is started and there is no speech detected for a
+ certain period of time, the recorder can send a RECORD-COMPLETE event */
+ apr_size_t no_input_timeout;
+ /** MUST be part of a RECORD-COMPLETE event coming from the
+ recorder resource to the client */
+ mrcp_recorder_completion_cause_e completion_cause;
+ /** MAY be specified in a RECORD-COMPLETE event coming from
+ the recorder resource to the client */
+ apt_str_t completion_reason;
+ /** When a recorder method needs to post the audio to a URI and access to
+ the URI fails, the server MUST provide the failed URI in this header
+ in the method response */
+ apt_str_t failed_uri;
+ /** When a recorder method needs to post the audio to a URI and access to
+ the URI fails, the server MUST provide the URI specific or protocol
+ specific response code through this header in the method response */
+ apt_str_t failed_uri_cause;
+ /** When a recorder method contains this header the server must capture
+ the audio and store it */
+ apt_str_t record_uri;
+ /** A RECORD method MUST contain this header, which specifies to the
+ server the Media Type of the captured audio or video */
+ apt_str_t media_type;
+ /** When recording is started this specifies the maximum length of the
+ recording in milliseconds, calculated from the time the actual
+ capture and store begins and is not necessarily the time the RECORD
+ method is received */
+ apr_size_t max_time;
+ /** This header MAY be sent on a STOP method and specifies the length of
+ audio to be trimmed from the end of the recording after the stop */
+ apr_size_t trim_length;
+ /** When recorder is started and the actual capture begins, this header
+ specifies the length of silence in the audio that is to be
+ interpreted as the end of the recording*/
+ apr_size_t final_silence;
+ /** f false, the recorder MUST start capturing immediately when started.
+ If true, the recorder MUST wait for the endpointing functionality to
+ detect speech before it starts capturing */
+ apt_bool_t capture_on_speech;
+ /** Tells the server to buffer the utterance associated with this
+ recording request into the verification buffer */
+ apt_bool_t ver_buffer_utterance;
+ /** MAY be sent as part of the RECORD request. A value of false tells the
+ recorder resource to start the operation, but not to start the no-input
+ timer until the client sends a START-INPUT-TIMERS */
+ apt_bool_t start_input_timers;
+ /** MAY be specified in a RECORD 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;
+};
+
+
+/** Get recorder header vtable */
+const mrcp_header_vtable_t* mrcp_recorder_header_vtable_get(mrcp_version_e version);
+
+/** Get recorder completion cause string */
+MRCP_DECLARE(const apt_str_t*) mrcp_recorder_completion_cause_get(
+ mrcp_recorder_completion_cause_e completion_cause,
+ mrcp_version_e version);
+
+APT_END_EXTERN_C
+
+#endif /*__MRCP_RECORDER_HEADER_H__*/
--- /dev/null
+/*
+ * 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_RECORDER_RESOURCE_H__
+#define __MRCP_RECORDER_RESOURCE_H__
+
+/**
+ * @file mrcp_recorder_resource.h
+ * @brief MRCP Recorder Resource
+ */
+
+#include "mrcp_types.h"
+
+APT_BEGIN_EXTERN_C
+
+/** MRCP recorder methods */
+typedef enum {
+ RECORDER_SET_PARAMS,
+ RECORDER_GET_PARAMS,
+ RECORDER_RECORD,
+ RECORDER_STOP,
+ RECORDER_START_INPUT_TIMERS,
+
+ RECORDER_METHOD_COUNT
+} mrcp_recorder_method_id;
+
+/** MRCP recorder events */
+typedef enum {
+ RECORDER_START_OF_INPUT,
+ RECORDER_RECORD_COMPLETE,
+
+ RECORDER_EVENT_COUNT
+} mrcp_recorder_event_id;
+
+/** Create MRCP recorder resource */
+MRCP_DECLARE(mrcp_resource_t*) mrcp_recorder_resource_create(apr_pool_t *pool);
+
+APT_END_EXTERN_C
+
+#endif /*__MRCP_RECORDER_RESOURCE_H__*/
};
/** Get synthesizer header vtable */
-MRCP_DECLARE(const mrcp_header_vtable_t*) mrcp_synth_header_vtable_get(mrcp_version_e version);
+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);
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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->on_dispatch(state_machine,message);
-}
-
-/** Create MRCP recognizer client state machine */
-mrcp_state_machine_t* mrcp_recog_client_state_machine_create(void *obj, 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);
- state_machine->update = recog_state_update;
- return state_machine;
-}
RECOGNIZER_HEADER_COUNT
};
-MRCP_DECLARE(const mrcp_header_vtable_t*) mrcp_recog_header_vtable_get(mrcp_version_e version)
+const mrcp_header_vtable_t* mrcp_recog_header_vtable_get(mrcp_version_e version)
{
if(version == MRCP_VERSION_1) {
return &v1_vtable;
#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[] = {
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;
+ mrcp_resource_t *resource = mrcp_resource_create(pool);
- resource->create_client_state_machine = mrcp_recog_client_state_machine_create;
- resource->create_server_state_machine = mrcp_recog_server_state_machine_create;
+ resource->method_count = RECOGNIZER_METHOD_COUNT;
+ resource->event_count = RECOGNIZER_EVENT_COUNT;
+ resource->get_method_str_table = recog_method_string_table_get;
+ resource->get_event_str_table = recog_event_string_table_get;
+ resource->get_resource_header_vtable = mrcp_recog_header_vtable_get;
return resource;
}
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include "mrcp_recorder_header.h"
+
+/** String table of recorder headers (mrcp_recorder_header_id) */
+static const apt_str_table_item_t recorder_header_string_table[] = {
+ {{"Sensitivity-Level", 17},3},
+ {{"No-Input-Timeout", 16},2},
+ {{"Completion-Cause", 16},16},
+ {{"Completion-Reason", 17},11},
+ {{"Failed-Uri", 10},10},
+ {{"Failed-Uri-Cause", 16},16},
+ {{"Record-Uri", 10},0},
+ {{"Media-Type", 10},2},
+ {{"Max-Time", 8},2},
+ {{"Trim-Length", 11},0},
+ {{"Final-Silence", 13},1},
+ {{"Capture-On-Speech", 17},2},
+ {{"Ver-Buffer-Utterance", 20},0},
+ {{"Start-Input-Timers", 18},1},
+ {{"New-Audio-Channel", 17},2}
+};
+
+/** String table of recorder completion-cause fields (mrcp_recorder_completion_cause_e) */
+static const apt_str_table_item_t completion_cause_string_table[] = {
+ {{"success-silence", 15},8},
+ {{"success-maxtime", 15},8},
+ {{"no-input-timeout", 16},0},
+ {{"uri-failure", 11},0},
+ {{"error", 5},0}
+};
+
+/** Generate MRCP recorder completion-cause */
+static apt_bool_t mrcp_completion_cause_generate(
+ mrcp_recorder_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 recorder header */
+static void mrcp_recorder_header_init(mrcp_recorder_header_t *recorder_header)
+{
+ recorder_header->sensitivity_level = 0.0;
+ recorder_header->no_input_timeout = 0;
+ recorder_header->completion_cause = RECORDER_COMPLETION_CAUSE_COUNT;
+ apt_string_reset(&recorder_header->completion_reason);
+ apt_string_reset(&recorder_header->failed_uri);
+ apt_string_reset(&recorder_header->failed_uri_cause);
+ apt_string_reset(&recorder_header->record_uri);
+ apt_string_reset(&recorder_header->media_type);
+ recorder_header->max_time = 0;
+ recorder_header->trim_length = 0;
+ recorder_header->final_silence = 0;
+ recorder_header->capture_on_speech = FALSE;
+ recorder_header->ver_buffer_utterance = FALSE;
+ recorder_header->start_input_timers = FALSE;
+ recorder_header->new_audio_channel = FALSE;
+}
+
+/** Allocate MRCP recorder header */
+static void* mrcp_recorder_header_allocate(mrcp_header_accessor_t *accessor, apr_pool_t *pool)
+{
+ mrcp_recorder_header_t *recorder_header = apr_palloc(pool,sizeof(mrcp_recorder_header_t));
+ mrcp_recorder_header_init(recorder_header);
+ accessor->data = recorder_header;
+ return accessor->data;
+}
+
+/** Parse MRCP recorder header */
+static apt_bool_t mrcp_recorder_header_parse(mrcp_header_accessor_t *accessor, apr_size_t id, const apt_str_t *value, apr_pool_t *pool)
+{
+ apt_bool_t status = TRUE;
+ mrcp_recorder_header_t *recorder_header = accessor->data;
+ switch(id) {
+ case RECORDER_HEADER_SENSITIVITY_LEVEL:
+ recorder_header->sensitivity_level = apt_float_value_parse(value);
+ break;
+ case RECORDER_HEADER_NO_INPUT_TIMEOUT:
+ recorder_header->no_input_timeout = apt_size_value_parse(value);
+ break;
+ case RECORDER_HEADER_COMPLETION_CAUSE:
+ recorder_header->completion_cause = apt_size_value_parse(value);
+ break;
+ case RECORDER_HEADER_COMPLETION_REASON:
+ apt_string_copy(&recorder_header->completion_reason,value,pool);
+ break;
+ case RECORDER_HEADER_FAILED_URI:
+ apt_string_copy(&recorder_header->failed_uri,value,pool);
+ break;
+ case RECORDER_HEADER_FAILED_URI_CAUSE:
+ apt_string_copy(&recorder_header->failed_uri_cause,value,pool);
+ break;
+ case RECORDER_HEADER_RECORD_URI:
+ apt_string_copy(&recorder_header->record_uri,value,pool);
+ break;
+ case RECORDER_HEADER_MEDIA_TYPE:
+ apt_string_copy(&recorder_header->media_type,value,pool);
+ break;
+ case RECORDER_HEADER_MAX_TIME:
+ recorder_header->max_time = apt_size_value_parse(value);
+ break;
+ case RECORDER_HEADER_TRIM_LENGTH:
+ recorder_header->trim_length = apt_size_value_parse(value);
+ break;
+ case RECORDER_HEADER_FINAL_SILENCE:
+ recorder_header->final_silence = apt_size_value_parse(value);
+ break;
+ case RECORDER_HEADER_CAPTURE_ON_SPEECH:
+ apt_boolean_value_parse(value,&recorder_header->capture_on_speech);
+ break;
+ case RECORDER_HEADER_VER_BUFFER_UTTERANCE:
+ apt_boolean_value_parse(value,&recorder_header->ver_buffer_utterance);
+ break;
+ case RECORDER_HEADER_START_INPUT_TIMERS:
+ apt_boolean_value_parse(value,&recorder_header->start_input_timers);
+ break;
+ case RECORDER_HEADER_NEW_AUDIO_CHANNEL:
+ apt_boolean_value_parse(value,&recorder_header->new_audio_channel);
+ break;
+ default:
+ status = FALSE;
+ }
+ return status;
+}
+
+/** Generate MRCP recorder header */
+static apt_bool_t mrcp_recorder_header_generate(mrcp_header_accessor_t *accessor, apr_size_t id, apt_text_stream_t *value)
+{
+ mrcp_recorder_header_t *recorder_header = accessor->data;
+ switch(id) {
+ case RECORDER_HEADER_SENSITIVITY_LEVEL:
+ apt_float_value_generate(recorder_header->sensitivity_level,value);
+ break;
+ case RECORDER_HEADER_NO_INPUT_TIMEOUT:
+ apt_size_value_generate(recorder_header->no_input_timeout,value);
+ break;
+ case RECORDER_HEADER_COMPLETION_CAUSE:
+ {
+ const apt_str_t *name = apt_string_table_str_get(
+ completion_cause_string_table,
+ RECORDER_COMPLETION_CAUSE_COUNT,
+ recorder_header->completion_cause);
+ mrcp_completion_cause_generate(recorder_header->completion_cause,name,value);
+ break;
+ }
+ case RECORDER_HEADER_COMPLETION_REASON:
+ apt_string_value_generate(&recorder_header->completion_reason,value);
+ break;
+ case RECORDER_HEADER_FAILED_URI:
+ apt_string_value_generate(&recorder_header->failed_uri,value);
+ break;
+ case RECORDER_HEADER_FAILED_URI_CAUSE:
+ apt_string_value_generate(&recorder_header->failed_uri_cause,value);
+ break;
+ case RECORDER_HEADER_RECORD_URI:
+ apt_string_value_generate(&recorder_header->record_uri,value);
+ break;
+ case RECORDER_HEADER_MEDIA_TYPE:
+ apt_string_value_generate(&recorder_header->media_type,value);
+ break;
+ case RECORDER_HEADER_MAX_TIME:
+ apt_size_value_generate(recorder_header->max_time,value);
+ break;
+ case RECORDER_HEADER_TRIM_LENGTH:
+ apt_size_value_generate(recorder_header->trim_length,value);
+ break;
+ case RECORDER_HEADER_FINAL_SILENCE:
+ apt_size_value_generate(recorder_header->final_silence,value);
+ break;
+ case RECORDER_HEADER_CAPTURE_ON_SPEECH:
+ apt_boolean_value_generate(recorder_header->capture_on_speech,value);
+ break;
+ case RECORDER_HEADER_VER_BUFFER_UTTERANCE:
+ apt_boolean_value_generate(recorder_header->ver_buffer_utterance,value);
+ break;
+ case RECORDER_HEADER_START_INPUT_TIMERS:
+ apt_boolean_value_generate(recorder_header->start_input_timers,value);
+ break;
+ case RECORDER_HEADER_NEW_AUDIO_CHANNEL:
+ apt_boolean_value_generate(recorder_header->new_audio_channel,value);
+ break;
+ default:
+ break;
+ }
+ return TRUE;
+}
+
+/** Duplicate MRCP recorder header */
+static apt_bool_t mrcp_recorder_header_duplicate(mrcp_header_accessor_t *accessor, const mrcp_header_accessor_t *src, apr_size_t id, apr_pool_t *pool)
+{
+ mrcp_recorder_header_t *recorder_header = accessor->data;
+ const mrcp_recorder_header_t *src_recorder_header = src->data;
+ apt_bool_t status = TRUE;
+
+ if(!recorder_header || !src_recorder_header) {
+ return FALSE;
+ }
+
+ switch(id) {
+ case RECORDER_HEADER_SENSITIVITY_LEVEL:
+ recorder_header->sensitivity_level = src_recorder_header->sensitivity_level;
+ break;
+ case RECORDER_HEADER_NO_INPUT_TIMEOUT:
+ recorder_header->no_input_timeout = src_recorder_header->no_input_timeout;
+ break;
+ case RECORDER_HEADER_COMPLETION_CAUSE:
+ recorder_header->completion_cause = src_recorder_header->completion_cause;
+ break;
+ case RECORDER_HEADER_COMPLETION_REASON:
+ apt_string_copy(&recorder_header->completion_reason,&src_recorder_header->completion_reason,pool);
+ break;
+ case RECORDER_HEADER_FAILED_URI:
+ apt_string_copy(&recorder_header->failed_uri,&src_recorder_header->failed_uri,pool);
+ break;
+ case RECORDER_HEADER_FAILED_URI_CAUSE:
+ apt_string_copy(&recorder_header->failed_uri_cause,&src_recorder_header->failed_uri_cause,pool);
+ break;
+ case RECORDER_HEADER_RECORD_URI:
+ apt_string_copy(&recorder_header->record_uri,&src_recorder_header->record_uri,pool);
+ break;
+ case RECORDER_HEADER_MEDIA_TYPE:
+ apt_string_copy(&recorder_header->media_type,&src_recorder_header->media_type,pool);
+ break;
+ case RECORDER_HEADER_MAX_TIME:
+ recorder_header->max_time = src_recorder_header->max_time;
+ break;
+ case RECORDER_HEADER_TRIM_LENGTH:
+ recorder_header->trim_length = src_recorder_header->trim_length;
+ break;
+ case RECORDER_HEADER_FINAL_SILENCE:
+ recorder_header->final_silence = src_recorder_header->final_silence;
+ break;
+ case RECORDER_HEADER_CAPTURE_ON_SPEECH:
+ recorder_header->capture_on_speech = src_recorder_header->capture_on_speech;
+ break;
+ case RECORDER_HEADER_VER_BUFFER_UTTERANCE:
+ recorder_header->ver_buffer_utterance = src_recorder_header->ver_buffer_utterance;
+ break;
+ case RECORDER_HEADER_START_INPUT_TIMERS:
+ recorder_header->start_input_timers = src_recorder_header->start_input_timers;
+ break;
+ case RECORDER_HEADER_NEW_AUDIO_CHANNEL:
+ recorder_header->new_audio_channel = src_recorder_header->new_audio_channel;
+ break;
+ default:
+ status = FALSE;
+ }
+ return status;
+}
+
+static const mrcp_header_vtable_t vtable = {
+ mrcp_recorder_header_allocate,
+ NULL, /* nothing to destroy */
+ mrcp_recorder_header_parse,
+ mrcp_recorder_header_generate,
+ mrcp_recorder_header_duplicate,
+ recorder_header_string_table,
+ RECORDER_HEADER_COUNT
+};
+
+const mrcp_header_vtable_t* mrcp_recorder_header_vtable_get(mrcp_version_e version)
+{
+ return &vtable;
+}
+
+MRCP_DECLARE(const apt_str_t*) mrcp_recorder_completion_cause_get(
+ mrcp_recorder_completion_cause_e completion_cause,
+ mrcp_version_e version)
+{
+ return apt_string_table_str_get(completion_cause_string_table,RECORDER_COMPLETION_CAUSE_COUNT,completion_cause);
+}
--- /dev/null
+/*
+ * 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_recorder_resource.h"
+#include "mrcp_recorder_header.h"
+#include "mrcp_resource.h"
+
+/** String table of MRCP recorder methods (mrcp_recorder_method_id) */
+static const apt_str_table_item_t recorder_method_string_table[] = {
+ {{"SET-PARAMS", 10},10},
+ {{"GET-PARAMS", 10},0},
+ {{"RECORD", 6},0},
+ {{"STOP", 4},2},
+ {{"START-INPUT-TIMERS", 18},2}
+};
+
+/** String table of MRCP recorder events (mrcp_recorder_event_id) */
+static const apt_str_table_item_t recorder_event_string_table[] = {
+ {{"START-OF-INPUT", 14},0},
+ {{"RECORD-COMPLETE", 15},0}
+};
+
+static APR_INLINE const apt_str_table_item_t* recorder_method_string_table_get(mrcp_version_e version)
+{
+ return recorder_method_string_table;
+}
+
+static APR_INLINE const apt_str_table_item_t* recorder_event_string_table_get(mrcp_version_e version)
+{
+ return recorder_event_string_table;
+}
+
+/** Create MRCP recorder resource */
+MRCP_DECLARE(mrcp_resource_t*) mrcp_recorder_resource_create(apr_pool_t *pool)
+{
+ mrcp_resource_t *resource = mrcp_resource_create(pool);
+
+ resource->method_count = RECORDER_METHOD_COUNT;
+ resource->event_count = RECORDER_EVENT_COUNT;
+ resource->get_method_str_table = recorder_method_string_table_get;
+ resource->get_event_str_table = recorder_event_string_table_get;
+ resource->get_resource_header_vtable = mrcp_recorder_header_vtable_get;
+ return resource;
+}
+++ /dev/null
-/*
- * 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->on_dispatch(state_machine,message);
-}
-
-/** Create MRCP synthesizer client state machine */
-mrcp_state_machine_t* mrcp_synth_client_state_machine_create(void *obj, 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);
- state_machine->update = synth_state_update;
- return state_machine;
-}
SYNTHESIZER_HEADER_COUNT
};
-MRCP_DECLARE(const mrcp_header_vtable_t*) mrcp_synth_header_vtable_get(mrcp_version_e version)
+const mrcp_header_vtable_t* mrcp_synth_header_vtable_get(mrcp_version_e version)
{
return &vtable;
}
#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[] = {
{{"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)
+static APR_INLINE const apt_str_table_item_t* synth_method_string_table_get(mrcp_version_e version)
{
- /* 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;
+ return synth_method_string_table;
}
-/** Set resource specifica data */
-static apt_bool_t synth_message_resourcify_by_name(mrcp_resource_t *resource, mrcp_message_t *message)
+static APR_INLINE const apt_str_table_item_t* synth_event_string_table_get(mrcp_version_e version)
{
- /* 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;
+ return synth_event_string_table;
}
-
/** 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;
+ mrcp_resource_t *resource = mrcp_resource_create(pool);
- resource->create_client_state_machine = mrcp_synth_client_state_machine_create;
- resource->create_server_state_machine = mrcp_synth_server_state_machine_create;
+ resource->method_count = SYNTHESIZER_METHOD_COUNT;
+ resource->event_count = SYNTHESIZER_EVENT_COUNT;
+ resource->get_method_str_table = synth_method_string_table_get;
+ resource->get_event_str_table = synth_event_string_table_get;
+ resource->get_resource_header_vtable = mrcp_synth_header_vtable_get;
return resource;
}
};
/** Create MRCP connection. */
-mrcp_connection_t* mrcp_connection_create();
+mrcp_connection_t* mrcp_connection_create(void);
/** Destroy MRCP connection. */
void mrcp_connection_destroy(mrcp_connection_t *connection);
/** Remove Control Channel from MRCP connection. */
apt_bool_t mrcp_connection_channel_remove(mrcp_connection_t *connection, mrcp_control_channel_t *channel);
+/** Raise disconnect event for each channel from the specified connection. */
+apt_bool_t mrcp_connection_disconnect_raise(mrcp_connection_t *connection, const mrcp_connection_event_vtable_t *vtable);
APT_END_EXTERN_C
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);
+ /** Disconnect event handler */
+ apt_bool_t (*on_disconnect)(mrcp_control_channel_t *channel);
};
/** MRCPv2 control channel */
* @brief MRCPv2 Control Descriptor
*/
+#include <apr_tables.h>
#include "apt_string.h"
#include "mrcp_connection_types.h"
APT_BEGIN_EXTERN_C
+/** TCP discard port used in offer/answer */
+#define TCP_DISCARD_PORT 9
+
+
/** MRCPv2 proto transport */
typedef enum {
MRCP_PROTO_TCP,
apt_str_t resource_name;
/** Session identifier */
apt_str_t session_id;
- /** Control media identifier */
- apr_size_t cmid;
+ /** Array of cmid attributes */
+ apr_array_header_t *cmid_arr;
/** 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 descriptor */
+MRCP_DECLARE(mrcp_control_descriptor_t*) mrcp_control_descriptor_create(apr_pool_t *pool);
/** 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);
+/** Add cmid to cmid_arr */
+MRCP_DECLARE(void) mrcp_cmid_add(apr_array_header_t *cmid_arr, apr_size_t cmid);
+
+/** Find cmid in cmid_arr */
+MRCP_DECLARE(apt_bool_t) mrcp_cmid_find(apr_array_header_t *cmid_arr, apr_size_t cmid);
+
/** Get MRCP protocol transport name by identifier */
MRCP_DECLARE(const apt_str_t*) mrcp_proto_get(mrcp_proto_type_e proto);
vtable->terminate = mrcp_client_agent_task_terminate;
vtable->destroy = mrcp_client_agent_task_on_destroy;
}
+ apt_task_auto_ready_set(agent->task,FALSE);
agent->connection_list = apt_list_create(pool);
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) {
+ if(apr_sockaddr_equal(sockaddr,connection->r_sockaddr) != 0 &&
+ descriptor->port == connection->r_sockaddr->port) {
return connection;
}
}
apr_socket_close(connection->sock);
connection->sock = NULL;
-// agent->vtable->on_disconnect(agent,connection);
+ mrcp_connection_disconnect_raise(connection,agent->vtable);
return TRUE;
}
/* calculate actual length of the stream */
return FALSE;
}
+ /* explicitly indicate task is ready to process messages */
+ apt_task_ready(agent->task);
+
while(running) {
status = apt_pollset_poll(agent->pollset, -1, &num, &ret_pfd);
if(status != APR_SUCCESS) {
connection->access_count--;
return TRUE;
}
+
+apt_bool_t mrcp_connection_disconnect_raise(mrcp_connection_t *connection, const mrcp_connection_event_vtable_t *vtable)
+{
+ if(vtable && vtable->on_disconnect) {
+ mrcp_control_channel_t *channel;
+ void *val;
+ apr_hash_index_t *it = apr_hash_first(connection->pool,connection->channel_table);
+ /* walk through the list of channels and raise disconnect event for them */
+ for(; it; it = apr_hash_next(it)) {
+ apr_hash_this(it,NULL,NULL,&val);
+ channel = val;
+ if(channel) {
+ vtable->on_disconnect(channel);
+ }
+ }
+ }
+ return TRUE;
+}
return apt_string_table_id_find(mrcp_connection_value_table,MRCP_CONNECTION_TYPE_COUNT,attrib);
}
+/** Create MRCP control descriptor */
+MRCP_DECLARE(mrcp_control_descriptor_t*) mrcp_control_descriptor_create(apr_pool_t *pool)
+{
+ mrcp_control_descriptor_t *descriptor;
+ descriptor = apr_palloc(pool,sizeof(mrcp_control_descriptor_t));
+
+ 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_arr = apr_array_make(pool,1,sizeof(apr_size_t));
+ descriptor->id = 0;
+ return descriptor;
+}
+
/** 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);
+ mrcp_control_descriptor_t *offer = mrcp_control_descriptor_create(pool);
offer->proto = MRCP_PROTO_TCP;
- offer->port = 9;
+ offer->port = TCP_DISCARD_PORT;
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);
+ mrcp_control_descriptor_t *answer = mrcp_control_descriptor_create(pool);
if(offer) {
*answer = *offer;
+ answer->cmid_arr = apr_array_copy(pool,offer->cmid_arr);
}
answer->setup_type = MRCP_SETUP_TYPE_PASSIVE;
return answer;
}
+
+/** Add cmid to cmid_arr */
+MRCP_DECLARE(void) mrcp_cmid_add(apr_array_header_t *cmid_arr, apr_size_t cmid)
+{
+ APR_ARRAY_PUSH(cmid_arr, apr_size_t) = cmid;
+}
+
+/** Find cmid in cmid_arr */
+MRCP_DECLARE(apt_bool_t) mrcp_cmid_find(apr_array_header_t *cmid_arr, apr_size_t cmid)
+{
+ int i;
+ for(i=0; i<cmid_arr->nelts; i++) {
+ if(APR_ARRAY_IDX(cmid_arr,i,apr_size_t) == cmid) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
vtable->terminate = mrcp_server_agent_task_terminate;
vtable->destroy = mrcp_server_agent_task_on_destroy;
}
+ apt_task_auto_ready_set(agent->task,FALSE);
agent->msg_queue = apt_cyclic_queue_create(CYCLIC_QUEUE_DEFAULT_SIZE);
apr_thread_mutex_create(&agent->guard,APR_THREAD_MUTEX_UNNESTED,pool);
}
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");
+ if(message->resource) {
+ mrcp_message_t *response;
+ 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;
return FALSE;
}
+ /* explicitly indicate task is ready to process messages */
+ apt_task_ready(agent->task);
+
while(running) {
status = apt_pollset_poll(agent->pollset, -1, &num, &ret_pfd);
if(status != APR_SUCCESS) {
RTSP_TRANSPORT_ATTRIB_DESTINATION,
RTSP_TRANSPORT_ATTRIB_UNICAST,
RTSP_TRANSPORT_ATTRIB_MULTICAST,
+ RTSP_TRANSPORT_ATTRIB_MODE,
RTSP_TRANSPORT_ATTRIB_COUNT,
RTSP_TRANSPORT_ATTRIB_NONE = RTSP_TRANSPORT_ATTRIB_COUNT
apt_str_t source;
/** Destination IP address */
apt_str_t destination;
+ /** Mode indicates the method to support (either PLAY or RECORD) */
+ apt_str_t mode;
};
/** RTSP header */
rtsp_port_range_init(&transport->server_port_range);
apt_string_reset(&transport->source);
apt_string_reset(&transport->destination);
+ apt_string_reset(&transport->mode);
}
/** Initialize header */
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->connection) {
+ 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);
+ }
}
}
/** 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},
+ {{"server_port", 11},2},
+ {{"source", 6},2},
{{"destination", 11},0},
- {{"unicast", 7}, 0},
- {{"multicast", 9}, 0}
+ {{"unicast", 7},0},
+ {{"multicast", 9},1},
+ {{"mode", 4},2}
};
/** Parse RTSP transport port range */
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)
+/** Parse text value of RTSP transport attrib (source/destination, e.t.c) */
+static apt_bool_t rtsp_transport_attrib_value_parse(apt_str_t *value, 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) {
+ apt_str_t field;
+ /* read value */
+ if(apt_text_field_read(stream,';',TRUE,&field) == FALSE) {
return FALSE;
}
- apt_string_copy(address,&value,pool);
+ apt_string_copy(value,&field,pool);
return TRUE;
}
rtsp_port_range_parse(&transport->client_port_range,&stream);
break;
case RTSP_TRANSPORT_ATTRIB_SOURCE:
- rtsp_address_parse(&transport->source,&stream,pool);
+ rtsp_transport_attrib_value_parse(&transport->source,&stream,pool);
break;
case RTSP_TRANSPORT_ATTRIB_DESTINATION:
- rtsp_address_parse(&transport->destination,&stream,pool);
+ rtsp_transport_attrib_value_parse(&transport->destination,&stream,pool);
break;
case RTSP_TRANSPORT_ATTRIB_UNICAST:
transport->delivery = RTSP_DELIVERY_UNICAST;
case RTSP_TRANSPORT_ATTRIB_MULTICAST:
transport->delivery = RTSP_DELIVERY_MULTICAST;
break;
+ case RTSP_TRANSPORT_ATTRIB_MODE:
+ rtsp_transport_attrib_value_parse(&transport->mode,&stream,pool);
+ break;
default:
break;
}
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);
apt_text_char_insert(text_stream,';');
rtsp_port_range_generate(RTSP_TRANSPORT_ATTRIB_SERVER_PORT,&transport->server_port_range,text_stream);
}
+
+ if(transport->mode.length) {
+ const apt_str_t *str;
+ str = apt_string_table_str_get(rtsp_transport_attrib_string_table,RTSP_TRANSPORT_ATTRIB_COUNT,RTSP_TRANSPORT_ATTRIB_MODE);
+ if(str) {
+ apt_text_char_insert(text_stream,';');
+ apt_string_value_generate(str,text_stream);
+ apt_text_char_insert(text_stream,'=');
+ apt_string_value_generate(&transport->mode,text_stream);
+ }
+ }
return TRUE;
}
{
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);
+ rtsp_message_t *response = rtsp_message_create(RTSP_MESSAGE_TYPE_RESPONSE,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);
+ apt_string_copy(&status_line->reason,reason_str,pool);
}
if(rtsp_header_property_check(&request->header.property_set,RTSP_HEADER_FIELD_CSEQ) == TRUE) {
rtsp_header_property_add(&response->header.property_set,RTSP_HEADER_FIELD_CSEQ);
}
+ if(rtsp_header_property_check(&request->header.property_set,RTSP_HEADER_FIELD_TRANSPORT) == TRUE) {
+ const rtsp_transport_t *req_transport = &request->header.transport;
+ rtsp_transport_t *res_transport = &response->header.transport;
+ if(req_transport->mode.length) {
+ apt_string_copy(&res_transport->mode,&req_transport->mode,pool);
+ rtsp_header_property_add(&response->header.property_set,RTSP_HEADER_FIELD_TRANSPORT);
+ }
+ }
+
return response;
}
/** Session identifier */
apt_str_t id;
- apt_str_t url;
/** Last cseq sent */
apr_size_t last_cseq;
/** request queue */
apt_obj_list_t *request_queue;
+ /** Resource table */
+ apr_hash_t *resource_table;
+
/** In-progress termination request */
apt_bool_t terminating;
};
session->last_cseq = 0;
session->active_request = NULL;
session->request_queue = apt_list_create(pool);
+ session->resource_table = apr_hash_make(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) {
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;
+ /* remove resource */
+ const char *resource_name = message->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) {
+ rtsp_server_session_terminate_request(server,session);
+ return TRUE;
+ }
}
if(server->vtable->handle_message(server,session,message) != TRUE) {
return TRUE;
}
-/* Process incoming SETUP request */
+/* Process incoming SETUP/DESCRIBE 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;
/* 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);
}
message->header.session_id.buf,
message->header.session_id.length);
if(!session) {
- /* error case */
+ /* error case, no such session */
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->terminating == TRUE) {
+ /* error case, session is being terminated */
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Not Acceptable Request "APT_SID_FMT,message->header.session_id.buf);
+ return rtsp_server_error_respond(server,rtsp_connection,message,
+ RTSP_STATUS_CODE_NOT_ACCEPTABLE,
+ RTSP_REASON_PHRASE_NOT_ACCEPTABLE);
+ }
+
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);
/* 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);
- }
-
+ apt_bool_t terminate = FALSE;
+ rtsp_message_t *request = NULL;
if(message->start_line.message_type == RTSP_MESSAGE_TYPE_REQUEST) {
/* RTSP ANNOUNCE request (asynch event) */
- message->start_line.common.request_line.url = session->url;
+ const char *resource_name = message->start_line.common.request_line.resource_name;
+ if(resource_name) {
+ request = apr_hash_get(session->resource_table,resource_name,APR_HASH_KEY_STRING);
+ }
+ if(!request) {
+ return FALSE;
+ }
+ message->start_line.common.request_line.url = request->start_line.common.request_line.url;
message->header.cseq = session->last_cseq;
rtsp_header_property_add(&message->header.property_set,RTSP_HEADER_FIELD_CSEQ);
+ if(session->id.buf) {
+ message->header.session_id = session->id;
+ rtsp_header_property_add(&message->header.property_set,RTSP_HEADER_FIELD_SESSION_ID);
+ }
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) {
+ /* unexpected response */
+ return FALSE;
+ }
- if(session->active_request) {
- rtsp_message_t *request = session->active_request;
+ request = session->active_request;
+ if(request->start_line.common.request_line.method_id == RTSP_METHOD_DESCRIBE) {
+ terminate = TRUE;
+ }
+ else {
+ if(session->id.buf) {
+ message->header.session_id = session->id;
+ rtsp_header_property_add(&message->header.property_set,RTSP_HEADER_FIELD_SESSION_ID);
+ }
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);
+ if(message->start_line.common.status_line.status_code == RTSP_STATUS_CODE_OK) {
+ /* add resource */
+ const char *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(apr_hash_count(session->resource_table) == 0) {
+ terminate = TRUE;
}
}
- else if(request->start_line.common.request_line.method_id == RTSP_METHOD_DESCRIBE) {
- rtsp_server_session_terminate_request(server,session);
- }
+ }
+
+ session->last_cseq = message->header.cseq;
+ rtsp_server_message_send(server,session->connection->base,message);
+
+ if(terminate == TRUE) {
+ session->active_request = NULL;
+ rtsp_server_session_terminate_request(server,session);
+ return TRUE;
}
session->active_request = apt_list_pop_front(session->request_queue);
count = mrcp_session_media_count_get(descriptor);
for(i=0; i<count; i++) {
audio_media = mrcp_session_audio_media_get(descriptor,audio_index);
- if(audio_media && audio_media->base.id == i) {
+ if(audio_media && audio_media->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) {
+ if(video_media && video_media->id == i) {
/* generate video media */
video_index++;
offset += sdp_rtp_media_generate(buffer+offset,size-offset,descriptor,video_media);
{
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);
+ media->id = mrcp_session_audio_media_add(descriptor,media);
mpf_rtp_media_generate(media,sdp_media,&descriptor->ip,pool);
break;
}
{
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);
+ media->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);
+ mrcp_control_descriptor_t *control_media = mrcp_control_descriptor_create(pool);
control_media->id = mrcp_session_control_media_add(descriptor,control_media);
mrcp_control_media_generate(control_media,sdp_media,&descriptor->ip,pool);
break;
}
offset += snprintf(buffer+offset,size-offset,
"m=audio %d RTP/AVP",
- audio_media->base.state == MPF_MEDIA_ENABLED ? audio_media->base.port : 0);
+ audio_media->state == MPF_MEDIA_ENABLED ? audio_media->port : 0);
for(i=0; i<descriptor_arr->nelts; i++) {
- codec_descriptor = (mpf_codec_descriptor_t*)descriptor_arr->elts + i;
+ codec_descriptor = &APR_ARRAY_IDX(descriptor_arr,i,mpf_codec_descriptor_t);
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;
+ if(descriptor->ip.length && audio_media->ip.length &&
+ apt_string_compare(&descriptor->ip,&audio_media->ip) != TRUE) {
+ const char *media_ip = audio_media->ext_ip.buf ? audio_media->ext_ip.buf : audio_media->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);
+ if(audio_media->state == MPF_MEDIA_ENABLED) {
+ const apt_str_t *direction_str = mpf_rtp_direction_str_get(audio_media->direction);
for(i=0; i<descriptor_arr->nelts; i++) {
- codec_descriptor = (mpf_codec_descriptor_t*)descriptor_arr->elts + i;
+ codec_descriptor = &APR_ARRAY_IDX(descriptor_arr,i,mpf_codec_descriptor_t);
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);
+ if(codec_descriptor->format.buf) {
+ offset += snprintf(buffer+offset,size-offset,"a=fmtp:%d %s\r\n",
+ codec_descriptor->payload_type,
+ codec_descriptor->format.buf);
+ }
}
}
- offset += snprintf(buffer+offset,size-offset,"a=%s\r\n",mode_str ? mode_str->buf : "");
+ if(direction_str) {
+ offset += snprintf(buffer+offset,size-offset,"a=%s\r\n",direction_str->buf);
+ }
if(audio_media->ptime) {
offset += snprintf(buffer+offset,size-offset,"a=ptime:%hu\r\n",audio_media->ptime);
/** 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)
{
+ int i;
apr_size_t offset = 0;
const apt_str_t *proto;
const apt_str_t *setup_type;
"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",
+ "a=resource:%s\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);
+ control_media->resource_name.buf);
+
}
else {
offset += snprintf(buffer+offset,size-offset,
"m=application %d %s 1\r\n"
- "a=resource:%s\r\n"
- "a=cmid:%d\r\n",
+ "a=resource:%s\r\n",
control_media->port,
proto ? proto->buf : "",
- control_media->resource_name.buf,
- control_media->cmid);
+ control_media->resource_name.buf);
}
}
else { /* answer */
"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",
+ "a=channel:%s@%s\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);
+ control_media->resource_name.buf);
}
else {
offset += sprintf(buffer+offset,
"m=application %d %s 1\r\n"
- "a=channel:%s@%s\r\n"
- "a=cmid:%d\r\n",
+ "a=channel:%s@%s\r\n",
control_media->port,
proto ? proto->buf : "",
control_media->session_id.buf,
- control_media->resource_name.buf,
- control_media->cmid);
+ control_media->resource_name.buf);
}
}
+ for(i=0; i<control_media->cmid_arr->nelts; i++) {
+ offset += snprintf(buffer+offset,size-offset,
+ "a=cmid:%d\r\n",
+ APR_ARRAY_IDX(control_media->cmid_arr,i,apr_size_t));
+
+ }
+
return offset;
}
switch(sdp_media->m_mode) {
case sdp_inactive:
- rtp_media->mode = STREAM_MODE_NONE;
+ rtp_media->direction = STREAM_DIRECTION_NONE;
break;
case sdp_sendonly:
- rtp_media->mode = STREAM_MODE_SEND;
+ rtp_media->direction = STREAM_DIRECTION_SEND;
break;
case sdp_recvonly:
- rtp_media->mode = STREAM_MODE_RECEIVE;
+ rtp_media->direction = STREAM_DIRECTION_RECEIVE;
break;
case sdp_sendrecv:
- rtp_media->mode = STREAM_MODE_SEND_RECEIVE;
+ rtp_media->direction = STREAM_DIRECTION_DUPLEX;
break;
}
if(sdp_media->m_connections) {
- apt_string_assign(&rtp_media->base.ip,sdp_media->m_connections->c_address,pool);
+ apt_string_assign(&rtp_media->ip,sdp_media->m_connections->c_address,pool);
}
else {
- rtp_media->base.ip = *ip;
+ rtp_media->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;
+ rtp_media->port = (apr_port_t)sdp_media->m_port;
+ rtp_media->state = MPF_MEDIA_ENABLED;
}
else {
- rtp_media->base.state = MPF_MEDIA_DISABLED;
+ rtp_media->state = MPF_MEDIA_DISABLED;
}
return TRUE;
}
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);
+ mrcp_cmid_add(control_media->cmid_arr,atoi(attrib->a_value));
break;
default:
break;
"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"
+ "m=audio 0 RTP/AVP 0 8 96 101\r\n"
"a=rtpmap:0 PCMU/8000\r\n"
- "a=rtpmap:8 PCMA/8000\r\n",
+ "a=rtpmap:8 PCMA/8000\r\n"
+ "a=rtpmap:96 L16/8000\r\n"
+ "a=rtpmap:101 telephone-event/8000\r\n",
origin,
ip,
ip);
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);
+ /* if status < 200, shutdown still in progress */
+ if(status >= 200) {
+ /* break main loop of sofia thread */
+ su_root_break(sofia_agent->root);
+ }
break;
default:
break;
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),
+ SOATAG_AUDIO_AUX("telephone-event"),
+ NUTAG_AUTOANSWER(0),
TAG_END());
return TRUE;
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)),
+ SOATAG_AUDIO_AUX("telephone-event"),
TAG_END());
}
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);
+ /* if status < 200, shutdown still in progress */
+ if(status >= 200) {
+ /* break main loop of sofia thread */
+ su_root_break(sofia_agent->root);
+ }
break;
- default:
+ default:
break;
}
}
const apr_table_t *resource_map,
apr_pool_t *pool);
-/** Generate resource descovery descriptor by RTSP response */
+/** Generate resource discovery 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,
}
offset += snprintf(buffer+offset,size-offset,
"m=audio %d RTP/AVP",
- audio_media->base.state == MPF_MEDIA_ENABLED ? audio_media->base.port : 0);
+ audio_media->state == MPF_MEDIA_ENABLED ? audio_media->port : 0);
for(i=0; i<descriptor_arr->nelts; i++) {
- codec_descriptor = (mpf_codec_descriptor_t*)descriptor_arr->elts + i;
+ codec_descriptor = &APR_ARRAY_IDX(descriptor_arr,i,mpf_codec_descriptor_t);
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);
+ if(audio_media->state == MPF_MEDIA_ENABLED) {
+ const apt_str_t *direction_str = mpf_rtp_direction_str_get(audio_media->direction);
for(i=0; i<descriptor_arr->nelts; i++) {
- codec_descriptor = (mpf_codec_descriptor_t*)descriptor_arr->elts + i;
+ codec_descriptor = &APR_ARRAY_IDX(descriptor_arr,i,mpf_codec_descriptor_t);
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);
+ if(codec_descriptor->format.buf) {
+ offset += snprintf(buffer+offset,size-offset,"a=fmtp:%d %s\r\n",
+ codec_descriptor->payload_type,
+ codec_descriptor->format.buf);
+ }
}
}
- offset += snprintf(buffer+offset,size-offset,"a=%s\r\n",mode_str ? mode_str->buf : "");
+ if(direction_str) {
+ offset += snprintf(buffer+offset,size-offset,"a=%s\r\n",direction_str->buf);
+ }
if(audio_media->ptime) {
offset += snprintf(buffer+offset,size-offset,"a=ptime:%hu\r\n",
switch(sdp_media->m_mode) {
case sdp_inactive:
- rtp_media->mode = STREAM_MODE_NONE;
+ rtp_media->direction = STREAM_DIRECTION_NONE;
break;
case sdp_sendonly:
- rtp_media->mode = STREAM_MODE_SEND;
+ rtp_media->direction = STREAM_DIRECTION_SEND;
break;
case sdp_recvonly:
- rtp_media->mode = STREAM_MODE_RECEIVE;
+ rtp_media->direction = STREAM_DIRECTION_RECEIVE;
break;
case sdp_sendrecv:
- rtp_media->mode = STREAM_MODE_SEND_RECEIVE;
+ rtp_media->direction = STREAM_DIRECTION_DUPLEX;
break;
}
if(sdp_media->m_connections) {
- apt_string_assign(&rtp_media->base.ip,sdp_media->m_connections->c_address,pool);
+ apt_string_assign(&rtp_media->ip,sdp_media->m_connections->c_address,pool);
}
else {
- rtp_media->base.ip = *ip;
+ rtp_media->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;
+ rtp_media->port = (apr_port_t)sdp_media->m_port;
+ rtp_media->state = MPF_MEDIA_ENABLED;
}
else {
- rtp_media->base.state = MPF_MEDIA_DISABLED;
+ rtp_media->state = MPF_MEDIA_DISABLED;
}
return TRUE;
}
{
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);
+ media->id = mrcp_session_audio_media_add(descriptor,media);
mpf_rtp_media_generate(media,sdp_media,&descriptor->ip,pool);
break;
}
{
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);
+ media->id = mrcp_session_video_media_add(descriptor,media);
mpf_rtp_media_generate(media,sdp_media,&descriptor->ip,pool);
break;
}
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);
+ media->state = MPF_MEDIA_ENABLED;
+ media->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;
+ media->port = request->header.transport.client_port_range.min;
+ media->ip = request->header.transport.destination;
}
}
count = mrcp_session_media_count_get(descriptor);
for(i=0; i<count; i++) {
audio_media = mrcp_session_audio_media_get(descriptor,audio_index);
- if(audio_media && audio_media->base.id == i) {
+ if(audio_media && audio_media->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;
+ request->header.transport.client_port_range.min = audio_media->port;
+ request->header.transport.client_port_range.max = audio_media->port+1;
continue;
}
video_media = mrcp_session_video_media_get(descriptor,video_index);
- if(video_media && video_media->base.id == i) {
+ if(video_media && video_media->id == i) {
/* generate video media */
video_index++;
offset += sdp_rtp_media_generate(buffer+offset,size-offset,descriptor,video_media);
count = mrcp_session_media_count_get(descriptor);
for(i=0; i<count; i++) {
audio_media = mrcp_session_audio_media_get(descriptor,audio_index);
- if(audio_media && audio_media->base.id == i) {
+ if(audio_media && audio_media->id == i) {
/* generate audio media */
+ rtsp_transport_t *transport;
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;
+ transport = &response->header.transport;
+ transport->server_port_range.min = audio_media->port;
+ transport->server_port_range.max = audio_media->port+1;
+ 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) {
+ if(video_media && video_media->id == i) {
/* generate video media */
video_index++;
offset += sdp_rtp_media_generate(buffer+offset,size-offset,descriptor,video_media);
return request;
}
-/** Generate resource descovery descriptor by RTSP response */
+/** Generate resource discovery 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,
"s=-\r\n"
"c=IN IP4 %s\r\n"
"t=0 0\r\n"
- "m=audio 0 RTP/AVP 0 8\r\n"
+ "m=audio 0 RTP/AVP 0 8 96 101\r\n"
"a=rtpmap:0 PCMU/8000\r\n"
- "a=rtpmap:8 PCMA/8000\r\n",
+ "a=rtpmap:8 PCMA/8000\r\n"
+ "a=rtpmap:96 L16/8000\r\n"
+ "a=rtpmap:101 telephone-event/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;
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.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;
}
-#define uni_version "0.6.0"
+#define uni_version "0.8.0"
AppName=UniMRCP
AppVerName=UniMRCP-{#= uni_version}
MAINTAINERCLEANFILES = Makefile.in
SUBDIRS = libunimrcp-server unimrcp-server \
- libunimrcp-client unimrcp-client
+ libunimrcp-client unimrcp-client \
+ libasr-client asr-client \
+ umc
--- /dev/null
+MAINTAINERCLEANFILES = Makefile.in
+
+INCLUDES = -Iinclude \
+ -I$(top_srcdir)/platforms/libasr-client/include \
+ -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 = asrclient
+asrclient_SOURCES = src/main.c
+
+asrclient_LDADD = $(top_builddir)/platforms/libasr-client/libasrclient.la
+
+if ISMAC
+asrclient_LDFLAGS = -framework CoreFoundation -framework SystemConfiguration
+endif
--- /dev/null
+<?xml version="1.0" encoding="windows-1251"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="asrclient"
+ ProjectGUID="{6B83AC6D-01CE-4E1C-81CE-02AD8116C684}"
+ RootNamespace="asrclient"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(ProjectDir)..\..\build\vsprops\unidebug.vsprops;$(ProjectDir)..\..\build\vsprops\unimrcpclient.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="include;"..\libasr-client\include""
+ RuntimeLibrary="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="libasrclient.lib libapr-1.lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(ProjectDir)..\..\build\vsprops\unirelease.vsprops;$(ProjectDir)..\..\build\vsprops\unimrcpclient.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="include;"..\libasr-client\include""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="libasrclient.lib libapr-1.lib"
+ LinkTimeCodeGeneration="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="src"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\src\main.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="include"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <apr_getopt.h>
+#include <apr_file_info.h>
+#include <apr_thread_proc.h>
+#include "asr_engine.h"
+
+typedef struct {
+ const char *root_dir_path;
+ apt_log_priority_e log_priority;
+ apt_log_output_e log_output;
+ apr_pool_t *pool;
+} client_options_t;
+
+typedef struct {
+ asr_engine_t *engine;
+ const char *grammar_file;
+ const char *input_file;
+ const char *profile;
+
+ apr_thread_t *thread;
+ apr_pool_t *pool;
+} asr_params_t;
+
+/** Thread function to run ASR scenario in */
+static void* APR_THREAD_FUNC asr_session_run(apr_thread_t *thread, void *data)
+{
+ asr_params_t *params = data;
+ asr_session_t *session = asr_session_create(params->engine,params->profile);
+ if(session) {
+ const char *result = asr_session_recognize(session,params->grammar_file,params->input_file);
+ if(result) {
+ printf("Recog Result [%s]",result);
+ }
+
+ asr_session_destroy(session);
+ }
+
+ /* destroy pool params allocated from */
+ apr_pool_destroy(params->pool);
+ return NULL;
+}
+
+/** Launch demo ASR session */
+static apt_bool_t asr_session_launch(asr_engine_t *engine, const char *grammar_file, const char *input_file, const char *profile)
+{
+ apr_pool_t *pool;
+ asr_params_t *params;
+
+ /* create pool to allocate params from */
+ apr_pool_create(&pool,NULL);
+ params = apr_palloc(pool,sizeof(asr_params_t));
+ params->pool = pool;
+ params->engine = engine;
+
+ if(grammar_file) {
+ params->grammar_file = apr_pstrdup(pool,grammar_file);
+ }
+ else {
+ params->grammar_file = "grammar.xml";
+ }
+
+ if(input_file) {
+ params->input_file = apr_pstrdup(pool,input_file);
+ }
+ else {
+ params->input_file = "one-8kHz.pcm";
+ }
+
+ if(profile) {
+ params->profile = apr_pstrdup(pool,profile);
+ }
+ else {
+ params->profile = "MRCPv2-Default";
+ }
+
+ /* Launch a thread to run demo ASR session in */
+ if(apr_thread_create(¶ms->thread,NULL,asr_session_run,params,pool) != APR_SUCCESS) {
+ apr_pool_destroy(pool);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static apt_bool_t cmdline_process(asr_engine_t *engine, char *cmdline)
+{
+ apt_bool_t running = TRUE;
+ char *name;
+ char *last;
+ name = apr_strtok(cmdline, " ", &last);
+
+ if(strcasecmp(name,"run") == 0) {
+ char *grammar = apr_strtok(NULL, " ", &last);
+ char *input = apr_strtok(NULL, " ", &last);
+ char *profile = apr_strtok(NULL, " ", &last);
+ asr_session_launch(engine,grammar,input,profile);
+ }
+ else if(strcasecmp(name,"loglevel") == 0) {
+ char *priority = apr_strtok(NULL, " ", &last);
+ if(priority) {
+ asr_engine_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 [grammar_file] [audio_input_file] [profile_name] (run demo asr client)\n"
+ " grammar_file is the name of grammar file, (path is relative to data dir)\n"
+ " audio_input_file is the name of audio file, (path is relative to data dir)\n"
+ " profile_name is one of 'MRCPv2-Default', 'MRCPv1-Default', ...\n"
+ "\n examples: \n"
+ " run\n"
+ " run grammar.xml one.pcm\n"
+ " run grammar.xml one.pcm 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 cmdline_run(asr_engine_t *engine)
+{
+ 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 = cmdline_process(engine,cmdline);
+ }
+ }
+ while(running != 0);
+ return TRUE;
+}
+
+static void usage()
+{
+ printf(
+ "\n"
+ "Usage:\n"
+ "\n"
+ " asrclient [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 void options_destroy(client_options_t *options)
+{
+ if(options->pool) {
+ apr_pool_destroy(options->pool);
+ }
+}
+
+static client_options_t* options_load(int argc, const char * const *argv)
+{
+ apr_status_t rv;
+ apr_getopt_t *opt = NULL;
+ int optch;
+ const char *optarg;
+ apr_pool_t *pool;
+ client_options_t *options;
+
+ 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 */
+ };
+
+ /* create APR pool to allocate options from */
+ apr_pool_create(&pool,NULL);
+ if(!pool) {
+ return NULL;
+ }
+ options = apr_palloc(pool,sizeof(client_options_t));
+ options->pool = pool;
+ /* set the default options */
+ options->root_dir_path = "../";
+ options->log_priority = APT_PRIO_INFO;
+ options->log_output = APT_LOG_OUTPUT_CONSOLE;
+
+
+ rv = apr_getopt_init(&opt, pool , argc, argv);
+ if(rv != APR_SUCCESS) {
+ options_destroy(options);
+ return NULL;
+ }
+
+ 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();
+ options_destroy(options);
+ return NULL;
+ }
+
+ return options;
+}
+
+int main(int argc, const char * const *argv)
+{
+ client_options_t *options;
+ asr_engine_t *engine;
+
+ /* APR global initialization */
+ if(apr_initialize() != APR_SUCCESS) {
+ apr_terminate();
+ return 0;
+ }
+
+ /* load options */
+ options = options_load(argc,argv);
+ if(!options) {
+ apr_terminate();
+ return 0;
+ }
+
+ /* create asr engine */
+ engine = asr_engine_create(
+ options->root_dir_path,
+ options->log_priority,
+ options->log_output);
+ if(engine) {
+ /* run command line */
+ cmdline_run(engine);
+ /* destroy demo framework */
+ asr_engine_destroy(engine);
+ }
+
+ /* destroy options */
+ options_destroy(options);
+
+ /* APR global termination */
+ apr_terminate();
+ return 0;
+}
--- /dev/null
+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)
+
+lib_LTLIBRARIES = libasrclient.la
+
+include_HEADERS = include/asr_engine.h
+
+libasrclient_la_SOURCES = src/asr_engine.c
+
+libasrclient_la_LIBADD = $(top_builddir)/platforms/libunimrcp-client/libunimrcpclient.la
+
+libasrclient_la_LDFLAGS = $(UNI_LT_VERSION)
--- /dev/null
+/*
+ * Copyright 2009 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 __ASR_ENGINE_H__
+#define __ASR_ENGINE_H__
+
+/**
+ * @file asr_engine.h
+ * @brief Basic ASR engine on top of UniMRCP client library
+ */
+
+#include "apt_log.h"
+
+/** Lib export/import defines (win32) */
+#ifdef WIN32
+#ifdef ASR_CLIENT_STATIC_LIB
+#define ASR_CLIENT_DECLARE(type) type __stdcall
+#else
+#ifdef ASR_CLIENT_LIB_EXPORT
+#define ASR_CLIENT_DECLARE(type) __declspec(dllexport) type __stdcall
+#else
+#define ASR_CLIENT_DECLARE(type) __declspec(dllimport) type __stdcall
+#endif
+#endif
+#else
+#define ASR_CLIENT_DECLARE(type) type
+#endif
+
+APT_BEGIN_EXTERN_C
+
+/** Opaque ASR engine */
+typedef struct asr_engine_t asr_engine_t;
+
+/** Opaque ASR session */
+typedef struct asr_session_t asr_session_t;
+
+
+/**
+ * Create ASR engine.
+ * @param root_dir_path the path to root directory
+ * @param log_priority the log priority level
+ * @param log_output the log output mode
+ */
+ASR_CLIENT_DECLARE(asr_engine_t*) asr_engine_create(
+ const char *root_dir_path,
+ apt_log_priority_e log_priority,
+ apt_log_output_e log_output);
+
+/**
+ * Destroy ASR engine.
+ * @param engine the engine to destroy
+ */
+ASR_CLIENT_DECLARE(apt_bool_t) asr_engine_destroy(asr_engine_t *engine);
+
+
+
+/**
+ * Create ASR session.
+ * @param engine the engine session belongs to
+ * @param profile the name of UniMRCP profile to use
+ */
+ASR_CLIENT_DECLARE(asr_session_t*) asr_session_create(asr_engine_t *engine, const char *profile);
+
+/**
+ * Initiate recognition.
+ * @param session the session to run recognition in the scope of
+ * @param grammar_file the name of the grammar file to use (path is relative to data dir)
+ * @param input_file the name of the audio input file to use (path is relative to data dir)
+ * @return the recognition result (input element of NLSML content)
+ */
+ASR_CLIENT_DECLARE(const char*) asr_session_recognize(asr_session_t *session, const char *grammar_file, const char *input_file);
+
+/**
+ * Destroy ASR session.
+ * @param session the session to destroy
+ */
+ASR_CLIENT_DECLARE(apt_bool_t) asr_session_destroy(asr_session_t *session);
+
+
+/**
+ * Set log priority.
+ * @param priority the priority to set
+ */
+ASR_CLIENT_DECLARE(apt_bool_t) asr_engine_log_priority_set(apt_log_priority_e log_priority);
+
+
+APT_END_EXTERN_C
+
+#endif /*__ASR_ENGINE_H__*/
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+ ProjectType="Visual C++"\r
+ Version="8.00"\r
+ Name="libasrclient"\r
+ ProjectGUID="{272FAFA8-2B2F-4716-B95F-3B37CF2E0CB3}"\r
+ RootNamespace="libasrclient"\r
+ Keyword="Win32Proj"\r
+ >\r
+ <Platforms>\r
+ <Platform\r
+ Name="Win32"\r
+ />\r
+ </Platforms>\r
+ <ToolFiles>\r
+ </ToolFiles>\r
+ <Configurations>\r
+ <Configuration\r
+ Name="Debug|Win32"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(ProjectDir)..\..\build\vsprops\unidebug.vsprops;$(ProjectDir)..\..\build\vsprops\unimrcpclient.vsprops"\r
+ CharacterSet="1"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories="include"\r
+ PreprocessorDefinitions="ASR_CLIENT_LIB_EXPORT"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalDependencies="$(UniMRCPClientLibs)"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ <Configuration\r
+ Name="Release|Win32"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(ProjectDir)..\..\build\vsprops\unirelease.vsprops;$(ProjectDir)..\..\build\vsprops\unimrcpclient.vsprops"\r
+ CharacterSet="1"\r
+ WholeProgramOptimization="1"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories="include"\r
+ PreprocessorDefinitions="ASR_CLIENT_LIB_EXPORT"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalDependencies="$(UniMRCPClientLibs)"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ </Configurations>\r
+ <References>\r
+ </References>\r
+ <Files>\r
+ <Filter\r
+ Name="include"\r
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+ >\r
+ <File\r
+ RelativePath=".\include\asr_engine.h"\r
+ >\r
+ </File>\r
+ </Filter>\r
+ <Filter\r
+ Name="src"\r
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+ >\r
+ <File\r
+ RelativePath=".\src\asr_engine.c"\r
+ >\r
+ </File>\r
+ </Filter>\r
+ </Files>\r
+ <Globals>\r
+ </Globals>\r
+</VisualStudioProject>\r
--- /dev/null
+/*
+ * Copyright 2009 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 <stdlib.h>
+
+/* APR includes */
+#include <apr_thread_cond.h>
+#include <apr_thread_proc.h>
+
+/* common includes */
+#include "unimrcp_client.h"
+#include "mrcp_application.h"
+#include "mrcp_message.h"
+#include "mrcp_generic_header.h"
+/* recognizer includes */
+#include "mrcp_recog_header.h"
+#include "mrcp_recog_resource.h"
+/* APT includes */
+#include "apt_nlsml_doc.h"
+#include "apt_log.h"
+#include "apt_pool.h"
+
+#include "asr_engine.h"
+
+
+/** ASR engine on top of UniMRCP client stack */
+struct asr_engine_t {
+ /** MRCP client stack */
+ mrcp_client_t *mrcp_client;
+ /** MRCP client stack */
+ mrcp_application_t *mrcp_app;
+ /** Memory pool */
+ apr_pool_t *pool;
+};
+
+
+/** ASR session on top of UniMRCP session/channel */
+struct asr_session_t {
+ /** Back pointer to engine */
+ asr_engine_t *engine;
+ /** MRCP session */
+ mrcp_session_t *mrcp_session;
+ /** MRCP channel */
+ mrcp_channel_t *mrcp_channel;
+ /** RECOGNITION-COMPLETE message */
+ mrcp_message_t *recog_complete;
+
+ /** File to read audio stream from */
+ FILE *audio_in;
+ /** Streaming is in-progress */
+ apt_bool_t streaming;
+
+ /** Conditional wait object */
+ apr_thread_cond_t *wait_object;
+ /** Mutex of the wait object */
+ apr_thread_mutex_t *mutex;
+
+ /** Message sent from client stack */
+ const mrcp_app_message_t *app_message;
+};
+
+
+/** Declaration of recognizer audio stream methods */
+static apt_bool_t asr_stream_read(mpf_audio_stream_t *stream, mpf_frame_t *frame);
+
+static const mpf_audio_stream_vtable_t audio_stream_vtable = {
+ NULL,
+ NULL,
+ NULL,
+ asr_stream_read,
+ NULL,
+ NULL,
+ NULL
+};
+
+static apt_bool_t app_message_handler(const mrcp_app_message_t *app_message);
+
+
+/** Create ASR engine */
+ASR_CLIENT_DECLARE(asr_engine_t*) asr_engine_create(
+ const char *root_dir_path,
+ apt_log_priority_e log_priority,
+ apt_log_output_e log_output)
+{
+ apr_pool_t *pool = NULL;
+ apt_dir_layout_t *dir_layout;
+ asr_engine_t *engine;
+ mrcp_client_t *mrcp_client;
+ mrcp_application_t *mrcp_app;
+
+ /* create APR pool */
+ pool = apt_pool_create();
+ if(!pool) {
+ return NULL;
+ }
+
+ /* create the structure of default directories layout */
+ dir_layout = apt_default_dir_layout_create(root_dir_path,pool);
+ /* create singleton logger */
+ apt_log_instance_create(log_output,log_priority,pool);
+
+ if((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);
+ }
+
+ engine = apr_palloc(pool,sizeof(asr_engine_t));
+ engine->pool = pool;
+ engine->mrcp_client = NULL;
+ engine->mrcp_app = NULL;
+
+ /* create UniMRCP client stack */
+ mrcp_client = unimrcp_client_create(dir_layout);
+ if(!mrcp_client) {
+ apt_log_instance_destroy();
+ apr_pool_destroy(pool);
+ return NULL;
+ }
+
+ /* create an application */
+ mrcp_app = mrcp_application_create(
+ app_message_handler,
+ engine,
+ pool);
+ if(!mrcp_app) {
+ mrcp_client_destroy(mrcp_client);
+ apt_log_instance_destroy();
+ apr_pool_destroy(pool);
+ return NULL;
+ }
+
+ /* register application in client stack */
+ mrcp_client_application_register(mrcp_client,mrcp_app,"ASRAPP");
+
+ /* start client stack */
+ if(mrcp_client_start(mrcp_client) != TRUE) {
+ mrcp_client_destroy(mrcp_client);
+ apt_log_instance_destroy();
+ apr_pool_destroy(pool);
+ return NULL;
+ }
+
+ engine->mrcp_client = mrcp_client;
+ engine->mrcp_app = mrcp_app;
+ return engine;
+}
+
+/** Destroy ASR engine */
+ASR_CLIENT_DECLARE(apt_bool_t) asr_engine_destroy(asr_engine_t *engine)
+{
+ if(engine->mrcp_client) {
+ /* shutdown client stack */
+ mrcp_client_shutdown(engine->mrcp_client);
+ /* destroy client stack */
+ mrcp_client_destroy(engine->mrcp_client);
+ engine->mrcp_client = NULL;
+ engine->mrcp_app = NULL;
+ }
+
+ /* destroy singleton logger */
+ apt_log_instance_destroy();
+ /* destroy APR pool */
+ apr_pool_destroy(engine->pool);
+ return TRUE;
+}
+
+
+
+/** Destroy ASR session */
+static apt_bool_t asr_session_destroy_ex(asr_session_t *asr_session, apt_bool_t terminate)
+{
+ if(terminate == TRUE) {
+ apr_thread_mutex_lock(asr_session->mutex);
+ if(mrcp_application_session_terminate(asr_session->mrcp_session) == TRUE) {
+ apr_thread_cond_wait(asr_session->wait_object,asr_session->mutex);
+ /* the response must be checked to be the valid one */
+ }
+ apr_thread_mutex_unlock(asr_session->mutex);
+ }
+
+ if(asr_session->audio_in) {
+ fclose(asr_session->audio_in);
+ asr_session->audio_in = NULL;
+ }
+
+ if(asr_session->mutex) {
+ apr_thread_mutex_destroy(asr_session->mutex);
+ asr_session->mutex = NULL;
+ }
+ if(asr_session->wait_object) {
+ apr_thread_cond_destroy(asr_session->wait_object);
+ asr_session->wait_object = NULL;
+ }
+ if(asr_session->mrcp_session) {
+ mrcp_application_session_destroy(asr_session->mrcp_session);
+ asr_session->mrcp_session = NULL;
+ }
+ free(asr_session);
+ return TRUE;
+}
+
+/** Open audio input file */
+static apt_bool_t asr_input_file_open(asr_session_t *asr_session, const char *input_file)
+{
+ const apt_dir_layout_t *dir_layout = mrcp_application_dir_layout_get(asr_session->engine->mrcp_app);
+ apr_pool_t *pool = mrcp_application_session_pool_get(asr_session->mrcp_session);
+ char *input_file_path = apt_datadir_filepath_get(dir_layout,input_file,pool);
+ if(!input_file_path) {
+ return FALSE;
+ }
+
+ if(asr_session->audio_in) {
+ fclose(asr_session->audio_in);
+ asr_session->audio_in = NULL;
+ }
+
+ asr_session->audio_in = fopen(input_file_path,"rb");
+ if(!asr_session->audio_in) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot Open [%s]",input_file_path);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/** MPF callback to read audio frame */
+static apt_bool_t asr_stream_read(mpf_audio_stream_t *stream, mpf_frame_t *frame)
+{
+ asr_session_t *asr_session = stream->obj;
+ if(asr_session && asr_session->streaming == TRUE) {
+ if(asr_session->audio_in) {
+ if(fread(frame->codec_frame.buffer,1,frame->codec_frame.size,asr_session->audio_in) == frame->codec_frame.size) {
+ /* normal read */
+ frame->type |= MEDIA_FRAME_TYPE_AUDIO;
+ }
+ else {
+ /* file is over */
+ asr_session->streaming = FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+/** Create DEFINE-GRAMMAR request */
+static mrcp_message_t* define_grammar_message_create(asr_session_t *asr_session, const char *grammar_file)
+{
+ /* create MRCP message */
+ mrcp_message_t *mrcp_message = mrcp_application_message_create(
+ asr_session->mrcp_session,
+ asr_session->mrcp_channel,
+ RECOGNIZER_DEFINE_GRAMMAR);
+ if(mrcp_message) {
+ mrcp_generic_header_t *generic_header;
+
+ /* set message body */
+ const apt_dir_layout_t *dir_layout = mrcp_application_dir_layout_get(asr_session->engine->mrcp_app);
+ apr_pool_t *pool = mrcp_application_session_pool_get(asr_session->mrcp_session);
+ char *grammar_file_path = apt_datadir_filepath_get(dir_layout,grammar_file,pool);
+ if(grammar_file_path) {
+ char text[1024];
+ apr_size_t size;
+ FILE *grammar = fopen(grammar_file_path,"r");
+ if(!grammar) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot Open [%s]",grammar_file_path);
+ return NULL;
+ }
+
+ size = fread(text,1,sizeof(text),grammar);
+ apt_string_assign_n(&mrcp_message->body,text,size,mrcp_message->pool);
+ fclose(grammar);
+ }
+
+ /* 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,"demo-grammar",mrcp_message->pool);
+ mrcp_generic_header_property_add(mrcp_message,GENERIC_HEADER_CONTENT_ID);
+ }
+ }
+ return mrcp_message;
+}
+
+/** Create RECOGNIZE request */
+static mrcp_message_t* recognize_message_create(asr_session_t *asr_session)
+{
+ /* create MRCP message */
+ mrcp_message_t *mrcp_message = mrcp_application_message_create(
+ asr_session->mrcp_session,
+ asr_session->mrcp_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);
+ /* set message body */
+ apt_string_assign(&mrcp_message->body,"session:demo-grammar",mrcp_message->pool);
+ }
+ /* 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);
+ recog_header->confidence_threshold = 0.87f;
+ mrcp_resource_header_property_add(mrcp_message,RECOGNIZER_HEADER_CONFIDENCE_THRESHOLD);
+ }
+ }
+ return mrcp_message;
+}
+
+/** Get NLSML input result */
+static const char* nlsml_input_get(mrcp_message_t *message)
+{
+ apr_xml_elem *interpret;
+ apr_xml_elem *instance;
+ apr_xml_elem *input;
+ apr_xml_doc *doc = nlsml_doc_load(&message->body,message->pool);
+ if(!doc) {
+ return NULL;
+ }
+
+ /* get interpreted result */
+ interpret = nlsml_first_interpret_get(doc);
+ if(!interpret) {
+ return NULL;
+ }
+ /* get instance and input */
+ nlsml_interpret_results_get(interpret,&instance,&input);
+ if(!input || !input->first_cdata.first) {
+ return NULL;
+ }
+
+ /* return input */
+ return input->first_cdata.first->text;
+}
+
+
+/** Application message handler */
+static apt_bool_t app_message_handler(const mrcp_app_message_t *app_message)
+{
+ if((app_message->message_type == MRCP_APP_MESSAGE_TYPE_SIGNALING &&
+ app_message->sig_message.message_type == MRCP_SIG_MESSAGE_TYPE_RESPONSE) ||
+ app_message->message_type == MRCP_APP_MESSAGE_TYPE_CONTROL) {
+
+ asr_session_t *asr_session = mrcp_application_session_object_get(app_message->session);
+ if(asr_session) {
+ apr_thread_mutex_lock(asr_session->mutex);
+ asr_session->app_message = app_message;
+ apr_thread_cond_signal(asr_session->wait_object);
+ apr_thread_mutex_unlock(asr_session->mutex);
+ }
+ }
+ return TRUE;
+}
+
+/** Check signaling response */
+static apt_bool_t sig_response_check(const mrcp_app_message_t *app_message)
+{
+ if(!app_message || app_message->message_type != MRCP_APP_MESSAGE_TYPE_SIGNALING) {
+ return FALSE;
+ }
+
+ return (app_message->sig_message.status == MRCP_SIG_STATUS_CODE_SUCCESS) ? TRUE : FALSE;
+}
+
+/** Check MRCP response */
+static apt_bool_t mrcp_response_check(const mrcp_app_message_t *app_message, mrcp_request_state_e state)
+{
+ mrcp_message_t *mrcp_message = NULL;
+ if(app_message && app_message->message_type == MRCP_APP_MESSAGE_TYPE_CONTROL) {
+ mrcp_message = app_message->control_message;
+ }
+
+ if(!mrcp_message || mrcp_message->start_line.message_type != MRCP_MESSAGE_TYPE_RESPONSE ) {
+ return FALSE;
+ }
+ return (mrcp_message->start_line.request_state == state) ? TRUE : FALSE;
+}
+
+/** Get MRCP event */
+static mrcp_message_t* mrcp_event_get(const mrcp_app_message_t *app_message)
+{
+ mrcp_message_t *mrcp_message = NULL;
+ if(app_message && app_message->message_type == MRCP_APP_MESSAGE_TYPE_CONTROL) {
+ mrcp_message = app_message->control_message;
+ }
+
+ if(!mrcp_message || mrcp_message->start_line.message_type != MRCP_MESSAGE_TYPE_EVENT) {
+ return NULL;
+ }
+ return mrcp_message;
+}
+
+/** Create ASR session */
+ASR_CLIENT_DECLARE(asr_session_t*) asr_session_create(asr_engine_t *engine, const char *profile)
+{
+ mpf_termination_t *termination;
+ mrcp_channel_t *channel;
+ mrcp_session_t *session;
+ const mrcp_app_message_t *app_message;
+ apr_pool_t *pool;
+
+ asr_session_t *asr_session = malloc(sizeof(asr_session_t));
+
+ /* create session */
+ session = mrcp_application_session_create(engine->mrcp_app,profile,asr_session);
+ if(!session) {
+ free(asr_session);
+ return NULL;
+ }
+ pool = mrcp_application_session_pool_get(session);
+
+ termination = mrcp_application_source_termination_create(
+ session, /* session, termination belongs to */
+ &audio_stream_vtable, /* virtual methods table of audio stream */
+ NULL, /* codec descriptor of audio stream (NULL by default) */
+ asr_session); /* 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) */
+ asr_session); /* object to associate */
+
+ if(!channel) {
+ mrcp_application_session_destroy(session);
+ free(asr_session);
+ return NULL;
+ }
+
+ asr_session->engine = engine;
+ asr_session->mrcp_session = session;
+ asr_session->mrcp_channel = channel;
+ asr_session->recog_complete = NULL;
+ asr_session->streaming = FALSE;
+ asr_session->audio_in = NULL;
+ asr_session->mutex = NULL;
+ asr_session->wait_object = NULL;
+ asr_session->app_message = NULL;
+
+ /* Create cond wait object and mutex */
+ apr_thread_mutex_create(&asr_session->mutex,APR_THREAD_MUTEX_DEFAULT,pool);
+ apr_thread_cond_create(&asr_session->wait_object,pool);
+
+ /* Send add channel request and wait for the response */
+ apr_thread_mutex_lock(asr_session->mutex);
+ app_message = NULL;
+ if(mrcp_application_channel_add(asr_session->mrcp_session,asr_session->mrcp_channel) == TRUE) {
+ apr_thread_cond_wait(asr_session->wait_object,asr_session->mutex);
+ app_message = asr_session->app_message;
+ asr_session->app_message = NULL;
+ }
+ apr_thread_mutex_unlock(asr_session->mutex);
+
+ if(sig_response_check(app_message) == FALSE) {
+ asr_session_destroy_ex(asr_session,TRUE);
+ return NULL;
+ }
+ return asr_session;
+}
+
+/** Initiate recognition */
+ASR_CLIENT_DECLARE(const char*) asr_session_recognize(asr_session_t *asr_session, const char *grammar_file, const char *input_file)
+{
+ const mrcp_app_message_t *app_message;
+ mrcp_message_t *mrcp_message;
+
+ app_message = NULL;
+ mrcp_message = define_grammar_message_create(asr_session,grammar_file);
+ if(!mrcp_message) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create DEFINE-GRAMMAR Request");
+ return NULL;
+ }
+
+ /* Send DEFINE-GRAMMAR request and wait for the response */
+ apr_thread_mutex_lock(asr_session->mutex);
+ if(mrcp_application_message_send(asr_session->mrcp_session,asr_session->mrcp_channel,mrcp_message) == TRUE) {
+ apr_thread_cond_wait(asr_session->wait_object,asr_session->mutex);
+ app_message = asr_session->app_message;
+ asr_session->app_message = NULL;
+ }
+ apr_thread_mutex_unlock(asr_session->mutex);
+
+ if(mrcp_response_check(app_message,MRCP_REQUEST_STATE_COMPLETE) == FALSE) {
+ return NULL;
+ }
+
+ /* Reset prev recog result (if any) */
+ asr_session->recog_complete = NULL;
+
+ app_message = NULL;
+ mrcp_message = recognize_message_create(asr_session);
+ if(!mrcp_message) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create RECOGNIZE Request");
+ return NULL;
+ }
+
+ /* Send RECOGNIZE request and wait for the response */
+ apr_thread_mutex_lock(asr_session->mutex);
+ if(mrcp_application_message_send(asr_session->mrcp_session,asr_session->mrcp_channel,mrcp_message) == TRUE) {
+ apr_thread_cond_wait(asr_session->wait_object,asr_session->mutex);
+ app_message = asr_session->app_message;
+ asr_session->app_message = NULL;
+ }
+ apr_thread_mutex_unlock(asr_session->mutex);
+
+ if(mrcp_response_check(app_message,MRCP_REQUEST_STATE_INPROGRESS) == FALSE) {
+ return NULL;
+ }
+
+ /* Open input file and start streaming */
+ if(asr_input_file_open(asr_session,input_file) == FALSE) {
+ return NULL;
+ }
+ asr_session->streaming = TRUE;
+
+ /* Wait for events either START-OF-INPUT or RECOGNITION-COMPLETE */
+ do {
+ apr_thread_mutex_lock(asr_session->mutex);
+ app_message = NULL;
+ if(apr_thread_cond_timedwait(asr_session->wait_object,asr_session->mutex, 60 * 1000000) != APR_SUCCESS) {
+ apr_thread_mutex_unlock(asr_session->mutex);
+ return NULL;
+ }
+ app_message = asr_session->app_message;
+ asr_session->app_message = NULL;
+ apr_thread_mutex_unlock(asr_session->mutex);
+
+ mrcp_message = mrcp_event_get(app_message);
+ if(mrcp_message && mrcp_message->start_line.method_id == RECOGNIZER_RECOGNITION_COMPLETE) {
+ asr_session->recog_complete = mrcp_message;
+ }
+ }
+ while(!asr_session->recog_complete);
+
+ /* Get results */
+ return nlsml_input_get(asr_session->recog_complete);
+}
+
+/** Destroy ASR session */
+ASR_CLIENT_DECLARE(apt_bool_t) asr_session_destroy(asr_session_t *asr_session)
+{
+ return asr_session_destroy_ex(asr_session,TRUE);
+}
+
+/** Set log priority */
+ASR_CLIENT_DECLARE(apt_bool_t) asr_engine_log_priority_set(apt_log_priority_e log_priority)
+{
+ return apt_log_priority_set(log_priority);
+}
$(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)
+ $(UNIMRCP_APR_LIBS) $(UNIMRCP_APU_LIBS) $(UNIMRCP_SOFIA_LIBS) -lm
libunimrcpclient_la_LDFLAGS = $(UNI_LT_VERSION)
#include <apr_xml.h>
#include "unimrcp_client.h"
#include "uni_version.h"
-#include "mrcp_default_factory.h"
+#include "mrcp_resource_loader.h"
#include "mpf_engine.h"
#include "mpf_codec_manager.h"
#include "mpf_rtp_termination_factory.h"
{
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;
}
- 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);
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;
+ doc = NULL;
}
apr_file_close(fd);
else if(strcasecmp(attr_name->value,"ptime") == 0) {
rtp_config->ptime = (apr_uint16_t)atol(attr_value->value);
}
+ else if(strcasecmp(attr_name->value,"rtcp") == 0) {
+ rtp_config->rtcp = atoi(attr_value->value);
+ }
+ else if(strcasecmp(attr_name->value,"rtcp-bye") == 0) {
+ rtp_config->rtcp_bye_policy = atoi(attr_value->value);
+ }
+ else if(strcasecmp(attr_name->value,"rtcp-tx-interval") == 0) {
+ rtp_config->rtcp_tx_interval = (apr_uint16_t)atoi(attr_value->value);
+ }
+ else if(strcasecmp(attr_name->value,"rtcp-rx-resolution") == 0) {
+ rtp_config->rtcp_rx_resolution = (apr_uint16_t)atol(attr_value->value);
+ }
else {
apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr_name->value);
}
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;
+
+ /* create codec manager first */
+ mpf_codec_manager_t *codec_manager = mpf_engine_codec_manager_create(pool);
+ if(codec_manager) {
+ mrcp_client_codec_manager_register(client,codec_manager);
+ }
+
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;
+ unsigned long rate = 1;
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,"rate") == 0) {
+ rate = atol(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);
+ media_engine = mpf_engine_create(rate,pool);
if(media_engine) {
mrcp_client_media_engine_register(client,media_engine,name);
}
return TRUE;
}
+/** Load resource */
+static apt_bool_t unimrcp_client_resource_load(mrcp_client_t *client, mrcp_resource_loader_t *resource_loader, const apr_xml_elem *root, apr_pool_t *pool)
+{
+ apt_str_t resource_class;
+ apt_bool_t resource_enabled = TRUE;
+ const apr_xml_attr *attr;
+ apt_string_reset(&resource_class);
+ for(attr = root->attr; attr; attr = attr->next) {
+ if(strcasecmp(attr->name,"class") == 0) {
+ apt_string_set(&resource_class,attr->value);
+ }
+ else if(strcasecmp(attr->name,"enable") == 0) {
+ resource_enabled = atoi(attr->value);
+ }
+ else {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr->name);
+ }
+ }
+
+ if(!resource_class.buf || !resource_enabled) {
+ return FALSE;
+ }
+
+ return mrcp_resource_load(resource_loader,&resource_class);
+}
+
+/** Load resources */
+static apt_bool_t unimrcp_client_resources_load(mrcp_client_t *client, const apr_xml_elem *root, apr_pool_t *pool)
+{
+ const apr_xml_elem *elem;
+ mrcp_resource_factory_t *resource_factory;
+ mrcp_resource_loader_t *resource_loader = mrcp_resource_loader_create(FALSE,pool);
+ if(!resource_loader) {
+ return FALSE;
+ }
+
+ apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Resources");
+ for(elem = root->first_child; elem; elem = elem->next) {
+ if(strcasecmp(elem->name,"resource") == 0) {
+ unimrcp_client_resource_load(client,resource_loader,elem,pool);
+ }
+ else {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Element <%s>",elem->name);
+ }
+ }
+
+ resource_factory = mrcp_resource_factory_get(resource_loader);
+ mrcp_client_resource_factory_register(client,resource_factory);
+ 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)
{
return FALSE;
}
for(elem = root->first_child; elem; elem = elem->next) {
- if(strcasecmp(elem->name,"settings") == 0) {
+ if(strcasecmp(elem->name,"resources") == 0) {
+ unimrcp_client_resources_load(client,elem,pool);
+ }
+ else if(strcasecmp(elem->name,"settings") == 0) {
unimrcp_client_settings_load(client,elem,pool);
}
else if(strcasecmp(elem->name,"profiles") == 0) {
$(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)
+ $(UNIMRCP_APR_LIBS) $(UNIMRCP_APU_LIBS) $(UNIMRCP_SOFIA_LIBS) -lm
libunimrcpserver_la_LDFLAGS = $(UNI_LT_VERSION)
#include <apr_xml.h>
#include "unimrcp_server.h"
#include "uni_version.h"
-#include "mrcp_default_factory.h"
+#include "mrcp_resource_loader.h"
#include "mpf_engine.h"
#include "mpf_codec_manager.h"
#include "mpf_rtp_termination_factory.h"
{
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;
}
- 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);
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;
+ doc = NULL;
}
apr_file_close(fd);
else if(strcasecmp(attr_name->value,"own-preference") == 0) {
rtp_config->own_preferrence = atoi(attr_value->value);
}
+ else if(strcasecmp(attr_name->value,"rtcp") == 0) {
+ rtp_config->rtcp = atoi(attr_value->value);
+ }
+ else if(strcasecmp(attr_name->value,"rtcp-bye") == 0) {
+ rtp_config->rtcp_bye_policy = atoi(attr_value->value);
+ }
+ else if(strcasecmp(attr_name->value,"rtcp-tx-interval") == 0) {
+ rtp_config->rtcp_tx_interval = (apr_uint16_t)atoi(attr_value->value);
+ }
+ else if(strcasecmp(attr_name->value,"rtcp-rx-resolution") == 0) {
+ rtp_config->rtcp_rx_resolution = (apr_uint16_t)atol(attr_value->value);
+ }
else {
apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr_name->value);
}
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;
+
+ /* create codec manager first */
+ mpf_codec_manager_t *codec_manager = mpf_engine_codec_manager_create(pool);
+ if(codec_manager) {
+ mrcp_server_codec_manager_register(server,codec_manager);
+ }
+
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;
+ unsigned long rate = 1;
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,"rate") == 0) {
+ rate = atol(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);
+ media_engine = mpf_engine_create(rate,pool);
if(media_engine) {
mrcp_server_media_engine_register(server,media_engine,name);
}
return TRUE;
}
+/** Load resource */
+static apt_bool_t unimrcp_server_resource_load(mrcp_server_t *server, mrcp_resource_loader_t *resource_loader, const apr_xml_elem *root, apr_pool_t *pool)
+{
+ apt_str_t resource_class;
+ apt_bool_t resource_enabled = TRUE;
+ const apr_xml_attr *attr;
+ apt_string_reset(&resource_class);
+ for(attr = root->attr; attr; attr = attr->next) {
+ if(strcasecmp(attr->name,"class") == 0) {
+ apt_string_set(&resource_class,attr->value);
+ }
+ else if(strcasecmp(attr->name,"enable") == 0) {
+ resource_enabled = atoi(attr->value);
+ }
+ else {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr->name);
+ }
+ }
+
+ if(!resource_class.buf || !resource_enabled) {
+ return FALSE;
+ }
+
+ return mrcp_resource_load(resource_loader,&resource_class);
+}
+
+/** Load resources */
+static apt_bool_t unimrcp_server_resources_load(mrcp_server_t *server, const apr_xml_elem *root, apr_pool_t *pool)
+{
+ const apr_xml_elem *elem;
+ mrcp_resource_factory_t *resource_factory;
+ mrcp_resource_loader_t *resource_loader = mrcp_resource_loader_create(FALSE,pool);
+ if(!resource_loader) {
+ return FALSE;
+ }
+
+ apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Resources");
+ for(elem = root->first_child; elem; elem = elem->next) {
+ if(strcasecmp(elem->name,"resource") == 0) {
+ unimrcp_server_resource_load(server,resource_loader,elem,pool);
+ }
+ else {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Element <%s>",elem->name);
+ }
+ }
+
+ resource_factory = mrcp_resource_factory_get(resource_loader);
+ mrcp_server_resource_factory_register(server,resource_factory);
+ 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;
+ mrcp_engine_config_t *config;
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;
+ config = mrcp_engine_config_alloc(pool);
for(attr = root->attr; attr; attr = attr->next) {
if(strcasecmp(attr->name,"name") == 0) {
- plugin_name = apr_pstrdup(pool,attr->value);
+ config->name = apr_pstrdup(pool,attr->value);
}
else if(strcasecmp(attr->name,"class") == 0) {
plugin_class = attr->value;
else if(strcasecmp(attr->name,"enable") == 0) {
plugin_enabled = atoi(attr->value);
}
+ else if(strcasecmp(attr->name,"max-channel-count") == 0) {
+ config->max_channel_count = atol(attr->value);
+ }
else {
apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Attribute <%s>",attr->name);
}
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 optional name/value params */
+ if(root->first_child){
+ 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 Engine Params");
+ config->params = apr_table_make(pool,1);
+ 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(config->params,attr_name->value,attr_value->value);
+ }
+ }
+ }
+ }
+
+ return mrcp_server_plugin_register(server,plugin_path,config);
}
/** 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)");
+ apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Loading Engine Plugins");
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);
return FALSE;
}
for(elem = root->first_child; elem; elem = elem->next) {
- if(strcasecmp(elem->name,"settings") == 0) {
+ if(strcasecmp(elem->name,"resources") == 0) {
+ unimrcp_server_resources_load(server,elem,pool);
+ }
+ else if(strcasecmp(elem->name,"settings") == 0) {
unimrcp_server_settings_load(server,plugin_dir_path,elem,pool);
}
else if(strcasecmp(elem->name,"profiles") == 0) {
--- /dev/null
+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 = umc
+umc_SOURCES = src/main.cpp \
+ src/umcconsole.cpp \
+ src/umcframework.cpp \
+ src/umcscenario.cpp \
+ src/umcsession.cpp \
+ src/synthscenario.cpp \
+ src/synthsession.cpp \
+ src/recogscenario.cpp \
+ src/recogsession.cpp \
+ src/recorderscenario.cpp \
+ src/recordersession.cpp \
+ src/dtmfscenario.cpp \
+ src/dtmfsession.cpp
+umc_LDADD = $(top_builddir)/platforms/libunimrcp-client/libunimrcpclient.la
+
+if ISMAC
+umc_LDFLAGS = -framework CoreFoundation -framework SystemConfiguration
+endif
--- /dev/null
+/*
+ * 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 __DTMF_SCENARIO_H__
+#define __DTMF_SCENARIO_H__
+
+/**
+ * @file dtmfscenario.h
+ * @brief DTMF Recognition Scenario
+ */
+
+#include "umcscenario.h"
+
+class DtmfScenario : public UmcScenario
+{
+public:
+/* ============================ CREATORS =================================== */
+ DtmfScenario();
+ virtual ~DtmfScenario();
+
+/* ============================ MANIPULATORS =============================== */
+ virtual void Destroy();
+
+ virtual UmcSession* CreateSession();
+
+/* ============================ ACCESSORS ================================== */
+ const char* GetContentType() const;
+ const char* GetGrammar() const;
+ const char* GetDigits() const;
+
+/* ============================ INQUIRIES ================================== */
+
+protected:
+/* ============================ MANIPULATORS =============================== */
+ virtual bool LoadElement(const apr_xml_elem* pElem, apr_pool_t* pool);
+
+ bool LoadRecognize(const apr_xml_elem* pElem, apr_pool_t* pool);
+
+/* ============================ DATA ======================================= */
+ const char* m_ContentType;
+ const char* m_Grammar;
+ const char* m_Digits;
+};
+
+/* ============================ INLINE METHODS ============================= */
+inline const char* DtmfScenario::GetContentType() const
+{
+ return m_ContentType;
+}
+
+inline const char* DtmfScenario::GetGrammar() const
+{
+ return m_Grammar;
+}
+
+inline const char* DtmfScenario::GetDigits() const
+{
+ return m_Digits;
+}
+
+
+#endif /*__DTMF_SCENARIO_H__*/
--- /dev/null
+/*
+ * 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 __DTMF_SESSION_H__
+#define __DTMF_SESSION_H__
+
+/**
+ * @file dtmfsession.h
+ * @brief DTMF Recognition Session
+ */
+
+#include "umcsession.h"
+
+class DtmfScenario;
+struct RecogChannel;
+
+class DtmfSession : public UmcSession
+{
+public:
+/* ============================ CREATORS =================================== */
+ DtmfSession(const DtmfScenario* pScenario);
+ virtual ~DtmfSession();
+
+protected:
+/* ============================ MANIPULATORS =============================== */
+ virtual bool Start();
+
+ RecogChannel* CreateRecogChannel();
+ bool StartRecognition(mrcp_channel_t* pMrcpChannel);
+
+ mrcp_message_t* CreateRecognizeRequest(mrcp_channel_t* pMrcpChannel);
+
+ bool ParseNLSMLResult(mrcp_message_t* pMrcpMessage) const;
+
+/* ============================ HANDLERS =================================== */
+ virtual bool OnSessionTerminate(mrcp_sig_status_code_e status);
+ virtual bool OnChannelAdd(mrcp_channel_t* channel, mrcp_sig_status_code_e status);
+ virtual bool OnChannelRemove(mrcp_channel_t* channel, mrcp_sig_status_code_e status);
+ virtual bool OnMessageReceive(mrcp_channel_t* channel, mrcp_message_t* message);
+
+/* ============================ ACCESSORS ================================== */
+ const DtmfScenario* GetScenario() const;
+
+private:
+/* ============================ DATA ======================================= */
+ RecogChannel* m_pRecogChannel;
+};
+
+
+/* ============================ INLINE METHODS ============================= */
+inline const DtmfScenario* DtmfSession::GetScenario() const
+{
+ return (DtmfScenario*)m_pScenario;
+}
+
+#endif /*__DTMF_SESSION_H__*/
--- /dev/null
+/*
+ * 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 __RECOG_SCENARIO_H__
+#define __RECOG_SCENARIO_H__
+
+/**
+ * @file recogscenario.h
+ * @brief Recognizer Scenario
+ */
+
+#include "umcscenario.h"
+
+class RecogScenario : public UmcScenario
+{
+public:
+/* ============================ CREATORS =================================== */
+ RecogScenario();
+ virtual ~RecogScenario();
+
+/* ============================ MANIPULATORS =============================== */
+ virtual void Destroy();
+
+ virtual UmcSession* CreateSession();
+
+/* ============================ ACCESSORS ================================== */
+ const char* GetContentType() const;
+ const char* GetContent() const;
+ const char* GetAudioSource() const;
+
+/* ============================ INQUIRIES ================================== */
+ bool IsDefineGrammarEnabled() const;
+ bool IsRecognizeEnabled() const;
+protected:
+/* ============================ MANIPULATORS =============================== */
+ virtual bool LoadElement(const apr_xml_elem* pElem, apr_pool_t* pool);
+
+ bool LoadRecognize(const apr_xml_elem* pElem, apr_pool_t* pool);
+ bool LoadDefineGrammar(const apr_xml_elem* pElem, apr_pool_t* pool);
+
+/* ============================ DATA ======================================= */
+ bool m_DefineGrammar;
+ bool m_Recognize;
+ const char* m_ContentType;
+ const char* m_Content;
+ const char* m_AudioSource;
+};
+
+/* ============================ INLINE METHODS ============================= */
+inline const char* RecogScenario::GetContentType() const
+{
+ return m_ContentType;
+}
+
+inline const char* RecogScenario::GetContent() const
+{
+ return m_Content;
+}
+
+inline const char* RecogScenario::GetAudioSource() const
+{
+ return m_AudioSource;
+}
+
+inline bool RecogScenario::IsDefineGrammarEnabled() const
+{
+ return m_DefineGrammar;
+}
+
+inline bool RecogScenario::IsRecognizeEnabled() const
+{
+ return m_Recognize;
+}
+
+#endif /*__RECOG_SCENARIO_H__*/
--- /dev/null
+/*
+ * 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 __RECOG_SESSION_H__
+#define __RECOG_SESSION_H__
+
+/**
+ * @file recogsession.h
+ * @brief Recognizer Session
+ */
+
+#include "umcsession.h"
+
+class RecogScenario;
+struct RecogChannel;
+
+class RecogSession : public UmcSession
+{
+public:
+/* ============================ CREATORS =================================== */
+ RecogSession(const RecogScenario* pScenario);
+ virtual ~RecogSession();
+
+protected:
+/* ============================ MANIPULATORS =============================== */
+ virtual bool Start();
+
+ RecogChannel* CreateRecogChannel();
+ bool StartRecognition(mrcp_channel_t* pMrcpChannel);
+ bool OnDefineGrammar(mrcp_channel_t* pMrcpChannel);
+
+ mrcp_message_t* CreateDefineGrammarRequest(mrcp_channel_t* pMrcpChannel);
+ mrcp_message_t* CreateRecognizeRequest(mrcp_channel_t* pMrcpChannel);
+
+ bool ParseNLSMLResult(mrcp_message_t* pMrcpMessage) const;
+ FILE* GetAudioIn(const mpf_codec_descriptor_t* pDescriptor, apr_pool_t* pool) const;
+
+/* ============================ HANDLERS =================================== */
+ virtual bool OnSessionTerminate(mrcp_sig_status_code_e status);
+ virtual bool OnChannelAdd(mrcp_channel_t* channel, mrcp_sig_status_code_e status);
+ virtual bool OnChannelRemove(mrcp_channel_t* channel, mrcp_sig_status_code_e status);
+ virtual bool OnMessageReceive(mrcp_channel_t* channel, mrcp_message_t* message);
+
+/* ============================ ACCESSORS ================================== */
+ const RecogScenario* GetScenario() const;
+
+private:
+/* ============================ DATA ======================================= */
+ RecogChannel* m_pRecogChannel;
+ const char* m_ContentId;
+};
+
+
+/* ============================ INLINE METHODS ============================= */
+inline const RecogScenario* RecogSession::GetScenario() const
+{
+ return (RecogScenario*)m_pScenario;
+}
+
+#endif /*__RECOG_SESSION_H__*/
--- /dev/null
+/*
+ * 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 __RECORDER_SCENARIO_H__
+#define __RECORDER_SCENARIO_H__
+
+/**
+ * @file recorderscenario.h
+ * @brief Recorder Scenario
+ */
+
+#include "umcscenario.h"
+
+class RecorderScenario : public UmcScenario
+{
+public:
+/* ============================ CREATORS =================================== */
+ RecorderScenario();
+ virtual ~RecorderScenario();
+
+/* ============================ MANIPULATORS =============================== */
+ virtual void Destroy();
+
+ virtual UmcSession* CreateSession();
+
+/* ============================ ACCESSORS ================================== */
+ const char* GetAudioSource() const;
+
+/* ============================ INQUIRIES ================================== */
+ bool IsRecordEnabled() const;
+protected:
+/* ============================ MANIPULATORS =============================== */
+ virtual bool LoadElement(const apr_xml_elem* pElem, apr_pool_t* pool);
+
+ bool LoadRecord(const apr_xml_elem* pElem, apr_pool_t* pool);
+
+/* ============================ DATA ======================================= */
+ bool m_Record;
+ const char* m_AudioSource;
+};
+
+/* ============================ INLINE METHODS ============================= */
+inline const char* RecorderScenario::GetAudioSource() const
+{
+ return m_AudioSource;
+}
+
+inline bool RecorderScenario::IsRecordEnabled() const
+{
+ return m_Record;
+}
+
+#endif /*__RECORDER_SCENARIO_H__*/
--- /dev/null
+/*
+ * 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 __RECORDER_SESSION_H__
+#define __RECORDER_SESSION_H__
+
+/**
+ * @file recordersession.h
+ * @brief Recorder Session
+ */
+
+#include "umcsession.h"
+
+class RecorderScenario;
+struct RecorderChannel;
+
+class RecorderSession : public UmcSession
+{
+public:
+/* ============================ CREATORS =================================== */
+ RecorderSession(const RecorderScenario* pScenario);
+ virtual ~RecorderSession();
+
+protected:
+/* ============================ MANIPULATORS =============================== */
+ virtual bool Start();
+
+ RecorderChannel* CreateRecorderChannel();
+ bool StartRecorder(mrcp_channel_t* pMrcpChannel);
+
+ mrcp_message_t* CreateRecordRequest(mrcp_channel_t* pMrcpChannel);
+
+ FILE* GetAudioIn(const mpf_codec_descriptor_t* pDescriptor, apr_pool_t* pool) const;
+
+/* ============================ HANDLERS =================================== */
+ virtual bool OnSessionTerminate(mrcp_sig_status_code_e status);
+ virtual bool OnChannelAdd(mrcp_channel_t* channel, mrcp_sig_status_code_e status);
+ virtual bool OnChannelRemove(mrcp_channel_t* channel, mrcp_sig_status_code_e status);
+ virtual bool OnMessageReceive(mrcp_channel_t* channel, mrcp_message_t* message);
+
+/* ============================ ACCESSORS ================================== */
+ const RecorderScenario* GetScenario() const;
+
+private:
+/* ============================ DATA ======================================= */
+ RecorderChannel* m_pRecorderChannel;
+};
+
+
+/* ============================ INLINE METHODS ============================= */
+inline const RecorderScenario* RecorderSession::GetScenario() const
+{
+ return (RecorderScenario*)m_pScenario;
+}
+
+#endif /*__RECORDER_SESSION_H__*/
--- /dev/null
+/*
+ * 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 __SYNTH_SCENARIO_H__
+#define __SYNTH_SCENARIO_H__
+
+/**
+ * @file synthscenario.h
+ * @brief Synthesizer Scenario
+ */
+
+#include "umcscenario.h"
+
+class SynthScenario : public UmcScenario
+{
+public:
+/* ============================ CREATORS =================================== */
+ SynthScenario();
+ virtual ~SynthScenario();
+
+/* ============================ MANIPULATORS =============================== */
+ virtual void Destroy();
+
+ virtual UmcSession* CreateSession();
+
+/* ============================ ACCESSORS ================================== */
+ const char* GetContentType() const;
+ const char* GetContent() const;
+
+/* ============================ INQUIRIES ================================== */
+ bool IsSpeakEnabled() const;
+
+protected:
+/* ============================ MANIPULATORS =============================== */
+ virtual bool LoadElement(const apr_xml_elem* pElem, apr_pool_t* pool);
+
+ bool LoadSpeak(const apr_xml_elem* pElem, apr_pool_t* pool);
+
+/* ============================ DATA ======================================= */
+ bool m_Speak;
+ const char* m_ContentType;
+ const char* m_Content;
+};
+
+/* ============================ INLINE METHODS ============================= */
+inline const char* SynthScenario::GetContentType() const
+{
+ return m_ContentType;
+}
+
+inline const char* SynthScenario::GetContent() const
+{
+ return m_Content;
+}
+
+inline bool SynthScenario::IsSpeakEnabled() const
+{
+ return m_Speak;
+}
+
+#endif /*__SYNTH_SCENARIO_H__*/
--- /dev/null
+/*
+ * 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 __SYNTH_SESSION_H__
+#define __SYNTH_SESSION_H__
+
+/**
+ * @file synthsession.h
+ * @brief Synthesizer Session
+ */
+
+#include "umcsession.h"
+
+class SynthScenario;
+struct SynthChannel;
+
+class SynthSession : public UmcSession
+{
+public:
+/* ============================ CREATORS =================================== */
+ SynthSession(const SynthScenario* pScenario);
+ virtual ~SynthSession();
+
+protected:
+/* ============================ MANIPULATORS =============================== */
+ virtual bool Start();
+
+ SynthChannel* CreateSynthChannel();
+
+ mrcp_message_t* CreateSpeakRequest(mrcp_channel_t* pMrcpChannel);
+ FILE* GetAudioOut(const mpf_codec_descriptor_t* pDescriptor, apr_pool_t* pool) const;
+
+/* ============================ HANDLERS =================================== */
+ virtual bool OnSessionTerminate(mrcp_sig_status_code_e status);
+ virtual bool OnChannelAdd(mrcp_channel_t* channel, mrcp_sig_status_code_e status);
+ virtual bool OnChannelRemove(mrcp_channel_t* channel, mrcp_sig_status_code_e status);
+ virtual bool OnMessageReceive(mrcp_channel_t* channel, mrcp_message_t* message);
+
+/* ============================ ACCESSORS ================================== */
+ const SynthScenario* GetScenario() const;
+
+private:
+/* ============================ DATA ======================================= */
+ SynthChannel* m_pSynthChannel;
+};
+
+
+/* ============================ INLINE METHODS ============================= */
+inline const SynthScenario* SynthSession::GetScenario() const
+{
+ return (SynthScenario*)m_pScenario;
+}
+
+#endif /*__SYNTH_SESSION_H__*/
--- /dev/null
+/*
+ * 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 __UMC_CONSOLE_H__
+#define __UMC_CONSOLE_H__
+
+/**
+ * @file umcconsole.h
+ * @brief UMC Application Console
+ */
+
+#include "apt_log.h"
+
+class UmcFramework;
+
+class UmcConsole
+{
+public:
+/* ============================ CREATORS =================================== */
+ UmcConsole();
+ ~UmcConsole();
+
+/* ============================ MANIPULATORS =============================== */
+ bool Run(int argc, const char * const *argv);
+
+protected:
+ bool LoadOptions(int argc, const char * const *argv, apr_pool_t *pool);
+ bool RunCmdLine();
+ bool ProcessCmdLine(char* pCmdLine);
+ void Usage() const;
+
+private:
+/* ============================ DATA ======================================= */
+ struct UmcOptions
+ {
+ const char* m_RootDirPath;
+ apt_log_priority_e m_LogPriority;
+ apt_log_output_e m_LogOutput;
+ };
+
+ UmcOptions m_Options;
+ UmcFramework* m_pFramework;
+};
+
+#endif /*__UMC_CONSOLE_H__*/
--- /dev/null
+/*
+ * 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 __UMC_FRAMEWORK_H__
+#define __UMC_FRAMEWORK_H__
+
+/**
+ * @file umcframework.h
+ * @brief UMC Application Framework
+ */
+
+#include <apr_xml.h>
+#include <apr_hash.h>
+#include "mrcp_application.h"
+#include "apt_consumer_task.h"
+
+class UmcSession;
+class UmcScenario;
+
+class UmcFramework
+{
+public:
+/* ============================ CREATORS =================================== */
+ UmcFramework();
+ ~UmcFramework();
+
+/* ============================ MANIPULATORS =============================== */
+ bool Create(apt_dir_layout_t* pDirLayout, apr_pool_t* pool);
+ void Destroy();
+
+ void RunSession(const char* pScenarioName, const char* pProfileName);
+ void KillSession(const char* id);
+
+ void ShowScenarios();
+ void ShowSessions();
+
+protected:
+ bool CreateMrcpClient();
+ void DestroyMrcpClient();
+
+ bool CreateTask();
+ void DestroyTask();
+
+ UmcScenario* CreateScenario(const char* pType);
+ apr_xml_doc* LoadDocument();
+
+ bool LoadScenarios();
+ void DestroyScenarios();
+
+ bool ProcessRunRequest(const char* pScenarioName, const char* pProfileName);
+ void ProcessKillRequest(const char* id);
+ void ProcessShowScenarios();
+ void ProcessShowSessions();
+
+ bool AddSession(UmcSession* pSession);
+ bool RemoveSession(UmcSession* pSession);
+
+/* ============================ HANDLERS =================================== */
+ friend apt_bool_t UmcProcessMsg(apt_task_t* pTask, apt_task_msg_t* pMsg);
+ friend void UmcOnStartComplete(apt_task_t* pTask);
+ friend void UmcOnTerminateComplete(apt_task_t* pTask);
+
+ friend apt_bool_t AppMessageHandler(const mrcp_app_message_t* pAppMessage);
+ friend apt_bool_t AppOnReady(mrcp_application_t* pMrcpApplication, mrcp_sig_status_code_e status);
+ friend apt_bool_t AppOnSessionTerminate(mrcp_application_t *application, mrcp_session_t *session, mrcp_sig_status_code_e status);
+
+private:
+/* ============================ DATA ======================================= */
+ apr_pool_t* m_pPool;
+ apt_dir_layout_t* m_pDirLayout;
+ apt_consumer_task_t* m_pTask;
+
+ mrcp_client_t* m_pMrcpClient;
+ mrcp_application_t* m_pMrcpApplication;
+
+ bool m_Ready;
+
+ apr_hash_t* m_pScenarioTable;
+ apr_hash_t* m_pSessionTable;
+};
+
+#endif /*__UMC_FRAMEWORK_H__*/
--- /dev/null
+/*
+ * 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 __UMC_SCENARIO_H__
+#define __UMC_SCENARIO_H__
+
+/**
+ * @file umcscenario.h
+ * @brief UMC Scenario
+ */
+
+#include <apr_xml.h>
+#include "mrcp_application.h"
+
+class UmcSession;
+
+class UmcScenario
+{
+public:
+/* ============================ CREATORS =================================== */
+ UmcScenario();
+ virtual ~UmcScenario();
+
+/* ============================ MANIPULATORS =============================== */
+ virtual bool Load(const apr_xml_elem* pElem, apr_pool_t* pool);
+ virtual void Destroy();
+
+ virtual UmcSession* CreateSession() = 0;
+
+ void SetDirLayout(apt_dir_layout_t* pDirLayout);
+ void SetName(const char* pName);
+ void SetMrcpProfile(const char* pMrcpProfile);
+
+ bool InitCapabilities(mpf_stream_capabilities_t* pCapabilities) const;
+
+/* ============================ ACCESSORS ================================== */
+ apt_dir_layout_t* GetDirLayout() const;
+ const char* GetName() const;
+ const char* GetMrcpProfile() const;
+
+/* ============================ INQUIRIES ================================== */
+ bool IsDiscoveryEnabled() const;
+
+protected:
+/* ============================ MANIPULATORS =============================== */
+ virtual bool LoadElement(const apr_xml_elem* pElem, apr_pool_t* pool);
+
+ bool LoadDiscovery(const apr_xml_elem* pElem, apr_pool_t* pool);
+ bool LoadTermination(const apr_xml_elem* pElem, apr_pool_t* pool);
+ bool LoadCapabilities(const apr_xml_elem* pElem, apr_pool_t* pool);
+ bool LoadRtpTermination(const apr_xml_elem* pElem, apr_pool_t* pool);
+
+ const char* LoadFileContent(const char* pFileName, apr_pool_t* pool) const;
+ int ParseRates(const char* pStr, apr_pool_t* pool) const;
+
+/* ============================ INQUIRIES ================================== */
+ bool IsElementEnabled(const apr_xml_elem* pElem) const;
+
+/* ============================ DATA ======================================= */
+ const char* m_pName;
+ const char* m_pMrcpProfile;
+ apt_dir_layout_t* m_pDirLayout;
+
+ bool m_ResourceDiscovery;
+ mpf_codec_capabilities_t* m_pCapabilities;
+ mpf_rtp_termination_descriptor_t* m_pRtpDescriptor;
+};
+
+
+/* ============================ INLINE METHODS ============================= */
+inline void UmcScenario::SetDirLayout(apt_dir_layout_t* pDirLayout)
+{
+ m_pDirLayout = pDirLayout;
+}
+
+inline apt_dir_layout_t* UmcScenario::GetDirLayout() const
+{
+ return m_pDirLayout;
+}
+
+inline void UmcScenario::SetName(const char* pName)
+{
+ m_pName = pName;
+}
+
+inline const char* UmcScenario::GetName() const
+{
+ return m_pName;
+}
+
+inline void UmcScenario::SetMrcpProfile(const char* pMrcpProfile)
+{
+ m_pMrcpProfile = pMrcpProfile;
+}
+
+inline const char* UmcScenario::GetMrcpProfile() const
+{
+ return m_pMrcpProfile;
+}
+
+inline bool UmcScenario::IsDiscoveryEnabled() const
+{
+ return m_ResourceDiscovery;
+}
+
+
+#endif /*__UMC_SCENARIO_H__*/
--- /dev/null
+/*
+ * 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 __UMC_SESSION_H__
+#define __UMC_SESSION_H__
+
+/**
+ * @file umcsession.h
+ * @brief UMC Session
+ */
+
+#include "mrcp_application.h"
+
+class UmcScenario;
+
+class UmcSession
+{
+public:
+/* ============================ CREATORS =================================== */
+ UmcSession(const UmcScenario* pScenario);
+ virtual ~UmcSession();
+
+/* ============================ MANIPULATORS =============================== */
+ virtual bool Run();
+ virtual bool Terminate();
+
+ void SetMrcpProfile(const char* pMrcpProfile);
+ void SetMrcpApplication(mrcp_application_t* pMrcpApplication);
+
+/* ============================ HANDLERS =================================== */
+ virtual bool OnSessionTerminate(mrcp_sig_status_code_e status);
+ virtual bool OnSessionUpdate(mrcp_sig_status_code_e status);
+ virtual bool OnChannelAdd(mrcp_channel_t *channel, mrcp_sig_status_code_e status);
+ virtual bool OnChannelRemove(mrcp_channel_t *channel, mrcp_sig_status_code_e status);
+ virtual bool OnMessageReceive(mrcp_channel_t *channel, mrcp_message_t *message);
+ virtual bool OnTerminateEvent(mrcp_channel_t *channel);
+ virtual bool OnResourceDiscover(mrcp_session_descriptor_t* descriptor, mrcp_sig_status_code_e status);
+
+/* ============================ ACCESSORS ================================== */
+ const UmcScenario* GetScenario() const;
+
+ const char* GetId() const;
+
+protected:
+/* ============================ MANIPULATORS =============================== */
+ virtual bool Start() = 0;
+
+ bool CreateMrcpSession(const char* pProfileName);
+ bool DestroyMrcpSession();
+
+ bool AddMrcpChannel(mrcp_channel_t* pMrcpChannel);
+ bool RemoveMrcpChannel(mrcp_channel_t* pMrcpChannel);
+ bool SendMrcpRequest(mrcp_channel_t* pMrcpChannel, mrcp_message_t* pMrcpMessage);
+ bool ResourceDiscover();
+
+ mrcp_channel_t* CreateMrcpChannel(
+ mrcp_resource_id resource_id,
+ mpf_termination_t* pTermination,
+ mpf_rtp_termination_descriptor_t* pRtpDescriptor,
+ void* pObj);
+ mpf_termination_t* CreateAudioTermination(
+ const mpf_audio_stream_vtable_t* pStreamVtable,
+ mpf_stream_capabilities_t* pCapabilities,
+ void* pObj);
+ mrcp_message_t* CreateMrcpMessage(
+ mrcp_channel_t* pMrcpChannel,
+ mrcp_method_id method_id);
+
+/* ============================ ACCESSORS ================================== */
+ apr_pool_t* GetSessionPool() const;
+ const char* GetMrcpSessionId() const;
+
+/* ============================ DATA ======================================= */
+ const UmcScenario* m_pScenario;
+ const char* m_pMrcpProfile;
+ char m_Id[10];
+
+private:
+/* ============================ DATA ======================================= */
+ mrcp_application_t* m_pMrcpApplication;
+ mrcp_session_t* m_pMrcpSession;
+ bool m_Running;
+ bool m_Terminating;
+};
+
+
+/* ============================ INLINE METHODS ============================= */
+inline const UmcScenario* UmcSession::GetScenario() const
+{
+ return m_pScenario;
+}
+
+inline const char* UmcSession::GetId() const
+{
+ return m_Id;
+}
+
+inline void UmcSession::SetMrcpApplication(mrcp_application_t* pMrcpApplication)
+{
+ m_pMrcpApplication = pMrcpApplication;
+}
+
+inline void UmcSession::SetMrcpProfile(const char* pMrcpProfile)
+{
+ m_pMrcpProfile = pMrcpProfile;
+}
+
+#endif /*__UMC_SESSION_H__*/
--- /dev/null
+/*
+ * 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 <stdlib.h>
+#include "dtmfscenario.h"
+#include "dtmfsession.h"
+#include "apt_log.h"
+
+DtmfScenario::DtmfScenario() :
+ m_ContentType(NULL),
+ m_Grammar(NULL),
+ m_Digits(NULL)
+{
+}
+
+DtmfScenario::~DtmfScenario()
+{
+}
+
+void DtmfScenario::Destroy()
+{
+}
+
+bool DtmfScenario::LoadElement(const apr_xml_elem* pElem, apr_pool_t* pool)
+{
+ if(UmcScenario::LoadElement(pElem,pool))
+ return true;
+
+ if(strcasecmp(pElem->name,"recognize") == 0)
+ {
+ LoadRecognize(pElem,pool);
+ return true;
+ }
+
+ return false;
+}
+
+bool DtmfScenario::LoadRecognize(const apr_xml_elem* pElem, apr_pool_t* pool)
+{
+ const apr_xml_attr* pAttr;
+ for(pAttr = pElem->attr; pAttr; pAttr = pAttr->next)
+ {
+ if(strcasecmp(pAttr->name,"content-type") == 0)
+ {
+ m_ContentType = pAttr->value;
+ }
+ else if(strcasecmp(pAttr->name,"grammar") == 0)
+ {
+ m_Grammar = pAttr->value;
+ }
+ else if(strcasecmp(pAttr->name,"digits") == 0)
+ {
+ m_Digits = pAttr->value;
+ }
+ }
+
+ return true;
+}
+
+
+UmcSession* DtmfScenario::CreateSession()
+{
+ return new DtmfSession(this);
+}
--- /dev/null
+/*
+ * 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 "dtmfsession.h"
+#include "dtmfscenario.h"
+#include "mrcp_message.h"
+#include "mrcp_generic_header.h"
+#include "mrcp_recog_header.h"
+#include "mrcp_recog_resource.h"
+#include "mpf_dtmf_generator.h"
+#include "apt_nlsml_doc.h"
+#include "apt_log.h"
+
+struct RecogChannel
+{
+ /** MRCP control channel */
+ mrcp_channel_t* m_pMrcpChannel;
+ /** DTMF generator */
+ mpf_dtmf_generator_t* m_pDtmfGenerator;
+ /** Audio stream */
+ mpf_audio_stream_t* m_pStream;
+ /** Streaming is in-progress */
+ bool m_Streaming;
+};
+
+DtmfSession::DtmfSession(const DtmfScenario* pScenario) :
+ UmcSession(pScenario),
+ m_pRecogChannel(NULL)
+{
+}
+
+DtmfSession::~DtmfSession()
+{
+}
+
+bool DtmfSession::Start()
+{
+ /* create channel and associate all the required data */
+ m_pRecogChannel = CreateRecogChannel();
+ if(!m_pRecogChannel)
+ return false;
+
+ /* add channel to session (send asynchronous request) */
+ if(!AddMrcpChannel(m_pRecogChannel->m_pMrcpChannel))
+ {
+ delete m_pRecogChannel;
+ m_pRecogChannel = NULL;
+ return false;
+ }
+ return true;
+}
+
+bool DtmfSession::OnSessionTerminate(mrcp_sig_status_code_e status)
+{
+ if(m_pRecogChannel)
+ {
+ if(m_pRecogChannel->m_pDtmfGenerator)
+ {
+ mpf_dtmf_generator_destroy(m_pRecogChannel->m_pDtmfGenerator);
+ m_pRecogChannel->m_pDtmfGenerator = NULL;
+ }
+
+ delete m_pRecogChannel;
+ m_pRecogChannel = NULL;
+ }
+ return UmcSession::OnSessionTerminate(status);
+}
+
+static apt_bool_t OpenStream(mpf_audio_stream_t* pStream, mpf_codec_t *codec)
+{
+ RecogChannel* pRecogChannel = (RecogChannel*) pStream->obj;
+ pRecogChannel->m_pStream = pStream;
+ return TRUE;
+}
+
+static apt_bool_t ReadStream(mpf_audio_stream_t* pStream, mpf_frame_t* pFrame)
+{
+ RecogChannel* pRecogChannel = (RecogChannel*) pStream->obj;
+ if(pRecogChannel && pRecogChannel->m_Streaming)
+ {
+ if(pRecogChannel->m_pDtmfGenerator)
+ {
+ mpf_dtmf_generator_put_frame(pRecogChannel->m_pDtmfGenerator,pFrame);
+ }
+ }
+ return TRUE;
+}
+
+RecogChannel* DtmfSession::CreateRecogChannel()
+{
+ mrcp_channel_t* pChannel;
+ mpf_termination_t* pTermination;
+ mpf_stream_capabilities_t* pCapabilities;
+ apr_pool_t* pool = GetSessionPool();
+
+ /* create channel */
+ RecogChannel *pRecogChannel = new RecogChannel;
+ pRecogChannel->m_pMrcpChannel = NULL;
+ pRecogChannel->m_pDtmfGenerator = NULL;
+ pRecogChannel->m_pStream = NULL;
+ pRecogChannel->m_Streaming = false;
+
+ /* create source stream capabilities */
+ pCapabilities = mpf_source_stream_capabilities_create(pool);
+ GetScenario()->InitCapabilities(pCapabilities);
+
+ static const mpf_audio_stream_vtable_t audio_stream_vtable =
+ {
+ NULL,
+ OpenStream,
+ NULL,
+ ReadStream,
+ NULL,
+ NULL,
+ NULL
+ };
+
+ pTermination = CreateAudioTermination(
+ &audio_stream_vtable, /* virtual methods table of audio stream */
+ pCapabilities, /* capabilities of audio stream */
+ pRecogChannel); /* object to associate */
+
+ pChannel = CreateMrcpChannel(
+ MRCP_RECOGNIZER_RESOURCE, /* MRCP resource identifier */
+ pTermination, /* media termination, used to terminate audio stream */
+ NULL, /* RTP descriptor, used to create RTP termination (NULL by default) */
+ pRecogChannel); /* object to associate */
+ if(!pChannel)
+ {
+ delete pRecogChannel;
+ return NULL;
+ }
+
+ pRecogChannel->m_pMrcpChannel = pChannel;
+ return pRecogChannel;
+}
+
+bool DtmfSession::OnChannelAdd(mrcp_channel_t* pMrcpChannel, mrcp_sig_status_code_e status)
+{
+ if(!UmcSession::OnChannelAdd(pMrcpChannel,status))
+ return false;
+
+ if(status != MRCP_SIG_STATUS_CODE_SUCCESS)
+ {
+ /* error case, just terminate the demo */
+ return Terminate();
+ }
+
+ RecogChannel* pRecogChannel = (RecogChannel*) mrcp_application_channel_object_get(pMrcpChannel);
+ if(pRecogChannel)
+ {
+ pRecogChannel->m_pDtmfGenerator = mpf_dtmf_generator_create(pRecogChannel->m_pStream,GetSessionPool());
+ }
+
+ return StartRecognition(pMrcpChannel);
+}
+
+bool DtmfSession::OnChannelRemove(mrcp_channel_t* pMrcpChannel, mrcp_sig_status_code_e status)
+{
+ if(!UmcSession::OnChannelRemove(pMrcpChannel,status))
+ return false;
+
+ RecogChannel* pRecogChannel = (RecogChannel*) mrcp_application_channel_object_get(pMrcpChannel);
+ if(pRecogChannel)
+ {
+ if(pRecogChannel->m_pDtmfGenerator)
+ {
+ mpf_dtmf_generator_destroy(pRecogChannel->m_pDtmfGenerator);
+ pRecogChannel->m_pDtmfGenerator = NULL;
+ }
+ }
+
+ /* terminate the demo */
+ return Terminate();
+}
+
+bool DtmfSession::OnMessageReceive(mrcp_channel_t* pMrcpChannel, mrcp_message_t* pMrcpMessage)
+{
+ if(!UmcSession::OnMessageReceive(pMrcpChannel,pMrcpMessage))
+ return false;
+
+ const DtmfScenario* pScenario = GetScenario();
+ RecogChannel* pRecogChannel = (RecogChannel*) mrcp_application_channel_object_get(pMrcpChannel);
+ if(pMrcpMessage->start_line.message_type == MRCP_MESSAGE_TYPE_RESPONSE)
+ {
+ if(pMrcpMessage->start_line.method_id == RECOGNIZER_RECOGNIZE)
+ {
+ /* received the response to RECOGNIZE request */
+ if(pMrcpMessage->start_line.request_state == MRCP_REQUEST_STATE_INPROGRESS)
+ {
+ /* start to stream the DTMFs to recognize */
+ if(pRecogChannel && pRecogChannel->m_pDtmfGenerator)
+ {
+ const char* digits = pScenario->GetDigits();
+ if(digits)
+ {
+ mpf_dtmf_generator_enqueue(pRecogChannel->m_pDtmfGenerator,digits);
+ pRecogChannel->m_Streaming = true;
+ }
+ }
+ }
+ else
+ {
+ /* received unexpected response, remove channel */
+ RemoveMrcpChannel(pMrcpChannel);
+ }
+ }
+ else
+ {
+ /* received unexpected response */
+ }
+ }
+ else if(pMrcpMessage->start_line.message_type == MRCP_MESSAGE_TYPE_EVENT)
+ {
+ if(pMrcpMessage->start_line.method_id == RECOGNIZER_RECOGNITION_COMPLETE)
+ {
+ ParseNLSMLResult(pMrcpMessage);
+ if(pRecogChannel)
+ {
+ pRecogChannel->m_Streaming = false;
+ }
+ RemoveMrcpChannel(pMrcpChannel);
+ }
+ else if(pMrcpMessage->start_line.method_id == RECOGNIZER_START_OF_INPUT)
+ {
+ /* received start-of-input, do whatever you need here */
+ }
+ }
+ return true;
+}
+
+bool DtmfSession::StartRecognition(mrcp_channel_t* pMrcpChannel)
+{
+ RecogChannel* pRecogChannel = (RecogChannel*) mrcp_application_channel_object_get(pMrcpChannel);
+ /* create and send RECOGNIZE request */
+ mrcp_message_t* pMrcpMessage = CreateRecognizeRequest(pMrcpChannel);
+ if(pMrcpMessage)
+ {
+ SendMrcpRequest(pRecogChannel->m_pMrcpChannel,pMrcpMessage);
+ }
+
+ return true;
+}
+
+mrcp_message_t* DtmfSession::CreateRecognizeRequest(mrcp_channel_t* pMrcpChannel)
+{
+ mrcp_message_t* pMrcpMessage = CreateMrcpMessage(pMrcpChannel,RECOGNIZER_RECOGNIZE);
+ if(!pMrcpMessage)
+ return NULL;
+
+ const DtmfScenario* pScenario = GetScenario();
+
+ mrcp_generic_header_t* pGenericHeader;
+ mrcp_recog_header_t* pRecogHeader;
+
+ /* get/allocate generic header */
+ pGenericHeader = (mrcp_generic_header_t*) mrcp_generic_header_prepare(pMrcpMessage);
+ if(pGenericHeader)
+ {
+ apt_string_assign(&pGenericHeader->content_type,pScenario->GetContentType(),pMrcpMessage->pool);
+ mrcp_generic_header_property_add(pMrcpMessage,GENERIC_HEADER_CONTENT_TYPE);
+ /* set message body */
+ if(pScenario->GetGrammar())
+ apt_string_assign(&pMrcpMessage->body,pScenario->GetGrammar(),pMrcpMessage->pool);
+ }
+ /* get/allocate recognizer header */
+ pRecogHeader = (mrcp_recog_header_t*) mrcp_resource_header_prepare(pMrcpMessage);
+ if(pRecogHeader)
+ {
+ /* set recognizer header fields */
+ if(pMrcpMessage->start_line.version == MRCP_VERSION_2)
+ {
+ pRecogHeader->cancel_if_queue = FALSE;
+ mrcp_resource_header_property_add(pMrcpMessage,RECOGNIZER_HEADER_CANCEL_IF_QUEUE);
+ }
+ }
+ return pMrcpMessage;
+}
+
+bool DtmfSession::ParseNLSMLResult(mrcp_message_t* pMrcpMessage) const
+{
+ apr_xml_elem* pInterpret;
+ apr_xml_elem* pInstance;
+ apr_xml_elem* pInput;
+ apr_xml_doc* pDoc = nlsml_doc_load(&pMrcpMessage->body,pMrcpMessage->pool);
+ if(!pDoc)
+ return false;
+
+ /* walk through interpreted results */
+ pInterpret = nlsml_first_interpret_get(pDoc);
+ for(; pInterpret; pInterpret = nlsml_next_interpret_get(pInterpret))
+ {
+ /* get instance and input */
+ nlsml_interpret_results_get(pInterpret,&pInstance,&pInput);
+ if(pInstance)
+ {
+ /* process instance */
+ if(pInstance->first_cdata.first)
+ {
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Interpreted Instance [%s]",pInstance->first_cdata.first->text);
+ }
+ }
+ if(pInput)
+ {
+ /* process input */
+ if(pInput->first_cdata.first)
+ {
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Interpreted Input [%s]",pInput->first_cdata.first->text);
+ }
+ }
+ }
+ return true;
+}
--- /dev/null
+/*
+ * 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 "umcconsole.h"
+
+int main(int argc, const char * const *argv)
+{
+ UmcConsole console;
+ console.Run(argc,argv);
+ return 0;
+}
--- /dev/null
+/*
+ * 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 <stdlib.h>
+#include "recogscenario.h"
+#include "recogsession.h"
+#include "mrcp_message.h"
+#include "mrcp_generic_header.h"
+#include "mrcp_recog_header.h"
+#include "mrcp_recog_resource.h"
+#include "apt_log.h"
+
+RecogScenario::RecogScenario() :
+ m_DefineGrammar(true),
+ m_Recognize(true),
+ m_ContentType("application/srgs+xml"),
+ m_Content(NULL),
+ m_AudioSource(NULL)
+{
+}
+
+RecogScenario::~RecogScenario()
+{
+}
+
+void RecogScenario::Destroy()
+{
+}
+
+bool RecogScenario::LoadElement(const apr_xml_elem* pElem, apr_pool_t* pool)
+{
+ if(UmcScenario::LoadElement(pElem,pool))
+ return true;
+
+ if(strcasecmp(pElem->name,"define-grammar") == 0)
+ {
+ LoadDefineGrammar(pElem,pool);
+ return true;
+ }
+ else if(strcasecmp(pElem->name,"recognize") == 0)
+ {
+ LoadRecognize(pElem,pool);
+ return true;
+ }
+
+ return false;
+}
+
+bool RecogScenario::LoadRecognize(const apr_xml_elem* pElem, apr_pool_t* pool)
+{
+ const apr_xml_attr* pAttr;
+ for(pAttr = pElem->attr; pAttr; pAttr = pAttr->next)
+ {
+ if(strcasecmp(pAttr->name,"enable") == 0)
+ {
+ m_Recognize = atoi(pAttr->value) > 0;
+ }
+ else if(strcasecmp(pAttr->name,"content-type") == 0)
+ {
+ m_ContentType = pAttr->value;
+ }
+ else if(strcasecmp(pAttr->name,"content-location") == 0)
+ {
+ m_Content = LoadFileContent(pAttr->value,pool);
+ }
+ else if(strcasecmp(pAttr->name,"audio-source") == 0)
+ {
+ m_AudioSource = pAttr->value;
+ }
+ }
+
+ return true;
+}
+
+bool RecogScenario::LoadDefineGrammar(const apr_xml_elem* pElem, apr_pool_t* pool)
+{
+ const apr_xml_attr* pAttr;
+ for(pAttr = pElem->attr; pAttr; pAttr = pAttr->next)
+ {
+ if(strcasecmp(pAttr->name,"enable") == 0)
+ {
+ m_DefineGrammar = atoi(pAttr->value) > 0;
+ }
+ else if(strcasecmp(pAttr->name,"content-type") == 0)
+ {
+ m_ContentType = pAttr->value;
+ }
+ else if(strcasecmp(pAttr->name,"content-location") == 0)
+ {
+ m_Content = LoadFileContent(pAttr->value,pool);
+ }
+ }
+ return true;
+}
+
+
+UmcSession* RecogScenario::CreateSession()
+{
+ return new RecogSession(this);
+}
--- /dev/null
+/*
+ * 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 "recogsession.h"
+#include "recogscenario.h"
+#include "mrcp_message.h"
+#include "mrcp_generic_header.h"
+#include "mrcp_recog_header.h"
+#include "mrcp_recog_resource.h"
+#include "apt_nlsml_doc.h"
+#include "apt_log.h"
+
+struct RecogChannel
+{
+ /** MRCP control channel */
+ mrcp_channel_t* m_pMrcpChannel;
+ /** Streaming is in-progress */
+ bool m_Streaming;
+ /** File to read audio stream from */
+ FILE* m_pAudioIn;
+ /** Estimated time to complete (used if no audio_in available) */
+ apr_size_t m_TimeToComplete;
+};
+
+RecogSession::RecogSession(const RecogScenario* pScenario) :
+ UmcSession(pScenario),
+ m_pRecogChannel(NULL),
+ m_ContentId("request1@form-level")
+{
+}
+
+RecogSession::~RecogSession()
+{
+}
+
+bool RecogSession::Start()
+{
+ const RecogScenario* pScenario = GetScenario();
+ if(!pScenario->IsDefineGrammarEnabled() && !pScenario->IsRecognizeEnabled())
+ return false;
+
+ /* create channel and associate all the required data */
+ m_pRecogChannel = CreateRecogChannel();
+ if(!m_pRecogChannel)
+ return false;
+
+ /* add channel to session (send asynchronous request) */
+ if(!AddMrcpChannel(m_pRecogChannel->m_pMrcpChannel))
+ {
+ delete m_pRecogChannel;
+ m_pRecogChannel = NULL;
+ return false;
+ }
+ return true;
+}
+
+bool RecogSession::OnSessionTerminate(mrcp_sig_status_code_e status)
+{
+ if(m_pRecogChannel)
+ {
+ FILE* pAudioIn = m_pRecogChannel->m_pAudioIn;
+ if(pAudioIn)
+ {
+ m_pRecogChannel->m_pAudioIn = NULL;
+ fclose(pAudioIn);
+ }
+
+ delete m_pRecogChannel;
+ m_pRecogChannel = NULL;
+ }
+ return UmcSession::OnSessionTerminate(status);
+}
+
+static apt_bool_t ReadStream(mpf_audio_stream_t* pStream, mpf_frame_t* pFrame)
+{
+ RecogChannel* pRecogChannel = (RecogChannel*) pStream->obj;
+ if(pRecogChannel && pRecogChannel->m_Streaming)
+ {
+ if(pRecogChannel->m_pAudioIn)
+ {
+ if(fread(pFrame->codec_frame.buffer,1,pFrame->codec_frame.size,pRecogChannel->m_pAudioIn) == pFrame->codec_frame.size)
+ {
+ /* normal read */
+ pFrame->type |= MEDIA_FRAME_TYPE_AUDIO;
+ }
+ else
+ {
+ /* file is over */
+ pRecogChannel->m_Streaming = false;
+ }
+ }
+ else
+ {
+ /* fill with silence in case no file available */
+ if(pRecogChannel->m_TimeToComplete >= CODEC_FRAME_TIME_BASE)
+ {
+ pFrame->type |= MEDIA_FRAME_TYPE_AUDIO;
+ memset(pFrame->codec_frame.buffer,0,pFrame->codec_frame.size);
+ pRecogChannel->m_TimeToComplete -= CODEC_FRAME_TIME_BASE;
+ }
+ else
+ {
+ pRecogChannel->m_Streaming = false;
+ }
+ }
+ }
+ return TRUE;
+}
+
+RecogChannel* RecogSession::CreateRecogChannel()
+{
+ mrcp_channel_t* pChannel;
+ mpf_termination_t* pTermination;
+ mpf_stream_capabilities_t* pCapabilities;
+ apr_pool_t* pool = GetSessionPool();
+
+ /* create channel */
+ RecogChannel *pRecogChannel = new RecogChannel;
+ pRecogChannel->m_pMrcpChannel = NULL;
+ pRecogChannel->m_Streaming = false;
+ pRecogChannel->m_pAudioIn = NULL;
+ pRecogChannel->m_TimeToComplete = 0;
+
+ /* create source stream capabilities */
+ pCapabilities = mpf_source_stream_capabilities_create(pool);
+ GetScenario()->InitCapabilities(pCapabilities);
+
+ static const mpf_audio_stream_vtable_t audio_stream_vtable =
+ {
+ NULL,
+ NULL,
+ NULL,
+ ReadStream,
+ NULL,
+ NULL,
+ NULL
+ };
+
+ pTermination = CreateAudioTermination(
+ &audio_stream_vtable, /* virtual methods table of audio stream */
+ pCapabilities, /* capabilities of audio stream */
+ pRecogChannel); /* object to associate */
+
+ pChannel = CreateMrcpChannel(
+ MRCP_RECOGNIZER_RESOURCE, /* MRCP resource identifier */
+ pTermination, /* media termination, used to terminate audio stream */
+ NULL, /* RTP descriptor, used to create RTP termination (NULL by default) */
+ pRecogChannel); /* object to associate */
+ if(!pChannel)
+ {
+ delete pRecogChannel;
+ return NULL;
+ }
+
+ pRecogChannel->m_pMrcpChannel = pChannel;
+ return pRecogChannel;
+}
+
+bool RecogSession::OnChannelAdd(mrcp_channel_t* pMrcpChannel, mrcp_sig_status_code_e status)
+{
+ if(!UmcSession::OnChannelAdd(pMrcpChannel,status))
+ return false;
+
+ if(status != MRCP_SIG_STATUS_CODE_SUCCESS)
+ {
+ /* error case, just terminate the demo */
+ return Terminate();
+ }
+
+ if(GetScenario()->IsDefineGrammarEnabled())
+ {
+ mrcp_message_t* pMrcpMessage = CreateDefineGrammarRequest(pMrcpChannel);
+ if(pMrcpMessage)
+ SendMrcpRequest(pMrcpChannel,pMrcpMessage);
+ return true;
+ }
+
+ return StartRecognition(pMrcpChannel);
+}
+
+bool RecogSession::OnChannelRemove(mrcp_channel_t* pMrcpChannel, mrcp_sig_status_code_e status)
+{
+ if(!UmcSession::OnChannelRemove(pMrcpChannel,status))
+ return false;
+
+ RecogChannel* pRecogChannel = (RecogChannel*) mrcp_application_channel_object_get(pMrcpChannel);
+ if(pRecogChannel)
+ {
+ FILE* pAudioIn = pRecogChannel->m_pAudioIn;
+ if(pAudioIn)
+ {
+ pRecogChannel->m_pAudioIn = NULL;
+ fclose(pAudioIn);
+ }
+ }
+
+ /* terminate the demo */
+ return Terminate();
+}
+
+bool RecogSession::OnMessageReceive(mrcp_channel_t* pMrcpChannel, mrcp_message_t* pMrcpMessage)
+{
+ if(!UmcSession::OnMessageReceive(pMrcpChannel,pMrcpMessage))
+ return false;
+
+ RecogChannel* pRecogChannel = (RecogChannel*) mrcp_application_channel_object_get(pMrcpChannel);
+ if(pMrcpMessage->start_line.message_type == MRCP_MESSAGE_TYPE_RESPONSE)
+ {
+ /* received MRCP response */
+ if(pMrcpMessage->start_line.method_id == RECOGNIZER_DEFINE_GRAMMAR)
+ {
+ /* received the response to DEFINE-GRAMMAR request */
+ if(pMrcpMessage->start_line.request_state == MRCP_REQUEST_STATE_COMPLETE)
+ {
+ OnDefineGrammar(pMrcpChannel);
+ }
+ else
+ {
+ /* received unexpected response, remove channel */
+ RemoveMrcpChannel(pMrcpChannel);
+ }
+ }
+ else if(pMrcpMessage->start_line.method_id == RECOGNIZER_RECOGNIZE)
+ {
+ /* received the response to RECOGNIZE request */
+ if(pMrcpMessage->start_line.request_state == MRCP_REQUEST_STATE_INPROGRESS)
+ {
+ /* start to stream the speech to recognize */
+ if(pRecogChannel)
+ {
+ pRecogChannel->m_Streaming = true;
+ }
+ }
+ else
+ {
+ /* received unexpected response, remove channel */
+ RemoveMrcpChannel(pMrcpChannel);
+ }
+ }
+ else
+ {
+ /* received unexpected response */
+ }
+ }
+ else if(pMrcpMessage->start_line.message_type == MRCP_MESSAGE_TYPE_EVENT)
+ {
+ if(pMrcpMessage->start_line.method_id == RECOGNIZER_RECOGNITION_COMPLETE)
+ {
+ ParseNLSMLResult(pMrcpMessage);
+ if(pRecogChannel)
+ {
+ pRecogChannel->m_Streaming = false;
+ }
+ RemoveMrcpChannel(pMrcpChannel);
+ }
+ else if(pMrcpMessage->start_line.method_id == RECOGNIZER_START_OF_INPUT)
+ {
+ /* received start-of-input, do whatever you need here */
+ }
+ }
+ return true;
+}
+
+bool RecogSession::OnDefineGrammar(mrcp_channel_t* pMrcpChannel)
+{
+ if(GetScenario()->IsRecognizeEnabled())
+ {
+ return StartRecognition(pMrcpChannel);
+ }
+
+ return Terminate();
+}
+
+bool RecogSession::StartRecognition(mrcp_channel_t* pMrcpChannel)
+{
+ RecogChannel* pRecogChannel = (RecogChannel*) mrcp_application_channel_object_get(pMrcpChannel);
+ /* create and send RECOGNIZE request */
+ mrcp_message_t* pMrcpMessage = CreateRecognizeRequest(pMrcpChannel);
+ if(pMrcpMessage)
+ {
+ SendMrcpRequest(pRecogChannel->m_pMrcpChannel,pMrcpMessage);
+ }
+
+ const mpf_codec_descriptor_t* pDescriptor = mrcp_application_source_descriptor_get(pMrcpChannel);
+ pRecogChannel->m_pAudioIn = GetAudioIn(pDescriptor,GetSessionPool());
+ if(!pRecogChannel->m_pAudioIn)
+ {
+ /* no audio input availble, set some estimated time to complete instead */
+ pRecogChannel->m_TimeToComplete = 5000; // 5 sec
+ }
+ return true;
+}
+
+mrcp_message_t* RecogSession::CreateDefineGrammarRequest(mrcp_channel_t* pMrcpChannel)
+{
+ mrcp_message_t* pMrcpMessage = CreateMrcpMessage(pMrcpChannel,RECOGNIZER_DEFINE_GRAMMAR);
+ if(!pMrcpMessage)
+ return NULL;
+
+ const RecogScenario* pScenario = GetScenario();
+
+ mrcp_generic_header_t* pGenericHeader;
+ /* get/allocate generic header */
+ pGenericHeader = (mrcp_generic_header_t*) mrcp_generic_header_prepare(pMrcpMessage);
+ if(pGenericHeader)
+ {
+ /* set generic header fields */
+ if(pScenario->GetContentType())
+ {
+ apt_string_assign(&pGenericHeader->content_type,pScenario->GetContentType(),pMrcpMessage->pool);
+ mrcp_generic_header_property_add(pMrcpMessage,GENERIC_HEADER_CONTENT_TYPE);
+ }
+ apt_string_assign(&pGenericHeader->content_id,m_ContentId,pMrcpMessage->pool);
+ mrcp_generic_header_property_add(pMrcpMessage,GENERIC_HEADER_CONTENT_ID);
+ }
+
+ /* set message body */
+ if(pScenario->GetContent())
+ apt_string_assign(&pMrcpMessage->body,pScenario->GetContent(),pMrcpMessage->pool);
+ return pMrcpMessage;
+}
+
+mrcp_message_t* RecogSession::CreateRecognizeRequest(mrcp_channel_t* pMrcpChannel)
+{
+ mrcp_message_t* pMrcpMessage = CreateMrcpMessage(pMrcpChannel,RECOGNIZER_RECOGNIZE);
+ if(!pMrcpMessage)
+ return NULL;
+
+ const RecogScenario* pScenario = GetScenario();
+
+ mrcp_generic_header_t* pGenericHeader;
+ mrcp_recog_header_t* pRecogHeader;
+
+ /* get/allocate generic header */
+ pGenericHeader = (mrcp_generic_header_t*) mrcp_generic_header_prepare(pMrcpMessage);
+ if(pGenericHeader)
+ {
+ /* set generic header fields */
+ if(pScenario->IsDefineGrammarEnabled())
+ {
+ apt_string_assign(&pGenericHeader->content_type,"text/uri-list",pMrcpMessage->pool);
+ /* set message body */
+ const char* pContent = apr_pstrcat(pMrcpMessage->pool,"session:",m_ContentId,NULL);
+ apt_string_set(&pMrcpMessage->body,pContent);
+ }
+ else
+ {
+ apt_string_assign(&pGenericHeader->content_type,pScenario->GetContentType(),pMrcpMessage->pool);
+ /* set content-id */
+ apt_string_assign(&pGenericHeader->content_id,m_ContentId,pMrcpMessage->pool);
+ mrcp_generic_header_property_add(pMrcpMessage,GENERIC_HEADER_CONTENT_ID);
+ /* set message body */
+ if(pScenario->GetContent())
+ apt_string_assign(&pMrcpMessage->body,pScenario->GetContent(),pMrcpMessage->pool);
+ }
+ mrcp_generic_header_property_add(pMrcpMessage,GENERIC_HEADER_CONTENT_TYPE);
+ }
+ /* get/allocate recognizer header */
+ pRecogHeader = (mrcp_recog_header_t*) mrcp_resource_header_prepare(pMrcpMessage);
+ if(pRecogHeader)
+ {
+ /* set recognizer header fields */
+ if(pMrcpMessage->start_line.version == MRCP_VERSION_2)
+ {
+ pRecogHeader->cancel_if_queue = FALSE;
+ mrcp_resource_header_property_add(pMrcpMessage,RECOGNIZER_HEADER_CANCEL_IF_QUEUE);
+ }
+ pRecogHeader->no_input_timeout = 5000;
+ mrcp_resource_header_property_add(pMrcpMessage,RECOGNIZER_HEADER_NO_INPUT_TIMEOUT);
+ pRecogHeader->recognition_timeout = 10000;
+ mrcp_resource_header_property_add(pMrcpMessage,RECOGNIZER_HEADER_RECOGNITION_TIMEOUT);
+ pRecogHeader->start_input_timers = TRUE;
+ mrcp_resource_header_property_add(pMrcpMessage,RECOGNIZER_HEADER_START_INPUT_TIMERS);
+ pRecogHeader->confidence_threshold = 0.87f;
+ mrcp_resource_header_property_add(pMrcpMessage,RECOGNIZER_HEADER_CONFIDENCE_THRESHOLD);
+ }
+ return pMrcpMessage;
+}
+
+bool RecogSession::ParseNLSMLResult(mrcp_message_t* pMrcpMessage) const
+{
+ apr_xml_elem* pInterpret;
+ apr_xml_elem* pInstance;
+ apr_xml_elem* pInput;
+ apr_xml_doc* pDoc = nlsml_doc_load(&pMrcpMessage->body,pMrcpMessage->pool);
+ if(!pDoc)
+ return false;
+
+ /* walk through interpreted results */
+ pInterpret = nlsml_first_interpret_get(pDoc);
+ for(; pInterpret; pInterpret = nlsml_next_interpret_get(pInterpret))
+ {
+ /* get instance and input */
+ nlsml_interpret_results_get(pInterpret,&pInstance,&pInput);
+ if(pInstance)
+ {
+ /* process instance */
+ if(pInstance->first_cdata.first)
+ {
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Interpreted Instance [%s]",pInstance->first_cdata.first->text);
+ }
+ }
+ if(pInput)
+ {
+ /* process input */
+ if(pInput->first_cdata.first)
+ {
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Interpreted Input [%s]",pInput->first_cdata.first->text);
+ }
+ }
+ }
+ return true;
+}
+
+FILE* RecogSession::GetAudioIn(const mpf_codec_descriptor_t* pDescriptor, apr_pool_t* pool) const
+{
+ const char* pFileName = GetScenario()->GetAudioSource();
+ if(!pFileName)
+ {
+ pFileName = apr_psprintf(pool,"one-%dkHz.pcm",
+ pDescriptor ? pDescriptor->sampling_rate/1000 : 8);
+ }
+ apt_dir_layout_t* pDirLayout = GetScenario()->GetDirLayout();
+ const char* pFilePath = apt_datadir_filepath_get(pDirLayout,pFileName,pool);
+ if(!pFilePath)
+ return NULL;
+
+ FILE* pFile = fopen(pFilePath,"rb");
+ if(!pFile)
+ {
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Cannot Find [%s]",pFilePath);
+ return NULL;
+ }
+
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Set [%s] as Speech Source",pFilePath);
+ return pFile;
+}
--- /dev/null
+/*
+ * 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 <stdlib.h>
+#include "recorderscenario.h"
+#include "recordersession.h"
+
+RecorderScenario::RecorderScenario() :
+ m_Record(true),
+ m_AudioSource(NULL)
+{
+}
+
+RecorderScenario::~RecorderScenario()
+{
+}
+
+void RecorderScenario::Destroy()
+{
+}
+
+bool RecorderScenario::LoadElement(const apr_xml_elem* pElem, apr_pool_t* pool)
+{
+ if(UmcScenario::LoadElement(pElem,pool))
+ return true;
+
+ if(strcasecmp(pElem->name,"record") == 0)
+ {
+ LoadRecord(pElem,pool);
+ return true;
+ }
+
+ return false;
+}
+
+bool RecorderScenario::LoadRecord(const apr_xml_elem* pElem, apr_pool_t* pool)
+{
+ const apr_xml_attr* pAttr;
+ for(pAttr = pElem->attr; pAttr; pAttr = pAttr->next)
+ {
+ if(strcasecmp(pAttr->name,"enable") == 0)
+ {
+ m_Record = atoi(pAttr->value) > 0;
+ }
+ else if(strcasecmp(pAttr->name,"audio-source") == 0)
+ {
+ m_AudioSource = pAttr->value;
+ }
+ }
+
+ return true;
+}
+
+UmcSession* RecorderScenario::CreateSession()
+{
+ return new RecorderSession(this);
+}
--- /dev/null
+/*
+ * 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 "recordersession.h"
+#include "recorderscenario.h"
+#include "mrcp_message.h"
+#include "mrcp_generic_header.h"
+#include "mrcp_recorder_header.h"
+#include "mrcp_recorder_resource.h"
+#include "apt_log.h"
+
+struct RecorderChannel
+{
+ /** MRCP control channel */
+ mrcp_channel_t* m_pMrcpChannel;
+ /** Streaming is in-progress */
+ bool m_Streaming;
+ /** File to read audio stream from */
+ FILE* m_pAudioIn;
+};
+
+RecorderSession::RecorderSession(const RecorderScenario* pScenario) :
+ UmcSession(pScenario),
+ m_pRecorderChannel(NULL)
+{
+}
+
+RecorderSession::~RecorderSession()
+{
+}
+
+bool RecorderSession::Start()
+{
+ const RecorderScenario* pScenario = GetScenario();
+ if(!pScenario->IsRecordEnabled())
+ return false;
+
+ /* create channel and associate all the required data */
+ m_pRecorderChannel = CreateRecorderChannel();
+ if(!m_pRecorderChannel)
+ return false;
+
+ /* add channel to session (send asynchronous request) */
+ if(!AddMrcpChannel(m_pRecorderChannel->m_pMrcpChannel))
+ {
+ delete m_pRecorderChannel;
+ m_pRecorderChannel = NULL;
+ return false;
+ }
+ return true;
+}
+
+bool RecorderSession::OnSessionTerminate(mrcp_sig_status_code_e status)
+{
+ if(m_pRecorderChannel)
+ {
+ FILE* pAudioIn = m_pRecorderChannel->m_pAudioIn;
+ if(pAudioIn)
+ {
+ m_pRecorderChannel->m_pAudioIn = NULL;
+ fclose(pAudioIn);
+ }
+
+ delete m_pRecorderChannel;
+ m_pRecorderChannel = NULL;
+ }
+ return UmcSession::OnSessionTerminate(status);
+}
+
+static apt_bool_t ReadStream(mpf_audio_stream_t* pStream, mpf_frame_t* pFrame)
+{
+ RecorderChannel* pRecorderChannel = (RecorderChannel*) pStream->obj;
+ if(pRecorderChannel && pRecorderChannel->m_Streaming)
+ {
+ if(pRecorderChannel->m_pAudioIn)
+ {
+ if(fread(pFrame->codec_frame.buffer,1,pFrame->codec_frame.size,pRecorderChannel->m_pAudioIn) == pFrame->codec_frame.size)
+ {
+ /* normal read */
+ pFrame->type |= MEDIA_FRAME_TYPE_AUDIO;
+ }
+ else
+ {
+ /* file is over */
+ pRecorderChannel->m_Streaming = false;
+ }
+ }
+ }
+ return TRUE;
+}
+
+RecorderChannel* RecorderSession::CreateRecorderChannel()
+{
+ mrcp_channel_t* pChannel;
+ mpf_termination_t* pTermination;
+ mpf_stream_capabilities_t* pCapabilities;
+ apr_pool_t* pool = GetSessionPool();
+
+ /* create channel */
+ RecorderChannel *pRecorderChannel = new RecorderChannel;
+ pRecorderChannel->m_pMrcpChannel = NULL;
+ pRecorderChannel->m_Streaming = false;
+ pRecorderChannel->m_pAudioIn = NULL;
+
+ /* create source stream capabilities */
+ pCapabilities = mpf_source_stream_capabilities_create(pool);
+ GetScenario()->InitCapabilities(pCapabilities);
+
+ static const mpf_audio_stream_vtable_t audio_stream_vtable =
+ {
+ NULL,
+ NULL,
+ NULL,
+ ReadStream,
+ NULL,
+ NULL,
+ NULL
+ };
+
+ pTermination = CreateAudioTermination(
+ &audio_stream_vtable, /* virtual methods table of audio stream */
+ pCapabilities, /* capabilities of audio stream */
+ pRecorderChannel); /* object to associate */
+
+ pChannel = CreateMrcpChannel(
+ MRCP_RECORDER_RESOURCE, /* MRCP resource identifier */
+ pTermination, /* media termination, used to terminate audio stream */
+ NULL, /* RTP descriptor, used to create RTP termination (NULL by default) */
+ pRecorderChannel); /* object to associate */
+ if(!pChannel)
+ {
+ delete pRecorderChannel;
+ return NULL;
+ }
+
+ pRecorderChannel->m_pMrcpChannel = pChannel;
+ return pRecorderChannel;
+}
+
+bool RecorderSession::OnChannelAdd(mrcp_channel_t* pMrcpChannel, mrcp_sig_status_code_e status)
+{
+ if(!UmcSession::OnChannelAdd(pMrcpChannel,status))
+ return false;
+
+ if(status != MRCP_SIG_STATUS_CODE_SUCCESS)
+ {
+ /* error case, just terminate the demo */
+ return Terminate();
+ }
+
+ return StartRecorder(pMrcpChannel);
+}
+
+bool RecorderSession::OnChannelRemove(mrcp_channel_t* pMrcpChannel, mrcp_sig_status_code_e status)
+{
+ if(!UmcSession::OnChannelRemove(pMrcpChannel,status))
+ return false;
+
+ RecorderChannel* pRecorderChannel = (RecorderChannel*) mrcp_application_channel_object_get(pMrcpChannel);
+ if(pRecorderChannel)
+ {
+ FILE* pAudioIn = pRecorderChannel->m_pAudioIn;
+ if(pAudioIn)
+ {
+ pRecorderChannel->m_pAudioIn = NULL;
+ fclose(pAudioIn);
+ }
+ }
+
+ /* terminate the demo */
+ return Terminate();
+}
+
+bool RecorderSession::OnMessageReceive(mrcp_channel_t* pMrcpChannel, mrcp_message_t* pMrcpMessage)
+{
+ if(!UmcSession::OnMessageReceive(pMrcpChannel,pMrcpMessage))
+ return false;
+
+ RecorderChannel* pRecorderChannel = (RecorderChannel*) mrcp_application_channel_object_get(pMrcpChannel);
+ if(pMrcpMessage->start_line.message_type == MRCP_MESSAGE_TYPE_RESPONSE)
+ {
+ /* received MRCP response */
+ if(pMrcpMessage->start_line.method_id == RECORDER_RECORD)
+ {
+ /* received the response to RECORD request */
+ if(pMrcpMessage->start_line.request_state == MRCP_REQUEST_STATE_INPROGRESS)
+ {
+ /* start to stream the speech to record */
+ if(pRecorderChannel)
+ {
+ pRecorderChannel->m_Streaming = true;
+ }
+ }
+ else
+ {
+ /* received unexpected response, remove channel */
+ RemoveMrcpChannel(pMrcpChannel);
+ }
+ }
+ else
+ {
+ /* received unexpected response */
+ }
+ }
+ else if(pMrcpMessage->start_line.message_type == MRCP_MESSAGE_TYPE_EVENT)
+ {
+ if(pMrcpMessage->start_line.method_id == RECORDER_RECORD_COMPLETE)
+ {
+ if(pRecorderChannel)
+ {
+ pRecorderChannel->m_Streaming = false;
+ }
+ RemoveMrcpChannel(pMrcpChannel);
+ }
+ else if(pMrcpMessage->start_line.method_id == RECORDER_START_OF_INPUT)
+ {
+ /* received start-of-input, do whatever you need here */
+ }
+ }
+ return true;
+}
+
+bool RecorderSession::StartRecorder(mrcp_channel_t* pMrcpChannel)
+{
+ RecorderChannel* pRecorderChannel = (RecorderChannel*) mrcp_application_channel_object_get(pMrcpChannel);
+ /* create and send RECORD request */
+ mrcp_message_t* pMrcpMessage = CreateRecordRequest(pMrcpChannel);
+ if(pMrcpMessage)
+ {
+ SendMrcpRequest(pRecorderChannel->m_pMrcpChannel,pMrcpMessage);
+ }
+
+ const mpf_codec_descriptor_t* pDescriptor = mrcp_application_source_descriptor_get(pMrcpChannel);
+ pRecorderChannel->m_pAudioIn = GetAudioIn(pDescriptor,GetSessionPool());
+ return true;
+}
+
+mrcp_message_t* RecorderSession::CreateRecordRequest(mrcp_channel_t* pMrcpChannel)
+{
+ mrcp_message_t* pMrcpMessage = CreateMrcpMessage(pMrcpChannel,RECORDER_RECORD);
+ if(!pMrcpMessage)
+ return NULL;
+
+ mrcp_recorder_header_t* pRecorderHeader;
+
+ /* get/allocate recorder header */
+ pRecorderHeader = (mrcp_recorder_header_t*) mrcp_resource_header_prepare(pMrcpMessage);
+ if(pRecorderHeader)
+ {
+ /* set recorder header fields */
+ pRecorderHeader->no_input_timeout = 5000;
+ mrcp_resource_header_property_add(pMrcpMessage,RECORDER_HEADER_NO_INPUT_TIMEOUT);
+
+ pRecorderHeader->final_silence = 300;
+ mrcp_resource_header_property_add(pMrcpMessage,RECORDER_HEADER_FINAL_SILENCE);
+
+ pRecorderHeader->max_time = 10000;
+ mrcp_resource_header_property_add(pMrcpMessage,RECORDER_HEADER_MAX_TIME);
+ }
+ return pMrcpMessage;
+}
+
+FILE* RecorderSession::GetAudioIn(const mpf_codec_descriptor_t* pDescriptor, apr_pool_t* pool) const
+{
+ const char* pFileName = GetScenario()->GetAudioSource();
+ if(!pFileName)
+ {
+ pFileName = apr_psprintf(pool,"demo-%dkHz.pcm",
+ pDescriptor ? pDescriptor->sampling_rate/1000 : 8);
+ }
+ apt_dir_layout_t* pDirLayout = GetScenario()->GetDirLayout();
+ const char* pFilePath = apt_datadir_filepath_get(pDirLayout,pFileName,pool);
+ if(!pFilePath)
+ return NULL;
+
+ FILE* pFile = fopen(pFilePath,"rb");
+ if(!pFile)
+ {
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Cannot Find [%s]",pFilePath);
+ return NULL;
+ }
+
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Set [%s] as Speech Source",pFilePath);
+ return pFile;
+}
--- /dev/null
+/*
+ * 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 <stdlib.h>
+#include "synthscenario.h"
+#include "synthsession.h"
+#include "mrcp_message.h"
+#include "mrcp_generic_header.h"
+#include "mrcp_synth_header.h"
+#include "mrcp_synth_resource.h"
+
+
+SynthScenario::SynthScenario() :
+ m_Speak(true),
+ m_ContentType("application/synthesis+ssml"),
+ m_Content(NULL)
+{
+}
+
+SynthScenario::~SynthScenario()
+{
+}
+
+void SynthScenario::Destroy()
+{
+}
+
+bool SynthScenario::LoadElement(const apr_xml_elem* pElem, apr_pool_t* pool)
+{
+ if(UmcScenario::LoadElement(pElem,pool))
+ return true;
+
+ if(strcasecmp(pElem->name,"speak") == 0)
+ {
+ LoadSpeak(pElem,pool);
+ return true;
+ }
+
+ return false;
+}
+
+bool SynthScenario::LoadSpeak(const apr_xml_elem* pElem, apr_pool_t* pool)
+{
+ const apr_xml_attr* pAttr;
+ for(pAttr = pElem->attr; pAttr; pAttr = pAttr->next)
+ {
+ if(strcasecmp(pAttr->name,"enable") == 0)
+ {
+ m_Speak = atoi(pAttr->value) > 0;
+ }
+ else if(strcasecmp(pAttr->name,"content-type") == 0)
+ {
+ m_ContentType = pAttr->value;
+ }
+ else if(strcasecmp(pAttr->name,"content-location") == 0)
+ {
+ m_Content = LoadFileContent(pAttr->value,pool);
+ }
+ }
+
+ return true;
+}
+
+UmcSession* SynthScenario::CreateSession()
+{
+ return new SynthSession(this);
+}
--- /dev/null
+/*
+ * 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 "synthsession.h"
+#include "synthscenario.h"
+#include "mrcp_message.h"
+#include "mrcp_generic_header.h"
+#include "mrcp_synth_header.h"
+#include "mrcp_synth_resource.h"
+
+struct SynthChannel
+{
+ mrcp_channel_t* m_pMrcpChannel;
+ /** File to write audio stream to */
+ FILE* m_pAudioOut;
+};
+
+SynthSession::SynthSession(const SynthScenario* pScenario) :
+ UmcSession(pScenario),
+ m_pSynthChannel(NULL)
+{
+}
+
+SynthSession::~SynthSession()
+{
+}
+
+bool SynthSession::Start()
+{
+ if(!GetScenario()->IsSpeakEnabled())
+ return false;
+
+ /* create channel and associate all the required data */
+ m_pSynthChannel = CreateSynthChannel();
+ if(!m_pSynthChannel)
+ return false;
+
+ /* add channel to session (send asynchronous request) */
+ if(!AddMrcpChannel(m_pSynthChannel->m_pMrcpChannel))
+ {
+ delete m_pSynthChannel;
+ m_pSynthChannel = NULL;
+ return false;
+ }
+ return true;
+}
+
+bool SynthSession::OnSessionTerminate(mrcp_sig_status_code_e status)
+{
+ if(m_pSynthChannel)
+ {
+ FILE* pAudioOut = m_pSynthChannel->m_pAudioOut;
+ if(pAudioOut)
+ {
+ m_pSynthChannel->m_pAudioOut = NULL;
+ fclose(pAudioOut);
+ }
+
+ delete m_pSynthChannel;
+ m_pSynthChannel = NULL;
+ }
+ return UmcSession::OnSessionTerminate(status);
+}
+
+static apt_bool_t WriteStream(mpf_audio_stream_t* pStream, const mpf_frame_t* pFrame)
+{
+ SynthChannel* pSynthChannel = (SynthChannel*) pStream->obj;
+ if(pSynthChannel && pSynthChannel->m_pAudioOut)
+ {
+ fwrite(pFrame->codec_frame.buffer,1,pFrame->codec_frame.size,pSynthChannel->m_pAudioOut);
+ }
+ return TRUE;
+}
+
+SynthChannel* SynthSession::CreateSynthChannel()
+{
+ mrcp_channel_t* pChannel;
+ mpf_termination_t* pTermination;
+ mpf_stream_capabilities_t* pCapabilities;
+ apr_pool_t* pool = GetSessionPool();
+
+ /* create channel */
+ SynthChannel *pSynthChannel = new SynthChannel;
+ pSynthChannel->m_pMrcpChannel = NULL;
+
+ /* create sink stream capabilities */
+ pCapabilities = mpf_sink_stream_capabilities_create(pool);
+ GetScenario()->InitCapabilities(pCapabilities);
+
+ static const mpf_audio_stream_vtable_t audio_stream_vtable =
+ {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ WriteStream
+ };
+
+ pTermination = CreateAudioTermination(
+ &audio_stream_vtable, /* virtual methods table of audio stream */
+ pCapabilities, /* capabilities of audio stream */
+ pSynthChannel); /* object to associate */
+
+ pChannel = CreateMrcpChannel(
+ MRCP_SYNTHESIZER_RESOURCE, /* MRCP resource identifier */
+ pTermination, /* media termination, used to terminate audio stream */
+ NULL, /* RTP descriptor, used to create RTP termination (NULL by default) */
+ pSynthChannel); /* object to associate */
+ if(!pChannel)
+ {
+ delete pSynthChannel;
+ return NULL;
+ }
+
+ pSynthChannel->m_pMrcpChannel = pChannel;
+ pSynthChannel->m_pAudioOut = NULL;
+ return pSynthChannel;
+}
+
+bool SynthSession::OnChannelAdd(mrcp_channel_t* pMrcpChannel, mrcp_sig_status_code_e status)
+{
+ if(!UmcSession::OnChannelAdd(pMrcpChannel,status))
+ return false;
+
+ SynthChannel* pSynthChannel = (SynthChannel*) mrcp_application_channel_object_get(pMrcpChannel);
+ if(status != MRCP_SIG_STATUS_CODE_SUCCESS)
+ {
+ /* error case, just terminate the demo */
+ return Terminate();
+ }
+
+ /* create MRCP message */
+ mrcp_message_t* pMrcpMessage = CreateSpeakRequest(pMrcpChannel);
+ if(pMrcpMessage)
+ {
+ SendMrcpRequest(pSynthChannel->m_pMrcpChannel,pMrcpMessage);
+ }
+
+ const mpf_codec_descriptor_t* pDescriptor = mrcp_application_sink_descriptor_get(pMrcpChannel);
+ pSynthChannel->m_pAudioOut = GetAudioOut(pDescriptor,GetSessionPool());
+ return true;
+}
+
+bool SynthSession::OnChannelRemove(mrcp_channel_t* pMrcpChannel, mrcp_sig_status_code_e status)
+{
+ if(!UmcSession::OnChannelRemove(pMrcpChannel,status))
+ return false;
+
+ SynthChannel* pSynthChannel = (SynthChannel*) mrcp_application_channel_object_get(pMrcpChannel);
+ if(pSynthChannel)
+ {
+ FILE* pAudioOut = pSynthChannel->m_pAudioOut;
+ if(pAudioOut)
+ {
+ pSynthChannel->m_pAudioOut = NULL;
+ fclose(pAudioOut);
+ }
+ }
+
+ /* terminate the demo */
+ return Terminate();
+}
+
+bool SynthSession::OnMessageReceive(mrcp_channel_t* pMrcpChannel, mrcp_message_t* pMrcpMessage)
+{
+ if(!UmcSession::OnMessageReceive(pMrcpChannel,pMrcpMessage))
+ return false;
+
+ if(pMrcpMessage->start_line.message_type == MRCP_MESSAGE_TYPE_RESPONSE)
+ {
+ /* received MRCP response */
+ if(pMrcpMessage->start_line.method_id == SYNTHESIZER_SPEAK)
+ {
+ /* received the response to SPEAK request */
+ if(pMrcpMessage->start_line.request_state == MRCP_REQUEST_STATE_INPROGRESS)
+ {
+ /* waiting for SPEAK-COMPLETE event */
+ }
+ else
+ {
+ /* received unexpected response, remove channel */
+ RemoveMrcpChannel(pMrcpChannel);
+ }
+ }
+ else
+ {
+ /* received unexpected response */
+ }
+ }
+ else if(pMrcpMessage->start_line.message_type == MRCP_MESSAGE_TYPE_EVENT)
+ {
+ /* received MRCP event */
+ if(pMrcpMessage->start_line.method_id == SYNTHESIZER_SPEAK_COMPLETE)
+ {
+ /* received SPEAK-COMPLETE event, remove channel */
+ RemoveMrcpChannel(pMrcpChannel);
+ }
+ }
+ return true;
+}
+
+mrcp_message_t* SynthSession::CreateSpeakRequest(mrcp_channel_t* pMrcpChannel)
+{
+ mrcp_message_t* pMrcpMessage = CreateMrcpMessage(pMrcpChannel,SYNTHESIZER_SPEAK);
+ if(!pMrcpMessage)
+ return NULL;
+
+ const SynthScenario* pScenario = GetScenario();
+
+ mrcp_generic_header_t* pGenericHeader;
+ mrcp_synth_header_t* pSynthHeader;
+ /* get/allocate generic header */
+ pGenericHeader = (mrcp_generic_header_t*) mrcp_generic_header_prepare(pMrcpMessage);
+ if(pGenericHeader)
+ {
+ /* set generic header fields */
+ apt_string_assign(&pGenericHeader->content_type,pScenario->GetContentType(),pMrcpMessage->pool);
+ mrcp_generic_header_property_add(pMrcpMessage,GENERIC_HEADER_CONTENT_TYPE);
+
+ /* set message body */
+ if(pScenario->GetContent())
+ apt_string_assign(&pMrcpMessage->body,pScenario->GetContent(),pMrcpMessage->pool);
+ }
+ /* get/allocate synthesizer header */
+ pSynthHeader = (mrcp_synth_header_t*) mrcp_resource_header_prepare(pMrcpMessage);
+ if(pSynthHeader)
+ {
+ /* set synthesizer header fields */
+ pSynthHeader->voice_param.age = 28;
+ mrcp_resource_header_property_add(pMrcpMessage,SYNTHESIZER_HEADER_VOICE_AGE);
+ }
+
+ return pMrcpMessage;
+}
+
+FILE* SynthSession::GetAudioOut(const mpf_codec_descriptor_t* pDescriptor, apr_pool_t* pool) const
+{
+ char* pFileName = apr_psprintf(pool,"synth-%dkHz-%s.pcm",
+ pDescriptor ? pDescriptor->sampling_rate/1000 : 8, GetMrcpSessionId());
+ apt_dir_layout_t* pDirLayout = GetScenario()->GetDirLayout();
+ char* pFilePath = apt_datadir_filepath_get(pDirLayout,pFileName,pool);
+ if(!pFilePath)
+ return NULL;
+
+ return fopen(pFilePath,"wb");
+}
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <apr_getopt.h>
+#include "umcconsole.h"
+#include "umcframework.h"
+#include "apt_pool.h"
+
+
+UmcConsole::UmcConsole() :
+ m_pFramework(NULL)
+{
+ m_pFramework = new UmcFramework;
+}
+
+UmcConsole::~UmcConsole()
+{
+ delete m_pFramework;
+}
+
+bool UmcConsole::Run(int argc, const char * const *argv)
+{
+ apr_pool_t* pool = NULL;
+ apt_dir_layout_t* pDirLayout = NULL;
+
+ /* APR global initialization */
+ if(apr_initialize() != APR_SUCCESS)
+ {
+ apr_terminate();
+ return false;
+ }
+
+ /* create APR pool */
+ pool = apt_pool_create();
+ if(!pool)
+ {
+ apr_terminate();
+ return false;
+ }
+
+ /* load options */
+ if(!LoadOptions(argc,argv,pool))
+ {
+ apr_pool_destroy(pool);
+ apr_terminate();
+ return false;
+ }
+
+ /* create the structure of default directories layout */
+ pDirLayout = apt_default_dir_layout_create(m_Options.m_RootDirPath,pool);
+ /* create singleton logger */
+ apt_log_instance_create(m_Options.m_LogOutput,m_Options.m_LogPriority,pool);
+
+ if((m_Options.m_LogOutput & APT_LOG_OUTPUT_FILE) == APT_LOG_OUTPUT_FILE)
+ {
+ /* open the log file */
+ apt_log_file_open(pDirLayout->log_dir_path,"unimrcpclient",MAX_LOG_FILE_SIZE,MAX_LOG_FILE_COUNT,pool);
+ }
+
+ /* create demo framework */
+ if(m_pFramework->Create(pDirLayout,pool))
+ {
+ /* run command line */
+ RunCmdLine();
+ /* destroy demo framework */
+ m_pFramework->Destroy();
+ }
+
+ /* destroy singleton logger */
+ apt_log_instance_destroy();
+ /* destroy APR pool */
+ apr_pool_destroy(pool);
+ /* APR global termination */
+ apr_terminate();
+ return true;
+}
+
+bool UmcConsole::ProcessCmdLine(char* pCmdLine)
+{
+ bool running = true;
+ char *name;
+ char *last;
+ name = apr_strtok(pCmdLine, " ", &last);
+
+ if(strcasecmp(name,"run") == 0)
+ {
+ char* pScenarioName = apr_strtok(NULL, " ", &last);
+ if(pScenarioName)
+ {
+ char* pProfileName = apr_strtok(NULL, " ", &last);
+ if(!pProfileName)
+ {
+ pProfileName = "MRCPv2-Default";
+ }
+ m_pFramework->RunSession(pScenarioName,pProfileName);
+ }
+ }
+ else if(strcasecmp(name,"kill") == 0)
+ {
+ char* pID = apr_strtok(NULL, " ", &last);
+ if(pID)
+ {
+ m_pFramework->KillSession(pID);
+ }
+ }
+ else if(strcasecmp(name,"show") == 0)
+ {
+ char* pWhat = apr_strtok(NULL, " ", &last);
+ if(pWhat)
+ {
+ if(strcasecmp(pWhat,"sessions") == 0)
+ m_pFramework->ShowSessions();
+ else if(strcasecmp(pWhat,"scenarios") == 0)
+ m_pFramework->ShowScenarios();
+ }
+ }
+ else if(strcasecmp(name,"loglevel") == 0)
+ {
+ char* pPriority = apr_strtok(NULL, " ", &last);
+ if(pPriority)
+ {
+ apt_log_priority_set((apt_log_priority_e)atol(pPriority));
+ }
+ }
+ else if(strcasecmp(name,"exit") == 0 || strcmp(name,"quit") == 0)
+ {
+ running = false;
+ }
+ else if(strcasecmp(name,"help") == 0)
+ {
+ printf("usage:\n"
+ "\n- run [scenario] [profile] (run new session)\n"
+ " scenario is one of 'synth', 'recog', ... (use 'show scenarios')\n"
+ " profile is one of 'MRCPv2-Default', 'MRCPv1-Default', ... (see unimrcpclient.xml)\n"
+ "\n examples: \n"
+ " run synth\n"
+ " run recog\n"
+ " run synth MRCPv1-Default\n"
+ " run recog MRCPv1-Default\n"
+ "\n- kill [id] (kill session)\n"
+ " id is a session identifier: 1, 2, ... (use 'show sessions')\n"
+ "\n example: \n"
+ " kill 1\n"
+ "\n- show [what] (show either available scenarios or in-progress sessions)\n"
+ "\n examples: \n"
+ " show scenarios\n"
+ " show sessions\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;
+}
+
+bool UmcConsole::RunCmdLine()
+{
+ 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 = ProcessCmdLine(cmdline);
+ }
+ }
+ while(running != 0);
+ return true;
+}
+
+void UmcConsole::Usage() const
+{
+ printf(
+ "\n"
+ "Usage:\n"
+ "\n"
+ " umc [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");
+}
+
+bool UmcConsole::LoadOptions(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 */
+ };
+
+ /* set the default options */
+ m_Options.m_RootDirPath = "../";
+ m_Options.m_LogPriority = APT_PRIO_INFO;
+ m_Options.m_LogOutput = APT_LOG_OUTPUT_CONSOLE;
+
+ 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':
+ m_Options.m_RootDirPath = optarg;
+ break;
+ case 'l':
+ if(optarg)
+ {
+ m_Options.m_LogPriority = (apt_log_priority_e) atoi(optarg);
+ }
+ break;
+ case 'o':
+ if(optarg)
+ {
+ m_Options.m_LogOutput = (apt_log_output_e) atoi(optarg);
+ }
+ break;
+ case 'h':
+ Usage();
+ return FALSE;
+ }
+ }
+
+ if(rv != APR_EOF)
+ {
+ Usage();
+ return false;
+ }
+
+ return true;
+}
--- /dev/null
+/*
+ * 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 "umcframework.h"
+#include "umcsession.h"
+#include "synthscenario.h"
+#include "recogscenario.h"
+#include "recorderscenario.h"
+#include "dtmfscenario.h"
+#include "unimrcp_client.h"
+#include "apt_log.h"
+
+typedef struct
+{
+ char m_SessionId[10];
+ char m_ScenarioName[128];
+ char m_ProfileName[128];
+ const mrcp_app_message_t* m_pAppMessage;
+} UmcTaskMsg;
+
+enum UmcTaskMsgType
+{
+ UMC_TASK_CLIENT_MSG,
+ UMC_TASK_RUN_SESSION_MSG,
+ UMC_TASK_KILL_SESSION_MSG,
+ UMC_TASK_SHOW_SCENARIOS_MSG,
+ UMC_TASK_SHOW_SESSIONS_MSG
+};
+
+apt_bool_t UmcProcessMsg(apt_task_t* pTask, apt_task_msg_t* pMsg);
+void UmcOnStartComplete(apt_task_t* pTask);
+void UmcOnTerminateComplete(apt_task_t* pTask);
+apt_bool_t AppMessageHandler(const mrcp_app_message_t* pAppMessage);
+
+
+UmcFramework::UmcFramework() :
+ m_pPool(NULL),
+ m_pDirLayout(NULL),
+ m_pTask(NULL),
+ m_pMrcpClient(NULL),
+ m_pMrcpApplication(NULL),
+ m_Ready(false),
+ m_pScenarioTable(NULL),
+ m_pSessionTable(NULL)
+{
+}
+
+UmcFramework::~UmcFramework()
+{
+}
+
+bool UmcFramework::Create(apt_dir_layout_t* pDirLayout, apr_pool_t* pool)
+{
+ m_pDirLayout = pDirLayout;
+ m_pPool = pool;
+
+ m_pSessionTable = apr_hash_make(m_pPool);
+ m_pScenarioTable = apr_hash_make(m_pPool);
+ if(!CreateTask())
+ return false;
+
+ /* wait for READY state,
+ preferably cond wait object should be used */
+ int attempts = 0;
+ while(!m_Ready && attempts < 10)
+ {
+ attempts++;
+ apr_sleep(500000);
+ }
+
+ return true;
+}
+
+void UmcFramework::Destroy()
+{
+ DestroyTask();
+
+ m_pScenarioTable = NULL;
+ m_pSessionTable = NULL;
+}
+
+bool UmcFramework::CreateMrcpClient()
+{
+ /* create MRCP client stack first */
+ m_pMrcpClient = unimrcp_client_create(m_pDirLayout);
+ if(!m_pMrcpClient)
+ return false;
+
+ /* create MRCP application to send/get requests to/from MRCP client stack */
+ m_pMrcpApplication = mrcp_application_create(AppMessageHandler,this,m_pPool);
+ if(!m_pMrcpApplication)
+ {
+ mrcp_client_destroy(m_pMrcpClient);
+ m_pMrcpClient = NULL;
+ return false;
+ }
+
+ /* register MRCP application to MRCP client */
+ mrcp_client_application_register(m_pMrcpClient,m_pMrcpApplication,"UMC");
+ /* start MRCP client stack processing */
+ if(mrcp_client_start(m_pMrcpClient) == FALSE)
+ {
+ mrcp_client_destroy(m_pMrcpClient);
+ m_pMrcpClient = NULL;
+ m_pMrcpApplication = NULL;
+ return false;
+ }
+ return true;
+}
+
+void UmcFramework::DestroyMrcpClient()
+{
+ if(m_pMrcpClient)
+ {
+ /* shutdown MRCP client stack processing first (blocking call) */
+ mrcp_client_shutdown(m_pMrcpClient);
+ /* destroy MRCP client stack */
+ mrcp_client_destroy(m_pMrcpClient);
+ m_pMrcpClient = NULL;
+ m_pMrcpApplication = NULL;
+ }
+}
+
+bool UmcFramework::CreateTask()
+{
+ apt_task_t* pTask;
+ apt_task_vtable_t* pVtable;
+ apt_task_msg_pool_t* pMsgPool;
+
+ pMsgPool = apt_task_msg_pool_create_dynamic(sizeof(UmcTaskMsg),m_pPool);
+ m_pTask = apt_consumer_task_create(this,pMsgPool,m_pPool);
+ if(!m_pTask)
+ return false;
+
+ pTask = apt_consumer_task_base_get(m_pTask);
+ apt_task_name_set(pTask,"Framework Task");
+ pVtable = apt_consumer_task_vtable_get(m_pTask);
+ if(pVtable)
+ {
+ pVtable->process_msg = UmcProcessMsg;
+ pVtable->on_start_complete = UmcOnStartComplete;
+ pVtable->on_terminate_complete = UmcOnTerminateComplete;
+ }
+
+ m_Ready = false;
+ apt_task_start(pTask);
+ return true;
+}
+
+void UmcFramework::DestroyTask()
+{
+ if(m_pTask)
+ {
+ apt_task_t* pTask = apt_consumer_task_base_get(m_pTask);
+ if(pTask)
+ {
+ apt_task_terminate(pTask,TRUE);
+ apt_task_destroy(pTask);
+ }
+ m_pTask = NULL;
+ }
+}
+
+UmcScenario* UmcFramework::CreateScenario(const char* pType)
+{
+ if(pType)
+ {
+ if(strcasecmp(pType,"Synthesizer") == 0)
+ return new SynthScenario();
+ else if(strcasecmp(pType,"Recognizer") == 0)
+ return new RecogScenario();
+ else if(strcasecmp(pType,"Recorder") == 0)
+ return new RecorderScenario();
+ else if(strcasecmp(pType,"DtmfRecognizer") == 0)
+ return new DtmfScenario();
+ }
+ return NULL;
+}
+
+apr_xml_doc* UmcFramework::LoadDocument()
+{
+ apr_xml_parser* pParser = NULL;
+ apr_xml_doc* pDoc = NULL;
+ apr_file_t* pFD = NULL;
+ apr_status_t rv;
+ const char* pFilePath;
+
+ pFilePath = apr_psprintf(m_pPool,"%s/%s",m_pDirLayout->conf_dir_path,"umcscenarios.xml");
+
+ apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Open Config File [%s]",pFilePath);
+ rv = apr_file_open(&pFD,pFilePath,APR_READ|APR_BINARY,0,m_pPool);
+ if(rv != APR_SUCCESS)
+ {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Open Config File [%s]",pFilePath);
+ return NULL;
+ }
+
+ rv = apr_xml_parse_file(m_pPool,&pParser,&pDoc,pFD,2000);
+ if(rv != APR_SUCCESS)
+ {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Parse Config File [%s]",pFilePath);
+ pDoc = NULL;
+ }
+
+ apr_file_close(pFD);
+ return pDoc;
+}
+
+bool UmcFramework::LoadScenarios()
+{
+ apr_xml_doc* pDoc = LoadDocument();
+ if(!pDoc)
+ return false;
+
+ const apr_xml_attr* pAttr;
+ const apr_xml_elem* pElem;
+ const apr_xml_elem* pRoot = pDoc->root;
+ if(!pRoot || strcasecmp(pRoot->name,"umcscenarios") != 0)
+ {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Document");
+ return FALSE;
+ }
+ for(pElem = pRoot->first_child; pElem; pElem = pElem->next)
+ {
+ if(strcasecmp(pElem->name,"scenario") != 0)
+ {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Element <%s>",pElem->name);
+ continue;
+ }
+
+ const char* pName = NULL;
+ const char* pClass = NULL;
+ const char* pMrcpProfile = NULL;
+ for(pAttr = pElem->attr; pAttr; pAttr = pAttr->next)
+ {
+ if(strcasecmp(pAttr->name,"name") == 0)
+ {
+ pName = pAttr->value;
+ }
+ else if(strcasecmp(pAttr->name,"class") == 0)
+ {
+ pClass = pAttr->value;
+ }
+ else if(strcasecmp(pAttr->name,"profile") == 0)
+ {
+ pMrcpProfile = pAttr->value;
+ }
+ }
+
+ if(pName && pClass)
+ {
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Load Scenario name [%s] class [%s]",pName,pClass);
+ UmcScenario* pScenario = CreateScenario(pClass);
+ if(pScenario)
+ {
+ pScenario->SetDirLayout(m_pDirLayout);
+ pScenario->SetName(pName);
+ pScenario->SetMrcpProfile(pMrcpProfile);
+ if(pScenario->Load(pElem,m_pPool))
+ apr_hash_set(m_pScenarioTable,pScenario->GetName(),APR_HASH_KEY_STRING,pScenario);
+ else
+ delete pScenario;
+ }
+ else
+ {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No such scenario <%s>",pClass);
+ }
+ }
+ else
+ {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Missing either name or class of the scenario");
+ }
+ }
+ return true;
+}
+
+void UmcFramework::DestroyScenarios()
+{
+ UmcScenario* pScenario;
+ void* pVal;
+ apr_hash_index_t* it = apr_hash_first(m_pPool,m_pScenarioTable);
+ for(; it; it = apr_hash_next(it))
+ {
+ apr_hash_this(it,NULL,NULL,&pVal);
+ pScenario = (UmcScenario*) pVal;
+ if(pScenario)
+ {
+ pScenario->Destroy();
+ delete pScenario;
+ }
+ }
+ apr_hash_clear(m_pScenarioTable);
+}
+
+bool UmcFramework::AddSession(UmcSession* pSession)
+{
+ if(!pSession)
+ return false;
+
+ apr_hash_set(m_pSessionTable,pSession->GetId(),APR_HASH_KEY_STRING,pSession);
+ return true;
+}
+
+bool UmcFramework::RemoveSession(UmcSession* pSession)
+{
+ if(!pSession)
+ return false;
+
+ apr_hash_set(m_pSessionTable,pSession->GetId(),APR_HASH_KEY_STRING,NULL);
+ return true;
+}
+
+bool UmcFramework::ProcessRunRequest(const char* pScenarioName, const char* pProfileName)
+{
+ UmcScenario* pScenario = (UmcScenario*) apr_hash_get(m_pScenarioTable,pScenarioName,APR_HASH_KEY_STRING);
+ if(!pScenario)
+ return false;
+
+ UmcSession* pSession = pScenario->CreateSession();
+ if(!pSession)
+ return false;
+
+ printf("[%s]\n",pSession->GetId());
+ pSession->SetMrcpProfile(pProfileName);
+ pSession->SetMrcpApplication(m_pMrcpApplication);
+ if(!pSession->Run())
+ {
+ delete pSession;
+ return false;
+ }
+
+ AddSession(pSession);
+ return true;
+}
+
+void UmcFramework::ProcessKillRequest(const char* id)
+{
+ UmcSession* pSession;
+ void* pVal;
+ apr_hash_index_t* it = apr_hash_first(m_pPool,m_pSessionTable);
+ for(; it; it = apr_hash_next(it))
+ {
+ apr_hash_this(it,NULL,NULL,&pVal);
+ pSession = (UmcSession*) pVal;
+ if(pSession && strcasecmp(pSession->GetId(),id) == 0)
+ {
+ /* first, terminate session */
+ pSession->Terminate();
+ return;
+ }
+ }
+}
+
+void UmcFramework::ProcessShowScenarios()
+{
+ UmcScenario* pScenario;
+ void* pVal;
+ printf("%d Scenario(s)\n", apr_hash_count(m_pScenarioTable));
+ apr_hash_index_t* it = apr_hash_first(m_pPool,m_pScenarioTable);
+ for(; it; it = apr_hash_next(it))
+ {
+ apr_hash_this(it,NULL,NULL,&pVal);
+ pScenario = (UmcScenario*) pVal;
+ if(pScenario)
+ {
+ printf("[%s]\n", pScenario->GetName());
+ }
+ }
+}
+
+void UmcFramework::ProcessShowSessions()
+{
+ UmcSession* pSession;
+ void* pVal;
+ printf("%d Session(s)\n", apr_hash_count(m_pSessionTable));
+ apr_hash_index_t* it = apr_hash_first(m_pPool,m_pSessionTable);
+ for(; it; it = apr_hash_next(it))
+ {
+ apr_hash_this(it,NULL,NULL,&pVal);
+ pSession = (UmcSession*) pVal;
+ if(pSession)
+ {
+ printf("[%s] - %s\n", pSession->GetId(), pSession->GetScenario()->GetName());
+ }
+ }
+}
+
+void UmcFramework::RunSession(const char* pScenarioName, const char* pProfileName)
+{
+ apt_task_t* pTask = apt_consumer_task_base_get(m_pTask);
+ apt_task_msg_t* pTaskMsg = apt_task_msg_get(pTask);
+ if(!pTaskMsg)
+ return;
+
+ pTaskMsg->type = TASK_MSG_USER;
+ pTaskMsg->sub_type = UMC_TASK_RUN_SESSION_MSG;
+ UmcTaskMsg* pUmcMsg = (UmcTaskMsg*) pTaskMsg->data;
+ strncpy(pUmcMsg->m_ScenarioName,pScenarioName,sizeof(pUmcMsg->m_ScenarioName)-1);
+ strncpy(pUmcMsg->m_ProfileName,pProfileName,sizeof(pUmcMsg->m_ProfileName)-1);
+ pUmcMsg->m_pAppMessage = NULL;
+ apt_task_msg_signal(pTask,pTaskMsg);
+}
+
+void UmcFramework::KillSession(const char* id)
+{
+ apt_task_t* pTask = apt_consumer_task_base_get(m_pTask);
+ apt_task_msg_t* pTaskMsg = apt_task_msg_get(pTask);
+ if(!pTaskMsg)
+ return;
+
+ pTaskMsg->type = TASK_MSG_USER;
+ pTaskMsg->sub_type = UMC_TASK_KILL_SESSION_MSG;
+
+ UmcTaskMsg* pUmcMsg = (UmcTaskMsg*) pTaskMsg->data;
+ strncpy(pUmcMsg->m_SessionId,id,sizeof(pUmcMsg->m_SessionId)-1);
+ pUmcMsg->m_pAppMessage = NULL;
+ apt_task_msg_signal(pTask,pTaskMsg);
+}
+
+void UmcFramework::ShowScenarios()
+{
+ apt_task_t* pTask = apt_consumer_task_base_get(m_pTask);
+ apt_task_msg_t* pTaskMsg = apt_task_msg_get(pTask);
+ if(!pTaskMsg)
+ return;
+
+ pTaskMsg->type = TASK_MSG_USER;
+ pTaskMsg->sub_type = UMC_TASK_SHOW_SCENARIOS_MSG;
+ apt_task_msg_signal(pTask,pTaskMsg);
+}
+
+void UmcFramework::ShowSessions()
+{
+ apt_task_t* pTask = apt_consumer_task_base_get(m_pTask);
+ apt_task_msg_t* pTaskMsg = apt_task_msg_get(pTask);
+ if(!pTaskMsg)
+ return;
+
+ pTaskMsg->type = TASK_MSG_USER;
+ pTaskMsg->sub_type = UMC_TASK_SHOW_SESSIONS_MSG;
+ apt_task_msg_signal(pTask,pTaskMsg);
+}
+
+apt_bool_t AppMessageHandler(const mrcp_app_message_t* pMessage)
+{
+ UmcFramework* pFramework = (UmcFramework*) mrcp_application_object_get(pMessage->application);
+ if(!pFramework)
+ return FALSE;
+
+ apt_task_t* pTask = apt_consumer_task_base_get(pFramework->m_pTask);
+ apt_task_msg_t* pTaskMsg = apt_task_msg_get(pTask);
+ if(pTaskMsg)
+ {
+ pTaskMsg->type = TASK_MSG_USER;
+ pTaskMsg->sub_type = UMC_TASK_CLIENT_MSG;
+
+ UmcTaskMsg* pUmcMsg = (UmcTaskMsg*) pTaskMsg->data;
+ pUmcMsg->m_pAppMessage = pMessage;
+ apt_task_msg_signal(pTask,pTaskMsg);
+ }
+
+ return TRUE;
+}
+
+
+apt_bool_t AppOnSessionUpdate(mrcp_application_t *application, mrcp_session_t *session, mrcp_sig_status_code_e status)
+{
+ UmcSession* pSession = (UmcSession*) mrcp_application_session_object_get(session);
+ return pSession->OnSessionUpdate(status);
+}
+
+apt_bool_t AppOnSessionTerminate(mrcp_application_t *application, mrcp_session_t *session, mrcp_sig_status_code_e status)
+{
+ UmcSession* pSession = (UmcSession*) mrcp_application_session_object_get(session);
+ if(!pSession->OnSessionTerminate(status))
+ return false;
+
+ UmcFramework* pFramework = (UmcFramework*) mrcp_application_object_get(application);
+ pFramework->RemoveSession(pSession);
+ delete pSession;
+ return true;
+}
+
+apt_bool_t AppOnChannelAdd(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_sig_status_code_e status)
+{
+ UmcSession* pSession = (UmcSession*) mrcp_application_session_object_get(session);
+ return pSession->OnChannelAdd(channel,status);
+}
+
+apt_bool_t AppOnChannelRemove(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_sig_status_code_e status)
+{
+ UmcSession* pSession = (UmcSession*) mrcp_application_session_object_get(session);
+ return pSession->OnChannelRemove(channel,status);
+}
+
+apt_bool_t AppOnMessageReceive(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_message_t *message)
+{
+ UmcSession* pSession = (UmcSession*) mrcp_application_session_object_get(session);
+ return pSession->OnMessageReceive(channel,message);
+}
+
+apt_bool_t AppOnTerminateEvent(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel)
+{
+ UmcSession* pSession = (UmcSession*) mrcp_application_session_object_get(session);
+ return pSession->OnTerminateEvent(channel);
+}
+
+apt_bool_t AppOnResourceDiscover(mrcp_application_t *application, mrcp_session_t *session, mrcp_session_descriptor_t *descriptor, mrcp_sig_status_code_e status)
+{
+ UmcSession* pSession = (UmcSession*) mrcp_application_session_object_get(session);
+ return pSession->OnResourceDiscover(descriptor,status);
+}
+
+apt_bool_t AppOnReady(mrcp_application_t *application, mrcp_sig_status_code_e status)
+{
+ UmcFramework* pFramework = (UmcFramework*) mrcp_application_object_get(application);
+ pFramework->m_Ready = true;
+ return TRUE;
+}
+
+void UmcOnStartComplete(apt_task_t* pTask)
+{
+ apt_consumer_task_t* pConsumerTask = (apt_consumer_task_t*) apt_task_object_get(pTask);
+ UmcFramework* pFramework = (UmcFramework*) apt_consumer_task_object_get(pConsumerTask);
+
+ pFramework->CreateMrcpClient();
+ pFramework->LoadScenarios();
+}
+
+void UmcOnTerminateComplete(apt_task_t* pTask)
+{
+ apt_consumer_task_t* pConsumerTask = (apt_consumer_task_t*) apt_task_object_get(pTask);
+ UmcFramework* pFramework = (UmcFramework*) apt_consumer_task_object_get(pConsumerTask);
+
+ pFramework->DestroyMrcpClient();
+ pFramework->DestroyScenarios();
+}
+
+apt_bool_t UmcProcessMsg(apt_task_t *pTask, apt_task_msg_t *pMsg)
+{
+ if(pMsg->type != TASK_MSG_USER)
+ return FALSE;
+
+ apt_consumer_task_t* pConsumerTask = (apt_consumer_task_t*) apt_task_object_get(pTask);
+ UmcFramework* pFramework = (UmcFramework*) apt_consumer_task_object_get(pConsumerTask);
+ UmcTaskMsg* pUmcMsg = (UmcTaskMsg*) pMsg->data;
+ switch(pMsg->sub_type)
+ {
+ case UMC_TASK_CLIENT_MSG:
+ {
+ static const mrcp_app_message_dispatcher_t applicationDispatcher =
+ {
+ AppOnSessionUpdate,
+ AppOnSessionTerminate,
+ AppOnChannelAdd,
+ AppOnChannelRemove,
+ AppOnMessageReceive,
+ AppOnReady,
+ AppOnTerminateEvent,
+ AppOnResourceDiscover
+ };
+
+ mrcp_application_message_dispatch(&applicationDispatcher,pUmcMsg->m_pAppMessage);
+ break;
+ }
+ case UMC_TASK_RUN_SESSION_MSG:
+ {
+ if(pFramework->m_Ready)
+ pFramework->ProcessRunRequest(pUmcMsg->m_ScenarioName,pUmcMsg->m_ProfileName);
+ break;
+ }
+ case UMC_TASK_KILL_SESSION_MSG:
+ {
+ if(pFramework->m_Ready)
+ pFramework->ProcessKillRequest(pUmcMsg->m_SessionId);
+ break;
+ }
+ case UMC_TASK_SHOW_SCENARIOS_MSG:
+ {
+ if(pFramework->m_Ready)
+ pFramework->ProcessShowScenarios();
+ break;
+ }
+ case UMC_TASK_SHOW_SESSIONS_MSG:
+ {
+ if(pFramework->m_Ready)
+ pFramework->ProcessShowSessions();
+ break;
+ }
+ }
+ return TRUE;
+}
--- /dev/null
+/*
+ * 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 <stdlib.h>
+#include "umcscenario.h"
+
+UmcScenario::UmcScenario() :
+ m_pName(NULL),
+ m_pMrcpProfile("MRCPv2-Default"),
+ m_pDirLayout(NULL),
+ m_ResourceDiscovery(false),
+ m_pCapabilities(NULL),
+ m_pRtpDescriptor(NULL)
+{
+}
+
+UmcScenario::~UmcScenario()
+{
+}
+
+bool UmcScenario::Load(const apr_xml_elem* pElem, apr_pool_t* pool)
+{
+ const apr_xml_elem* pChildElem;
+ /* Load Child Elements */
+ for(pChildElem = pElem->first_child; pChildElem; pChildElem = pChildElem->next)
+ {
+ LoadElement(pChildElem,pool);
+ }
+ return true;
+}
+
+void UmcScenario::Destroy()
+{
+}
+
+bool UmcScenario::LoadElement(const apr_xml_elem* pElem, apr_pool_t* pool)
+{
+ if(strcasecmp(pElem->name,"resource-discovery") == 0)
+ {
+ LoadDiscovery(pElem,pool);
+ return true;
+ }
+ else if(strcasecmp(pElem->name,"termination") == 0)
+ {
+ LoadTermination(pElem,pool);
+ return true;
+ }
+ else if(strcasecmp(pElem->name,"rtp-termination") == 0)
+ {
+ LoadRtpTermination(pElem,pool);
+ return true;
+ }
+
+ return false;
+}
+
+bool UmcScenario::LoadDiscovery(const apr_xml_elem* pElem, apr_pool_t* pool)
+{
+ m_ResourceDiscovery = IsElementEnabled(pElem);
+ return true;
+}
+
+bool UmcScenario::LoadTermination(const apr_xml_elem* pElem, apr_pool_t* pool)
+{
+ if(!IsElementEnabled(pElem))
+ return true;
+
+ const apr_xml_elem* pChildElem;
+ /* Load Child Elements */
+ for(pChildElem = pElem->first_child; pChildElem; pChildElem = pChildElem->next)
+ {
+ if(strcasecmp(pChildElem->name,"capabilities") == 0)
+ return LoadCapabilities(pChildElem,pool);
+ }
+ return true;
+}
+
+bool UmcScenario::LoadCapabilities(const apr_xml_elem* pElem, apr_pool_t* pool)
+{
+ const apr_xml_elem* pChildElem;
+ /* Load Child Elements */
+ m_pCapabilities = (mpf_codec_capabilities_t*) apr_palloc(pool,sizeof(mpf_codec_capabilities_t*));
+ mpf_codec_capabilities_init(m_pCapabilities,1,pool);
+ for(pChildElem = pElem->first_child; pChildElem; pChildElem = pChildElem->next)
+ {
+ if(strcasecmp(pChildElem->name,"codec") != 0)
+ continue;
+
+ const char* pName = NULL;
+ const char* pRates = NULL;
+ const apr_xml_attr* pAttr;
+ for(pAttr = pChildElem->attr; pAttr; pAttr = pAttr->next)
+ {
+ if(strcasecmp(pAttr->name,"name") == 0)
+ {
+ pName = pAttr->value;
+ }
+ else if(strcasecmp(pAttr->name,"rates") == 0)
+ {
+ pRates = pAttr->value;
+ }
+ }
+
+ if(pName)
+ {
+ int rates = ParseRates(pRates,pool);
+ mpf_codec_capabilities_add(m_pCapabilities,rates,pName);
+ }
+ }
+ return true;
+}
+
+int UmcScenario::ParseRates(const char* pStr, apr_pool_t* pool) const
+{
+ int rates = 0;
+ if(pStr)
+ {
+ char* pRateStr;
+ char* pState;
+ char* pRateListStr = apr_pstrdup(pool,pStr);
+ do
+ {
+ pRateStr = apr_strtok(pRateListStr, " ", &pState);
+ if(pRateStr)
+ {
+ apr_uint16_t rate = (apr_uint16_t)atoi(pRateStr);
+ rates |= mpf_sample_rate_mask_get(rate);
+ }
+ pRateListStr = NULL; /* make sure we pass NULL on subsequent calls of apr_strtok() */
+ }
+ while(pRateStr);
+ }
+ return rates;
+}
+
+bool UmcScenario::LoadRtpTermination(const apr_xml_elem* pElem, apr_pool_t* pool)
+{
+ return true;
+}
+
+bool UmcScenario::InitCapabilities(mpf_stream_capabilities_t* pCapabilities) const
+{
+ if(m_pCapabilities)
+ {
+ int i;
+ mpf_codec_attribs_t *pAttribs;
+ for(i=0; i<m_pCapabilities->attrib_arr->nelts; i++)
+ {
+ pAttribs = &APR_ARRAY_IDX(m_pCapabilities->attrib_arr,i,mpf_codec_attribs_t);
+ mpf_codec_capabilities_add(
+ &pCapabilities->codecs,
+ pAttribs->sample_rates,
+ pAttribs->name.buf);
+ }
+ }
+ else
+ {
+ /* add default codec capabilities (Linear PCM) */
+ mpf_codec_capabilities_add(
+ &pCapabilities->codecs,
+ MPF_SAMPLE_RATE_8000 | MPF_SAMPLE_RATE_16000,
+ "LPCM");
+ }
+
+ return true;
+}
+
+bool UmcScenario::IsElementEnabled(const apr_xml_elem* pElem) const
+{
+ const apr_xml_attr* pAttr;
+ for(pAttr = pElem->attr; pAttr; pAttr = pAttr->next)
+ {
+ if(strcasecmp(pAttr->name,"enable") == 0)
+ {
+ return atoi(pAttr->value) > 0;
+ }
+ }
+ return true;
+}
+
+const char* UmcScenario::LoadFileContent(const char* pFileName, apr_pool_t* pool) const
+{
+ if(!m_pDirLayout || !pFileName)
+ return NULL;
+
+ char* pFilePath = apt_datadir_filepath_get(m_pDirLayout,pFileName,pool);
+ if(!pFilePath)
+ return NULL;
+
+ FILE* pFile = fopen(pFilePath,"r");
+ if(!pFile)
+ return NULL;
+
+ char text[1024];
+ apr_size_t size;
+ size = fread(text,1,sizeof(text)-1,pFile);
+ text[size] = '\0';
+ fclose(pFile);
+ return apr_pstrdup(pool,text);
+}
--- /dev/null
+/*
+ * 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 "umcsession.h"
+#include "umcscenario.h"
+
+UmcSession::UmcSession(const UmcScenario* pScenario) :
+ m_pScenario(pScenario),
+ m_pMrcpProfile(NULL),
+ m_pMrcpApplication(NULL),
+ m_pMrcpSession(NULL),
+ m_Running(false),
+ m_Terminating(false)
+{
+ static int id = 0;
+ if(id == INT_MAX)
+ id = 0;
+ id++;
+
+ int size = apr_snprintf(m_Id,sizeof(m_Id)-1,"%d",id);
+ m_Id[size] = '\0';
+}
+
+UmcSession::~UmcSession()
+{
+}
+
+bool UmcSession::Run()
+{
+ if(m_Running)
+ return false;
+
+ if(!m_pMrcpProfile)
+ m_pMrcpProfile = m_pScenario->GetMrcpProfile();
+
+ if(!m_pMrcpProfile || !m_pMrcpApplication)
+ return false;
+
+ /* create session */
+ if(!CreateMrcpSession(m_pMrcpProfile))
+ return false;
+
+ m_Running = true;
+
+ bool ret = false;
+ if(m_pScenario->IsDiscoveryEnabled())
+ ret = ResourceDiscover();
+ else
+ ret = Start();
+
+ if(!ret)
+ {
+ m_Running = false;
+ DestroyMrcpSession();
+ }
+ return ret;
+}
+
+bool UmcSession::Terminate()
+{
+ if(m_Terminating)
+ return false;
+
+ m_Running = false;
+ m_Terminating = true;
+ return (mrcp_application_session_terminate(m_pMrcpSession) == TRUE);
+}
+
+bool UmcSession::OnSessionTerminate(mrcp_sig_status_code_e status)
+{
+ if(!m_Terminating)
+ return false;
+
+ m_Terminating = false;
+ return DestroyMrcpSession();
+}
+
+bool UmcSession::OnSessionUpdate(mrcp_sig_status_code_e status)
+{
+ return m_Running;
+}
+
+bool UmcSession::OnChannelAdd(mrcp_channel_t *channel, mrcp_sig_status_code_e status)
+{
+ return m_Running;
+}
+
+bool UmcSession::OnChannelRemove(mrcp_channel_t *channel, mrcp_sig_status_code_e status)
+{
+ return m_Running;
+}
+
+bool UmcSession::OnMessageReceive(mrcp_channel_t *channel, mrcp_message_t *message)
+{
+ return m_Running;
+}
+
+bool UmcSession::OnTerminateEvent(mrcp_channel_t *channel)
+{
+ if(!m_Running)
+ return false;
+
+ return Terminate();
+}
+
+bool UmcSession::OnResourceDiscover(mrcp_session_descriptor_t* descriptor, mrcp_sig_status_code_e status)
+{
+ if(!m_Running)
+ return false;
+
+ if(!Start())
+ Terminate();
+ return true;
+}
+
+bool UmcSession::CreateMrcpSession(const char* pProfileName)
+{
+ m_pMrcpSession = mrcp_application_session_create(m_pMrcpApplication,pProfileName,this);
+ return (m_pMrcpSession != NULL);
+}
+
+bool UmcSession::DestroyMrcpSession()
+{
+ if(!m_pMrcpSession)
+ return false;
+
+ mrcp_application_session_destroy(m_pMrcpSession);
+ m_pMrcpSession = NULL;
+ return true;
+}
+
+bool UmcSession::AddMrcpChannel(mrcp_channel_t* pMrcpChannel)
+{
+ if(!m_Running)
+ return false;
+
+ return (mrcp_application_channel_add(m_pMrcpSession,pMrcpChannel) == TRUE);
+}
+
+bool UmcSession::RemoveMrcpChannel(mrcp_channel_t* pMrcpChannel)
+{
+ if(!m_Running)
+ return false;
+
+ return (mrcp_application_channel_remove(m_pMrcpSession,pMrcpChannel) == TRUE);
+}
+
+bool UmcSession::SendMrcpRequest(mrcp_channel_t* pMrcpChannel, mrcp_message_t* pMrcpMessage)
+{
+ if(!m_Running)
+ return false;
+
+ return (mrcp_application_message_send(m_pMrcpSession,pMrcpChannel,pMrcpMessage) == TRUE);
+}
+
+bool UmcSession::ResourceDiscover()
+{
+ if(!m_Running)
+ return false;
+
+ return (mrcp_application_resource_discover(m_pMrcpSession) == TRUE);
+}
+
+mrcp_channel_t* UmcSession::CreateMrcpChannel(
+ mrcp_resource_id resource_id,
+ mpf_termination_t* pTermination,
+ mpf_rtp_termination_descriptor_t* pRtpDescriptor,
+ void* pObj)
+{
+ return mrcp_application_channel_create(
+ m_pMrcpSession, /* session, channel belongs to */
+ resource_id, /* MRCP resource identifier */
+ pTermination, /* media termination, used to terminate audio stream */
+ NULL, /* RTP descriptor, used to create RTP termination (NULL by default) */
+ pObj); /* object to associate */
+}
+
+mpf_termination_t* UmcSession::CreateAudioTermination(
+ const mpf_audio_stream_vtable_t* pStreamVtable,
+ mpf_stream_capabilities_t* pCapabilities,
+ void* pObj)
+{
+ return mrcp_application_audio_termination_create(
+ m_pMrcpSession, /* session, termination belongs to */
+ pStreamVtable, /* virtual methods table of audio stream */
+ pCapabilities, /* capabilities of audio stream */
+ pObj); /* object to associate */
+}
+
+mrcp_message_t* UmcSession::CreateMrcpMessage(
+ mrcp_channel_t* pMrcpChannel,
+ mrcp_method_id method_id)
+{
+ return mrcp_application_message_create(m_pMrcpSession,pMrcpChannel,method_id);
+}
+
+
+
+apr_pool_t* UmcSession::GetSessionPool() const
+{
+ if(!m_pMrcpSession)
+ return NULL;
+ return mrcp_application_session_pool_get(m_pMrcpSession);
+}
+
+const char* UmcSession::GetMrcpSessionId() const
+{
+ if(!m_pMrcpSession)
+ return NULL;
+
+ const apt_str_t *pId = mrcp_application_session_id_get(m_pMrcpSession);
+ return pId->buf;
+}
--- /dev/null
+<?xml version="1.0" encoding="windows-1251"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="umc"
+ ProjectGUID="{CD1C52C1-D8E1-4654-AE65-6CCAB38DE894}"
+ RootNamespace="umc"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(ProjectDir)..\..\build\vsprops\unidebug.vsprops;$(ProjectDir)..\..\build\vsprops\unimrcpclient.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="include"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(UniMRCPClientLibs)"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(ProjectDir)..\..\build\vsprops\unirelease.vsprops;$(ProjectDir)..\..\build\vsprops\unimrcpclient.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="include"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(UniMRCPClientLibs)"
+ LinkTimeCodeGeneration="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="src"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\src\dtmfscenario.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\src\dtmfsession.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\src\main.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\src\recogscenario.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\src\recogsession.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\src\recorderscenario.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\src\recordersession.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\src\synthscenario.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\src\synthsession.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\src\umcconsole.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\src\umcframework.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\src\umcscenario.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\src\umcsession.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="include"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\include\dtmfscenario.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\dtmfsession.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\recogscenario.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\recogsession.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\recorderscenario.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\recordersession.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\synthscenario.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\synthsession.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\umcconsole.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\umcframework.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\umcscenario.h"
+ >
+ </File>
+ <File
+ RelativePath=".\include\umcsession.h"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
#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"
static mrcp_channel_t* demo_application_channel_create(mrcp_session_t *session)
{
mrcp_channel_t *channel;
+ apr_pool_t *pool = mrcp_application_session_pool_get(session);
/* 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);
+ demo_app_channel_t *demo_channel = apr_palloc(pool,sizeof(demo_app_channel_t));
+ mpf_rtp_termination_descriptor_t *rtp_descriptor = demo_rtp_descriptor_create(pool);
channel = mrcp_application_channel_create(
session, /* session, channel belongs to */
MRCP_SYNTHESIZER_RESOURCE, /* MRCP resource identifier */
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);
+ local_media->ip.buf,
+ local_media->port,
+ remote_media->ip.buf,
+ remote_media->port);
}
}
}
#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 */
/** 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_open(mpf_audio_stream_t *stream, mpf_codec_t *codec);
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);
{
mrcp_channel_t *channel;
mpf_termination_t *termination;
- mpf_codec_descriptor_t *codec_descriptor = NULL;
+ mpf_stream_capabilities_t *capabilities;
+ apr_pool_t *pool = mrcp_application_session_pool_get(session);
/* create channel */
- recog_app_channel_t *recog_channel = apr_palloc(session->pool,sizeof(recog_app_channel_t));
+ recog_app_channel_t *recog_channel = apr_palloc(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;
+ /* create source stream capabilities */
+ capabilities = mpf_source_stream_capabilities_create(pool);
+
+ /* add codec capabilities (Linear PCM) */
+ mpf_codec_capabilities_add(
+ &capabilities->codecs,
+ MPF_SAMPLE_RATE_8000 | MPF_SAMPLE_RATE_16000,
+ "LPCM");
+
+#if 0
+ /* more capabilities can be added or replaced */
+ mpf_codec_capabilities_add(
+ &capabilities->codecs,
+ MPF_SAMPLE_RATE_8000 | MPF_SAMPLE_RATE_16000,
+ "PCMU");
#endif
- termination = mrcp_application_source_termination_create(
+ termination = mrcp_application_audio_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) */
+ capabilities, /* capabilities of audio stream */
recog_channel); /* object to associate */
channel = mrcp_application_channel_create(
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);
+ apr_pool_t *pool = mrcp_application_session_pool_get(session);
/* 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);
+ const mpf_codec_descriptor_t *descriptor = mrcp_application_source_descriptor_get(channel);
+ char *file_name = apr_psprintf(pool,"one-%dkHz.pcm",
+ descriptor ? descriptor->sampling_rate/1000 : 8);
+ char *file_path = apt_datadir_filepath_get(dir_layout,file_name,pool);
if(file_path) {
recog_channel->audio_in = fopen(file_path,"rb");
if(recog_channel->audio_in) {
}
/** 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)
+static apt_bool_t recog_app_stream_open(mpf_audio_stream_t *stream, mpf_codec_t *codec)
{
return TRUE;
}
#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"
/** 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_open(mpf_audio_stream_t *stream, mpf_codec_t *codec);
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);
{
mrcp_channel_t *channel;
mpf_termination_t *termination;
- mpf_codec_descriptor_t *codec_descriptor = NULL;
+ mpf_stream_capabilities_t *capabilities;
+ apr_pool_t *pool = mrcp_application_session_pool_get(session);
/* create channel */
- synth_app_channel_t *synth_channel = apr_palloc(session->pool,sizeof(synth_app_channel_t));
+ synth_app_channel_t *synth_channel = apr_palloc(pool,sizeof(synth_app_channel_t));
synth_channel->audio_out = NULL;
+ /* create sink stream capabilities */
+ capabilities = mpf_sink_stream_capabilities_create(pool);
+
+ /* add codec capabilities (Linear PCM) */
+ mpf_codec_capabilities_add(
+ &capabilities->codecs,
+ MPF_SAMPLE_RATE_8000 | MPF_SAMPLE_RATE_16000,
+ "LPCM");
+
#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;
+ /* more capabilities can be added or replaced */
+ mpf_codec_capabilities_add(
+ &capabilities->codecs,
+ MPF_SAMPLE_RATE_8000 | MPF_SAMPLE_RATE_16000,
+ "PCMU");
#endif
- termination = mrcp_application_sink_termination_create(
+ termination = mrcp_application_audio_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) */
+ capabilities, /* capabilities of audio stream */
synth_channel); /* object to associate */
channel = mrcp_application_channel_create(
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);
+ apr_pool_t *pool = mrcp_application_session_pool_get(session);
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);
}
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);
+ const apt_str_t *id = mrcp_application_session_id_get(session);
+ const mpf_codec_descriptor_t *descriptor = mrcp_application_sink_descriptor_get(channel);
+ char *file_name = apr_psprintf(pool,"synth-%dkHz-%s.pcm",
+ descriptor ? descriptor->sampling_rate/1000 : 8,
+ id->buf);
+ char *file_path = apt_datadir_filepath_get(dir_layout,file_name,pool);
if(file_path) {
synth_channel->audio_out = fopen(file_path,"wb");
}
}
/** 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)
+static apt_bool_t synth_app_stream_open(mpf_audio_stream_t *stream, mpf_codec_t *codec)
{
return TRUE;
}
#include "demo_util.h"
/* common includes */
-#include "mrcp_session.h"
#include "mrcp_message.h"
#include "mrcp_generic_header.h"
/* synthesizer includes */
/* 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;
+ apt_string_assign(&media->ip,"127.0.0.1",pool);
+ media->port = 6000;
+ media->state = MPF_MEDIA_ENABLED;
+ media->direction = STREAM_DIRECTION_RECEIVE;
/* initialize codec list */
mpf_codec_list_init(&media->codec_list,2,pool);
SUBDIRS += demo-recog
endif
+if RECORDER_PLUGIN
+SUBDIRS += mrcp-recorder
+endif
+
if CEPSTRAL_PLUGIN
SUBDIRS += mrcp-cepstral
endif
*/
/*
- * 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.
+ * Mandatory rules concerning plugin implementation.
+ * 1. Each plugin MUST implement a plugin/engine creator function
+ * with the exact signature and name (the main entry point)
+ * MRCP_PLUGIN_DECLARE(mrcp_engine_t*) mrcp_plugin_create(apr_pool_t *pool)
+ * 2. Each plugin MUST declare its version number
+ * MRCP_PLUGIN_VERSION_DECLARE
+ * 3. One and only one response MUST be sent back to the received request.
+ * 4. Methods (callbacks) of the MRCP engine channel MUST not block.
+ * (asynchronous response can be sent from the context of other thread)
+ * 5. 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 "mrcp_recog_engine.h"
#include "mpf_activity_detector.h"
#include "apt_consumer_task.h"
#include "apt_log.h"
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 apt_bool_t demo_recog_engine_destroy(mrcp_engine_t *engine);
+static apt_bool_t demo_recog_engine_open(mrcp_engine_t *engine);
+static apt_bool_t demo_recog_engine_close(mrcp_engine_t *engine);
+static mrcp_engine_channel_t* demo_recog_engine_channel_create(mrcp_engine_t *engine, apr_pool_t *pool);
static const struct mrcp_engine_method_vtable_t engine_vtable = {
demo_recog_engine_destroy,
/** 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_open(mpf_audio_stream_t *stream, mpf_codec_t *codec);
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 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 set plugin version */
+MRCP_PLUGIN_VERSION_DECLARE
+
/** 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)
+MRCP_PLUGIN_DECLARE(mrcp_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;
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 */
+ /* create engine base */
+ return mrcp_engine_create(
+ MRCP_RECOGNIZER_RESOURCE, /* MRCP resource identifier */
+ demo_engine, /* object to associate */
+ &engine_vtable, /* virtual methods table of engine */
+ pool); /* pool to allocate memory from */
}
/** Destroy recognizer engine */
-static apt_bool_t demo_recog_engine_destroy(mrcp_resource_engine_t *engine)
+static apt_bool_t demo_recog_engine_destroy(mrcp_engine_t *engine)
{
demo_recog_engine_t *demo_engine = engine->obj;
if(demo_engine->task) {
}
/** Open recognizer engine */
-static apt_bool_t demo_recog_engine_open(mrcp_resource_engine_t *engine)
+static apt_bool_t demo_recog_engine_open(mrcp_engine_t *engine)
{
demo_recog_engine_t *demo_engine = engine->obj;
if(demo_engine->task) {
}
/** Close recognizer engine */
-static apt_bool_t demo_recog_engine_close(mrcp_resource_engine_t *engine)
+static apt_bool_t demo_recog_engine_close(mrcp_engine_t *engine)
{
demo_recog_engine_t *demo_engine = engine->obj;
if(demo_engine->task) {
return TRUE;
}
-static mrcp_engine_channel_t* demo_recog_engine_channel_create(mrcp_resource_engine_t *engine, apr_pool_t *pool)
+static mrcp_engine_channel_t* demo_recog_engine_channel_create(mrcp_engine_t *engine, apr_pool_t *pool)
{
+ mpf_stream_capabilities_t *capabilities;
+ mpf_termination_t *termination;
+
/* 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->stop_response = NULL;
recog_channel->detector = mpf_activity_detector_create(pool);
recog_channel->audio_out = NULL;
+
+ capabilities = mpf_sink_stream_capabilities_create(pool);
+ mpf_codec_capabilities_add(
+ &capabilities->codecs,
+ MPF_SAMPLE_RATE_8000 | MPF_SAMPLE_RATE_16000,
+ "LPCM");
+
+ /* create media termination */
+ termination = mrcp_engine_audio_termination_create(
+ recog_channel, /* object to associate */
+ &audio_stream_vtable, /* virtual methods table of audio stream */
+ capabilities, /* stream capabilities */
+ pool); /* pool to allocate memory from */
+
/* create engine channel base */
- recog_channel->channel = mrcp_engine_sink_channel_create(
- engine, /* resource engine */
+ recog_channel->channel = mrcp_engine_channel_create(
+ engine, /* 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 */
+ termination, /* associated media termination */
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;
- }
-
+ /* nothing to destrtoy */
return 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);
+ mpf_activity_detector_silence_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);
+ const mpf_codec_descriptor_t *descriptor = mrcp_engine_sink_stream_codec_get(channel);
+ char *file_name = apr_psprintf(channel->pool,"utter-%dkHz-%s.pcm",
+ descriptor ? descriptor->sampling_rate/1000 : 8,
+ request->channel_id.session_id.buf);
char *file_path = apt_datadir_filepath_get(dir_layout,file_name,channel->pool);
if(file_path) {
recog_channel->audio_out = fopen(file_path,"wb");
}
/** 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)
+static apt_bool_t demo_recog_stream_open(mpf_audio_stream_t *stream, mpf_codec_t *codec)
{
return TRUE;
}
break;
}
+ if((frame->type & MEDIA_FRAME_TYPE_EVENT) == MEDIA_FRAME_TYPE_EVENT) {
+ if(frame->marker == MPF_MARKER_START_OF_EVENT) {
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Detected Start of Event: id [%d]",
+ frame->event_frame.event_id);
+ }
+ else if(frame->marker == MPF_MARKER_END_OF_EVENT) {
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Detected End of Event: id [%d] duration [%d ts]",
+ frame->event_frame.event_id,
+ frame->event_frame.duration);
+ }
+ }
+
if(recog_channel->audio_out) {
fwrite(frame->codec_frame.buffer,1,frame->codec_frame.size,recog_channel->audio_out);
}
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 */
+ demo_recog_channel_t *recog_channel = demo_msg->channel->method_obj;
+ if(recog_channel->audio_out) {
+ fclose(recog_channel->audio_out);
+ recog_channel->audio_out = NULL;
+ }
+
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;
*/
/*
- * 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.
+ * Mandatory rules concerning plugin implementation.
+ * 1. Each plugin MUST implement a plugin/engine creator function
+ * with the exact signature and name (the main entry point)
+ * MRCP_PLUGIN_DECLARE(mrcp_engine_t*) mrcp_plugin_create(apr_pool_t *pool)
+ * 2. Each plugin MUST declare its version number
+ * MRCP_PLUGIN_VERSION_DECLARE
+ * 3. One and only one response MUST be sent back to the received request.
+ * 4. Methods (callbacks) of the MRCP engine channel MUST not block.
+ * (asynchronous response can be sent from the context of other thread)
+ * 5. 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 "mrcp_synth_engine.h"
#include "apt_consumer_task.h"
#include "apt_log.h"
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 apt_bool_t demo_synth_engine_destroy(mrcp_engine_t *engine);
+static apt_bool_t demo_synth_engine_open(mrcp_engine_t *engine);
+static apt_bool_t demo_synth_engine_close(mrcp_engine_t *engine);
+static mrcp_engine_channel_t* demo_synth_engine_channel_create(mrcp_engine_t *engine, apr_pool_t *pool);
static const struct mrcp_engine_method_vtable_t engine_vtable = {
demo_synth_engine_destroy,
/** 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_open(mpf_audio_stream_t *stream, mpf_codec_t *codec);
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);
};
-#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 set plugin version */
+MRCP_PLUGIN_VERSION_DECLARE
+
/** 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)
+MRCP_PLUGIN_DECLARE(mrcp_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));
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 */
+ /* create engine base */
+ return mrcp_engine_create(
+ MRCP_SYNTHESIZER_RESOURCE, /* MRCP resource identifier */
+ demo_engine, /* object to associate */
+ &engine_vtable, /* virtual methods table of engine */
+ pool); /* pool to allocate memory from */
}
/** Destroy synthesizer engine */
-static apt_bool_t demo_synth_engine_destroy(mrcp_resource_engine_t *engine)
+static apt_bool_t demo_synth_engine_destroy(mrcp_engine_t *engine)
{
demo_synth_engine_t *demo_engine = engine->obj;
if(demo_engine->task) {
}
/** Open synthesizer engine */
-static apt_bool_t demo_synth_engine_open(mrcp_resource_engine_t *engine)
+static apt_bool_t demo_synth_engine_open(mrcp_engine_t *engine)
{
demo_synth_engine_t *demo_engine = engine->obj;
if(demo_engine->task) {
}
/** Close synthesizer engine */
-static apt_bool_t demo_synth_engine_close(mrcp_resource_engine_t *engine)
+static apt_bool_t demo_synth_engine_close(mrcp_engine_t *engine)
{
demo_synth_engine_t *demo_engine = engine->obj;
if(demo_engine->task) {
}
/** 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)
+static mrcp_engine_channel_t* demo_synth_engine_channel_create(mrcp_engine_t *engine, apr_pool_t *pool)
{
+ mpf_stream_capabilities_t *capabilities;
+ mpf_termination_t *termination;
+
/* 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->time_to_complete = 0;
synth_channel->paused = FALSE;
synth_channel->audio_file = NULL;
+
+ capabilities = mpf_source_stream_capabilities_create(pool);
+ mpf_codec_capabilities_add(
+ &capabilities->codecs,
+ MPF_SAMPLE_RATE_8000 | MPF_SAMPLE_RATE_16000,
+ "LPCM");
+
+ /* create media termination */
+ termination = mrcp_engine_audio_termination_create(
+ synth_channel, /* object to associate */
+ &audio_stream_vtable, /* virtual methods table of audio stream */
+ capabilities, /* stream capabilities */
+ pool); /* pool to allocate memory from */
+
/* create engine channel base */
- synth_channel->channel = mrcp_engine_source_channel_create(
- engine, /* resource engine */
+ synth_channel->channel = mrcp_engine_channel_create(
+ engine, /* 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 */
+ termination, /* associated media termination */
pool); /* pool to allocate memory from */
+
return synth_channel->channel;
}
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);
+ const mpf_codec_descriptor_t *descriptor = mrcp_engine_source_stream_codec_get(channel);
+ char *file_name = apr_psprintf(channel->pool,"demo-%dkHz.pcm",
+ descriptor ? descriptor->sampling_rate/1000 : 8);
+ file_path = apt_datadir_filepath_get(channel->engine->dir_layout,file_name,channel->pool);
}
if(file_path) {
synth_channel->audio_file = fopen(file_path,"rb");
}
/** 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)
+static apt_bool_t demo_synth_stream_open(mpf_audio_stream_t *stream, mpf_codec_t *codec)
{
return TRUE;
}
*/
/*
- * 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.
+ * Mandatory rules concerning plugin implementation.
+ * 1. Each plugin MUST implement a plugin/engine creator function
+ * with the exact signature and name (the main entry point)
+ * MRCP_PLUGIN_DECLARE(mrcp_engine_t*) mrcp_plugin_create(apr_pool_t *pool)
+ * 2. Each plugin MUST declare its version number
+ * MRCP_PLUGIN_VERSION_DECLARE
+ * 3. One and only one response MUST be sent back to the received request.
+ * 4. Methods (callbacks) of the MRCP engine channel MUST not block.
+ * (asynchronous response can be sent from the context of other thread)
+ * 5. Methods (callbacks) of the MPF engine stream MUST not block.
*/
+#include <stdlib.h>
#include <swift.h>
-#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 "mrcp_synth_engine.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 apt_bool_t mrcp_swift_engine_destroy(mrcp_engine_t *engine);
+static apt_bool_t mrcp_swift_engine_open(mrcp_engine_t *engine);
+static apt_bool_t mrcp_swift_engine_close(mrcp_engine_t *engine);
+static mrcp_engine_channel_t* mrcp_swift_engine_channel_create(mrcp_engine_t *engine, apr_pool_t *pool);
static const struct mrcp_engine_method_vtable_t engine_vtable = {
mrcp_swift_engine_destroy,
/** 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_open(mpf_audio_stream_t *stream, mpf_codec_t *codec);
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);
NULL
};
+typedef struct mrcp_swift_engine_t mrcp_swift_engine_t;
+/** Declaration of Swift synthesizer engine */
+struct mrcp_swift_engine_t {
+ /** Swift synthesizer engine */
+ swift_engine *swift;
+ /** Speech language mapping */
+ apr_table_t *language_table;
+ /** Sampling rates (bitmask of mpf_sample_rates_e)
+ installed voices support */
+ int sample_rates;
+};
+
typedef struct mrcp_swift_channel_t mrcp_swift_channel_t;
/** Declaration of Swift synthesizer channel */
struct mrcp_swift_channel_t {
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 apt_bool_t mrcp_swift_voices_scan(mrcp_swift_engine_t *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 set plugin version */
+MRCP_PLUGIN_VERSION_DECLARE
+
/** 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)
+MRCP_PLUGIN_DECLARE(mrcp_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;
+ mrcp_swift_engine_t *synth_engine = apr_palloc(pool,sizeof(mrcp_swift_engine_t));
+ synth_engine->swift = NULL;
+ synth_engine->language_table = mrcp_swift_language_table_create(pool);
+ synth_engine->sample_rates = MPF_SAMPLE_RATE_NONE;
+
+ /* create engine base */
+ return mrcp_engine_create(
+ MRCP_SYNTHESIZER_RESOURCE, /* MRCP resource identifier */
+ synth_engine, /* object to associate */
+ &engine_vtable, /* virtual methods table of engine */
+ pool); /* pool to allocate memory from */
}
/** Destroy synthesizer engine */
-static apt_bool_t mrcp_swift_engine_destroy(mrcp_resource_engine_t *engine)
+static apt_bool_t mrcp_swift_engine_destroy(mrcp_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;
- }
+ /* nothing to destroy */
return TRUE;
}
/** Open synthesizer engine */
-static apt_bool_t mrcp_swift_engine_open(mrcp_resource_engine_t *engine)
+static apt_bool_t mrcp_swift_engine_open(mrcp_engine_t *engine)
{
+ mrcp_swift_engine_t *synth_engine = engine->obj;
+
+ /* open swift engine */
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Open Swift Engine [%s]",swift_version);
+ if((synth_engine->swift = swift_engine_open(NULL)) == NULL) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Open Swift Engine");
+ return FALSE;
+ }
+
+ if(mrcp_swift_voices_scan(synth_engine) == FALSE) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Scan Swift Voices");
+ swift_engine_close(synth_engine->swift);
+ synth_engine->swift = NULL;
+ return FALSE;
+ }
return TRUE;
}
/** Close synthesizer engine */
-static apt_bool_t mrcp_swift_engine_close(mrcp_resource_engine_t *engine)
+static apt_bool_t mrcp_swift_engine_close(mrcp_engine_t *engine)
{
+ mrcp_swift_engine_t *synth_engine = engine->obj;
+
+ /* close swift engine */
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Close Swift Engine");
+ if(synth_engine->swift) {
+ swift_engine_close(synth_engine->swift);
+ synth_engine->swift = NULL;
+ }
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)
+static mrcp_engine_channel_t* mrcp_swift_engine_channel_create(mrcp_engine_t *engine, apr_pool_t *pool)
{
- swift_engine *synth_engine = engine->obj;
- mrcp_swift_channel_t *synth_channel;
+ mrcp_swift_engine_t *synth_engine = engine->obj;
+
+ mpf_stream_capabilities_t *capabilities;
+ mpf_termination_t *termination;
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,"LPCM");
- 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));
+ mrcp_swift_channel_t *synth_channel = apr_palloc(pool,sizeof(mrcp_swift_channel_t));
+ synth_channel->port = NULL;
+ synth_channel->tts_stream = 0;
+ synth_channel->channel = NULL;
+ synth_channel->audio_buffer = NULL;
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;
+
+ capabilities = mpf_source_stream_capabilities_create(pool);
+ mpf_codec_capabilities_add(
+ &capabilities->codecs,
+ synth_engine->sample_rates,
+ "LPCM");
+
+ /* create media termination */
+ termination = mrcp_engine_audio_termination_create(
+ synth_channel, /* object to associate */
+ &audio_stream_vtable, /* virtual methods table of audio stream */
+ capabilities, /* stream capabilities */
+ pool); /* pool to allocate memory from */
+
/* create engine channel base */
- channel = mrcp_engine_source_channel_create(
- engine, /* resource engine */
+ channel = mrcp_engine_channel_create(
+ engine, /* 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 */
+ termination, /* associated media termination */
pool); /* pool to allocate memory from */
- if(!channel) {
- swift_port_close(port);
- synth_channel->port = NULL;
- return NULL;
+ if(channel) {
+ synth_channel->audio_buffer = mpf_buffer_create(pool);
}
-
- 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;
- }
+ /* nothing to destroy */
return TRUE;
}
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);
+ apt_bool_t status = FALSE;
+ mrcp_swift_channel_t *synth_channel = channel->method_obj;
+ mrcp_swift_engine_t *synth_engine = channel->engine->obj;
+ const mpf_codec_descriptor_t *descriptor = mrcp_engine_source_stream_codec_get(synth_channel->channel);
+ if(descriptor) {
+ swift_params *params;
+ swift_port *port;
+
+ params = swift_params_new(NULL);
+ swift_params_set_string(params, "audio/encoding", "pcm16");
+ swift_params_set_int(params, "audio/sampling-rate", descriptor->sampling_rate);
+ /* open swift port */
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Open Swift Port");
+ port = swift_port_open(synth_engine->swift,params);
+ if(port) {
+ /* 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->port = port;
+ status = TRUE;
+ }
+ else {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Open Swift Port");
+ }
+ }
+
+ return mrcp_engine_channel_open_respond(channel,status);
}
/** 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 */
+ 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 mrcp_engine_channel_close_respond(channel);
}
}
/** 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)
+static apt_bool_t synth_stream_open(mpf_audio_stream_t *stream, mpf_codec_t *codec)
{
return TRUE;
}
}
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);
+ mrcp_engine_t *engine = synth_channel->channel->engine;
+ mrcp_swift_engine_t *synth_engine = engine->obj;
+ if(synth_engine && synth_engine->language_table) {
+ swift_lang_name = apr_table_get(synth_engine->language_table,synth_header->speech_language.buf);
}
if(!swift_lang_name) {
swift_lang_name = synth_header->speech_language.buf;
}
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,"Find Swift Voice Matching the Criteria [%s]", search_criteria);
+ voice = swift_port_find_first_voice(synth_channel->port,search_criteria,NULL);
+ if(!voice) {
apt_log(APT_LOG_MARK,APT_PRIO_INFO,"No Swift Voice Available Matching the Criteria [%s]",search_criteria);
+ /* find the first available one */
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;
- }
+
+ if(voice) {
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Set Swift Voice [%s]",
+ swift_voice_get_attribute(voice, "name"));
+ 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)
+/** Get volume by prosody params */
+static 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) {
return res;
}
-apt_bool_t swift_prosody_rate_get(const mrcp_prosody_rate_t *prosody_rate, int *rate)
+/** Get rate by prosody params */
+static 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) {
return TRUE;
}
-/** Show Swift available voices */
-static void mrcp_swift_voices_show(swift_engine *engine)
+static void mrcp_swift_sample_rates_set(mrcp_swift_engine_t *engine, const char *str)
+{
+ if(str) {
+ int value = atoi(str);
+ if(value == 8000) {
+ engine->sample_rates |= MPF_SAMPLE_RATE_8000;
+ }
+ else if(value == 16000) {
+ engine->sample_rates |= MPF_SAMPLE_RATE_8000 | MPF_SAMPLE_RATE_16000;
+ }
+ }
+}
+
+/** Scan Swift available voices */
+static apt_bool_t mrcp_swift_voices_scan(mrcp_swift_engine_t *engine)
{
swift_port *port;
swift_voice *voice;
const char *license_status;
+ const char *sample_rate;
+
+ if(!engine->swift) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Invalid Swift Engine");
+ return FALSE;
+ }
/* open swift port*/
- if((port = swift_port_open(engine, NULL)) == NULL) {
+ if((port = swift_port_open(engine->swift, NULL)) == NULL) {
apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Open Swift Port");
- return;
+ return FALSE;
}
+ engine->sample_rates = MPF_SAMPLE_RATE_NONE;
+
/* 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;
+ return FALSE;
}
/* 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:");
else {
license_status = "unlicensed";
}
- apt_log(APT_LOG_MARK,APT_PRIO_INFO,"%s: %s, age %s, %s, %sHz, %s",
+ sample_rate = swift_voice_get_attribute(voice, "sample-rate");
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"%s: %s, age %s, %s, %sHz, %s, %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"),
+ sample_rate,
+ swift_voice_get_attribute(voice, "version"),
license_status);
+
+ mrcp_swift_sample_rates_set(engine,sample_rate);
}
swift_port_close(port);
+ return TRUE;
}
/** Create speech language lookup table */
APT_BEGIN_EXTERN_C
+/** Opaque Flite voice declaration */
typedef struct flite_voices_t flite_voices_t;
+/** Load Flite voices */
flite_voices_t* flite_voices_load(apr_pool_t *pool);
+/** Unload Flite voices */
void flite_voices_unload(flite_voices_t *voices);
+/** Get best matched voice */
cst_voice* flite_voices_best_match_get(flite_voices_t *voices, mrcp_message_t *message);
APT_END_EXTERN_C
*/
/*
- * 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.
+ * Mandatory rules concerning plugin implementation.
+ * 1. Each plugin MUST implement a plugin/engine creator function
+ * with the exact signature and name (the main entry point)
+ * MRCP_PLUGIN_DECLARE(mrcp_engine_t*) mrcp_plugin_create(apr_pool_t *pool)
+ * 2. Each plugin MUST declare its version number
+ * MRCP_PLUGIN_VERSION_DECLARE
+ * 3. One and only one response MUST be sent back to the received request.
+ * 4. Methods (callbacks) of the MRCP engine channel MUST not block.
+ * (asynchronous response can be sent from the context of other thread)
+ * 5. Methods (callbacks) of the MPF engine stream MUST not block.
*/
#include "flite_voices.h"
-#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 "mrcp_synth_engine.h"
#include "mpf_buffer.h"
#include "apr_time.h"
#include "apt_consumer_task.h"
typedef struct flite_synth_channel_t flite_synth_channel_t;
/** Declaration of synthesizer engine methods */
-static apt_bool_t flite_synth_engine_destroy(mrcp_resource_engine_t *engine);
-static apt_bool_t flite_synth_engine_open(mrcp_resource_engine_t *engine);
-static apt_bool_t flite_synth_engine_close(mrcp_resource_engine_t *engine);
-static mrcp_engine_channel_t* flite_synth_engine_channel_create(mrcp_resource_engine_t *engine, apr_pool_t *pool);
+static apt_bool_t flite_synth_engine_destroy(mrcp_engine_t *engine);
+static apt_bool_t flite_synth_engine_open(mrcp_engine_t *engine);
+static apt_bool_t flite_synth_engine_close(mrcp_engine_t *engine);
+static mrcp_engine_channel_t* flite_synth_engine_channel_create(mrcp_engine_t *engine, apr_pool_t *pool);
static const struct mrcp_engine_method_vtable_t engine_vtable = {
flite_synth_engine_destroy,
/* we have a special task for the actual synthesis -
the task is created when a mrcp speak message is received */
static apt_bool_t flite_speak(apt_task_t *task, apt_task_msg_t *msg);
-static apt_bool_t flite_on_start(apt_task_t *task);
-static apt_bool_t flite_on_terminate(apt_task_t *task);
+static void flite_on_start(apt_task_t *task);
+static void flite_on_terminate(apt_task_t *task);
+
+/** Declare this macro to set plugin version */
+MRCP_PLUGIN_VERSION_DECLARE
/** Declare this macro to use log routine of the server where the plugin is loaded from */
MRCP_PLUGIN_LOGGER_IMPLEMENT
/** Create flite synthesizer engine */
-MRCP_PLUGIN_DECLARE(mrcp_resource_engine_t*) mrcp_plugin_create(apr_pool_t *pool)
+MRCP_PLUGIN_DECLARE(mrcp_engine_t*) mrcp_plugin_create(apr_pool_t *pool)
{
/* create flite engine */
flite_synth_engine_t *flite_engine = (flite_synth_engine_t *) apr_palloc(pool,sizeof(flite_synth_engine_t));
flite_engine->iChannels = 0;
- /* create resource engine base */
- return mrcp_resource_engine_create(
- MRCP_SYNTHESIZER_RESOURCE, /* MRCP resource identifier */
- flite_engine, /* object to associate */
- &engine_vtable, /* virtual methods table of resource engine */
- pool); /* pool to allocate memory from */
+ /* create engine base */
+ return mrcp_engine_create(
+ MRCP_SYNTHESIZER_RESOURCE, /* MRCP resource identifier */
+ flite_engine, /* object to associate */
+ &engine_vtable, /* virtual methods table of engine */
+ pool); /* pool to allocate memory from */
}
/** Destroy synthesizer engine */
-static apt_bool_t flite_synth_engine_destroy(mrcp_resource_engine_t *engine)
+static apt_bool_t flite_synth_engine_destroy(mrcp_engine_t *engine)
{
apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite_synth_engine_destroy");
return TRUE;
}
/** Open synthesizer engine */
-static apt_bool_t flite_synth_engine_open(mrcp_resource_engine_t *engine)
+static apt_bool_t flite_synth_engine_open(mrcp_engine_t *engine)
{
flite_synth_engine_t *flite_engine = (flite_synth_engine_t *) engine->obj;
apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite_synth_engine_open");
}
/** Close synthesizer engine */
-static apt_bool_t flite_synth_engine_close(mrcp_resource_engine_t *engine)
+static apt_bool_t flite_synth_engine_close(mrcp_engine_t *engine)
{
flite_synth_engine_t *flite_engine = (flite_synth_engine_t *) engine->obj;
apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite_synth_engine_close");
}
/** Create flite synthesizer channel derived from engine channel base */
-static mrcp_engine_channel_t* flite_synth_engine_channel_create(mrcp_resource_engine_t *engine, apr_pool_t *pool)
+static mrcp_engine_channel_t* flite_synth_engine_channel_create(mrcp_engine_t *engine, apr_pool_t *pool)
{
+ mpf_stream_capabilities_t *capabilities;
+ mpf_termination_t *termination;
+
/* create flite synth channel */
- mpf_codec_descriptor_t *codec_descriptor = NULL;
flite_synth_channel_t *synth_channel = (flite_synth_channel_t *) apr_palloc(pool,sizeof(flite_synth_channel_t));
apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite_synth_engine_channel_create");
return NULL;
}
-#if 0
- codec_descriptor = (mpf_codec_descriptor_t *) 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,"LPCM");
- codec_descriptor->sampling_rate = 16000;
-#endif
-
- /* 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 */
+ capabilities = mpf_source_stream_capabilities_create(pool);
+ mpf_codec_capabilities_add(
+ &capabilities->codecs,
+ MPF_SAMPLE_RATE_8000 | MPF_SAMPLE_RATE_16000,
+ "LPCM");
+
+ /* create media termination */
+ termination = mrcp_engine_audio_termination_create(
synth_channel, /* object to associate */
- codec_descriptor, /* codec descriptor might be NULL by default */
+ &audio_stream_vtable, /* virtual methods table of audio stream */
+ capabilities, /* stream capabilities */
pool); /* pool to allocate memory from */
-
- if(!synth_channel->channel) {
- apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "flite_synth_engine_channel_create failed");
- apt_task_destroy(synth_channel->task);
- return NULL;
- }
+
+ /* create engine channel base */
+ synth_channel->channel = mrcp_engine_channel_create(
+ engine, /* engine */
+ &channel_vtable, /* virtual methods table of engine channel */
+ synth_channel, /* object to associate */
+ termination, /* associated media termination */
+ pool); /* pool to allocate memory from */
+
+ if(!synth_channel->channel) {
+ apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "flite_synth_engine_channel_create failed");
+ apt_task_destroy(synth_channel->task);
+ return NULL;
+ }
synth_channel->audio_buffer = mpf_buffer_create(pool);
synth_channel->iId = ++synth_channel->flite_engine->iChannels;
apt_str_t *body;
mrcp_message_t *response;
- mpf_codec_t * codec = mrcp_engine_source_stream_codec_get(synth_channel->channel);
- apr_uint16_t rate = codec->descriptor->sampling_rate;
+ apr_uint16_t rate = 8000;
+ const mpf_codec_descriptor_t * descriptor = mrcp_engine_source_stream_codec_get(synth_channel->channel);
+ if(descriptor) {
+ rate = descriptor->sampling_rate;
+ }
body = &synth_channel->speak_request->body;
response = synth_channel->speak_response;
return apt_consumer_task_object_get(consumer_task);
}
-static apt_bool_t flite_on_start(apt_task_t *task)
+static void flite_on_start(apt_task_t *task)
{
flite_synth_channel_t *synth_channel = flite_synth_channel_get(task);
apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "Speak task started - channel %d", synth_channel->iId);
- return mrcp_engine_channel_open_respond(synth_channel->channel,TRUE);
+ mrcp_engine_channel_open_respond(synth_channel->channel,TRUE);
}
-static apt_bool_t flite_on_terminate(apt_task_t *task)
+static void flite_on_terminate(apt_task_t *task)
{
flite_synth_channel_t *synth_channel = flite_synth_channel_get(task);
apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "Speak task terminated - channel %d", synth_channel->iId);
- return mrcp_engine_channel_close_respond(synth_channel->channel);
+ mrcp_engine_channel_close_respond(synth_channel->channel);
}
/** Process STOP request */
mrcppocketsphinx_la_LIBADD = $(UNIMRCP_POCKETSPHINX_LIBS) $(UNIMRCP_SPHINXBASE_LIBS) -lm
+dictionary:
+ !(test -f $(UNIMRCP_POCKETSPHINX_MODELS)/lm/cmudict.0.6d) || \
+ $(INSTALL) -m 644 $(UNIMRCP_POCKETSPHINX_MODELS)/lm/cmudict.0.6d $(datadir)/default.dic
+
+model:
+ !(test -d $(UNIMRCP_POCKETSPHINX_MODELS)/hmm/wsj1/) || ($(mkinstalldirs) $(datadir)/wsj1; \
+ $(INSTALL) -m 644 $(UNIMRCP_POCKETSPHINX_MODELS)/hmm/wsj1/* $(datadir)/wsj1)
+
install-data-local:
test -d $(confdir) || $(mkinstalldirs) $(confdir)
test -f $(confdir)/pocketsphinx.xml || $(INSTALL) -m 644 conf/pocketsphinx.xml $(confdir)
- test -d $(datadir)/wsj1 || $(mkinstalldirs) $(datadir)/wsj1; \
- $(INSTALL) -m 644 $(UNIMRCP_POCKETSPHINX_MODELS)/hmm/wsj1/* $(datadir)/wsj1; \
- $(INSTALL) -m 644 $(UNIMRCP_POCKETSPHINX_MODELS)/lm/cmudict.0.6d $(datadir)/default.dic
+ test -f $(datadir)/default.dic || $(MAKE) dictionary
+ test -d $(datadir)/wsj1 || $(MAKE) model
APT_BEGIN_EXTERN_C
+/** Enumeration of PocketSphinx models */
typedef enum {
- POCKETSPHINX_MODEL_NARROWBAND,
- POCKETSPHINX_MODEL_WIDEBAND
-
+ POCKETSPHINX_MODEL_NARROWBAND, /**< narrowband model */
+ POCKETSPHINX_MODEL_WIDEBAND /**< wideband model */
} pocketsphinx_model_e;
-/** Declaration of pocketsphinx properties */
+/** Declaration of PocketSphinx properties */
typedef struct pocketsphinx_properties_t pocketsphinx_properties_t;
-/** Pocketsphinx properties */
+/** PocketSphinx properties */
struct pocketsphinx_properties_t {
+ /** Data directory */
const char *data_dir;
+ /** Path to dictionary file */
const char *dictionary;
+ /** Path to narrowband model */
const char *model_8k;
+ /** Path to wideband model */
const char *model_16k;
+ /** Preferred (default) model */
pocketsphinx_model_e preferred_model;
+ /** Sensitivity level */
apr_size_t sensitivity_level;
+ /** Sensitivity timeout */
apr_size_t sensitivity_timeout;
+ /** Noinput timeout */
apr_size_t no_input_timeout;
+ /** Recognition timeout */
apr_size_t recognition_timeout;
+ /** Partial result checking timeout */
apr_size_t partial_result_timeout;
+ /** Whether to save waveform or not */
apt_bool_t save_waveform;
+ /** Directory to save waveform in */
const char *save_waveform_dir;
};
+/** Load PocketSphinx properties */
apt_bool_t pocketsphinx_properties_load(pocketsphinx_properties_t *properties,
const char *file_path,
const apt_dir_layout_t *dir_layout,
*/
/*
- * 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.
+ * Mandatory rules concerning plugin implementation.
+ * 1. Each plugin MUST implement a plugin/engine creator function
+ * with the exact signature and name (the main entry point)
+ * MRCP_PLUGIN_DECLARE(mrcp_engine_t*) mrcp_plugin_create(apr_pool_t *pool)
+ * 2. Each plugin MUST declare its version number
+ * MRCP_PLUGIN_VERSION_DECLARE
+ * 3. One and only one response MUST be sent back to the received request.
+ * 4. Methods (callbacks) of the MRCP engine channel MUST not block.
+ * (asynchronous response can be sent from the context of other thread)
+ * 5. Methods (callbacks) of the MPF engine stream MUST not block.
*/
#include <pocketsphinx.h>
#include <apr_thread_proc.h>
#include <apr_tables.h>
#include <apr_file_io.h>
-#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 "mrcp_recog_engine.h"
#include "mpf_activity_detector.h"
#include "pocketsphinx_properties.h"
#include "apt_log.h"
typedef struct pocketsphinx_recognizer_t pocketsphinx_recognizer_t;
/** Methods of recognition engine */
-static apt_bool_t pocketsphinx_engine_destroy(mrcp_resource_engine_t *engine);
-static apt_bool_t pocketsphinx_engine_open(mrcp_resource_engine_t *engine);
-static apt_bool_t pocketsphinx_engine_close(mrcp_resource_engine_t *engine);
-static mrcp_engine_channel_t* pocketsphinx_engine_recognizer_create(mrcp_resource_engine_t *engine, apr_pool_t *pool);
+static apt_bool_t pocketsphinx_engine_destroy(mrcp_engine_t *engine);
+static apt_bool_t pocketsphinx_engine_open(mrcp_engine_t *engine);
+static apt_bool_t pocketsphinx_engine_close(mrcp_engine_t *engine);
+static mrcp_engine_channel_t* pocketsphinx_engine_recognizer_create(mrcp_engine_t *engine, apr_pool_t *pool);
static const struct mrcp_engine_method_vtable_t engine_vtable = {
pocketsphinx_engine_destroy,
/** Pocketsphinx engine (engine is an aggregation of recognizers) */
struct pocketsphinx_engine_t {
- /* Resource engine base */
- mrcp_resource_engine_t *base;
+ /* Engine base */
+ mrcp_engine_t *base;
/** Properties loaded from config file */
pocketsphinx_properties_t properties;
};
ps_decoder_t *decoder;
/** Configuration */
cmd_ln_t *config;
- /** Recognizer properties coppied from defualt engine properties */
+ /** Recognizer properties coppied from default engine properties */
pocketsphinx_properties_t properties;
/** Is input timer started */
apt_bool_t is_input_timer_on;
mrcp_message_t *stop_response;
/** Is recognition channel being closed */
apt_bool_t close_requested;
+ /** Flag to prevent race condition when checking if a message is present */
+ apt_bool_t message_waiting;
};
+static void* APR_THREAD_FUNC pocketsphinx_recognizer_run(apr_thread_t *thread, void *data);
+
+/** Declare this macro to set plugin version */
+MRCP_PLUGIN_VERSION_DECLARE
+
/** Declare this macro to use log routine of the server, plugin is loaded from */
MRCP_PLUGIN_LOGGER_IMPLEMENT
-static void* APR_THREAD_FUNC pocketsphinx_recognizer_run(apr_thread_t *thread, void *data);
-
/** Create pocketsphinx engine (engine is an aggregation of recognizers) */
-MRCP_PLUGIN_DECLARE(mrcp_resource_engine_t*) mrcp_plugin_create(apr_pool_t *pool)
+MRCP_PLUGIN_DECLARE(mrcp_engine_t*) mrcp_plugin_create(apr_pool_t *pool)
{
pocketsphinx_engine_t *engine = apr_palloc(pool,sizeof(pocketsphinx_engine_t));
apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Create PocketSphinx Engine");
- /* create resource engine base */
- engine->base = mrcp_resource_engine_create(
+ /* create engine base */
+ engine->base = mrcp_engine_create(
MRCP_RECOGNIZER_RESOURCE, /* MRCP resource identifier */
engine, /* object to associate */
- &engine_vtable, /* virtual methods table of resource engine */
+ &engine_vtable, /* virtual methods table of engine */
pool); /* pool to allocate memory from */
return engine->base;
}
/** Destroy pocketsphinx engine */
-static apt_bool_t pocketsphinx_engine_destroy(mrcp_resource_engine_t *resource_engine)
+static apt_bool_t pocketsphinx_engine_destroy(mrcp_engine_t *engine_base)
{
return TRUE;
}
/** Open pocketsphinx engine */
-static apt_bool_t pocketsphinx_engine_open(mrcp_resource_engine_t *resource_engine)
+static apt_bool_t pocketsphinx_engine_open(mrcp_engine_t *engine_base)
{
- pocketsphinx_engine_t *engine = resource_engine->obj;
- const apt_dir_layout_t *dir_layout = resource_engine->dir_layout;
+ pocketsphinx_engine_t *engine = engine_base->obj;
+ const apt_dir_layout_t *dir_layout = engine_base->dir_layout;
char *file_path = NULL;
- apr_filepath_merge(&file_path,dir_layout->conf_dir_path,POCKETSPHINX_CONFFILE_NAME,0,resource_engine->pool);
+ apr_filepath_merge(&file_path,dir_layout->conf_dir_path,POCKETSPHINX_CONFFILE_NAME,0,engine_base->pool);
/* load properties */
- pocketsphinx_properties_load(&engine->properties,file_path,dir_layout,resource_engine->pool);
+ pocketsphinx_properties_load(&engine->properties,file_path,dir_layout,engine_base->pool);
return TRUE;
}
/** Close pocketsphinx engine */
-static apt_bool_t pocketsphinx_engine_close(mrcp_resource_engine_t *resource_engine)
+static apt_bool_t pocketsphinx_engine_close(mrcp_engine_t *engine_base)
{
return TRUE;
}
/** Create pocketsphinx recognizer */
-static mrcp_engine_channel_t* pocketsphinx_engine_recognizer_create(mrcp_resource_engine_t *resource_engine, apr_pool_t *pool)
+static mrcp_engine_channel_t* pocketsphinx_engine_recognizer_create(mrcp_engine_t *engine_base, apr_pool_t *pool)
{
+ mpf_stream_capabilities_t *capabilities;
+ mpf_termination_t *termination;
mrcp_engine_channel_t *channel;
- mpf_codec_descriptor_t *codec_descriptor;
- pocketsphinx_engine_t *engine = resource_engine->obj;
+ pocketsphinx_engine_t *engine = engine_base->obj;
+
+ /* create pocketsphinx recognizer */
pocketsphinx_recognizer_t *recognizer = apr_palloc(pool,sizeof(pocketsphinx_recognizer_t));
recognizer->decoder = NULL;
recognizer->config = NULL;
recognizer->grammar_id = NULL;
recognizer->grammar_table = apr_table_make(pool,1);
recognizer->waveform = NULL;
+ recognizer->message_waiting = FALSE;
/* copy default properties loaded from config */
recognizer->properties = engine->properties;
- codec_descriptor = (mpf_codec_descriptor_t *) 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,"LPCM");
- codec_descriptor->sampling_rate = 8000;
- if(recognizer->properties.preferred_model == POCKETSPHINX_MODEL_WIDEBAND) {
- codec_descriptor->sampling_rate = 16000;
- }
+ capabilities = mpf_sink_stream_capabilities_create(pool);
+ mpf_codec_capabilities_add(
+ &capabilities->codecs,
+ MPF_SAMPLE_RATE_8000 | MPF_SAMPLE_RATE_16000,
+ "LPCM");
+
+ /* create media termination */
+ termination = mrcp_engine_audio_termination_create(
+ recognizer, /* object to associate */
+ &audio_stream_vtable, /* virtual methods table of audio stream */
+ capabilities, /* stream capabilities */
+ pool); /* pool to allocate memory from */
/* create engine channel base */
- channel = mrcp_engine_sink_channel_create(
- resource_engine, /* resource engine */
+ channel = mrcp_engine_channel_create(
+ engine_base, /* engine */
&channel_vtable, /* virtual methods table of engine channel */
- &audio_stream_vtable, /* virtual methods table of audio stream */
recognizer, /* object to associate */
- NULL, /* codec descriptor might be NULL by default */
+ termination, /* associated media termination */
pool); /* pool to allocate memory from */
apr_thread_mutex_create(&recognizer->mutex,APR_THREAD_MUTEX_DEFAULT,channel->pool);
/* Signal recognition thread to terminate */
apr_thread_mutex_lock(recognizer->mutex);
recognizer->close_requested = TRUE;
+ recognizer->message_waiting = TRUE;
apr_thread_cond_signal(recognizer->wait_object);
apr_thread_mutex_unlock(recognizer->mutex);
return TRUE;
/* Store request and signal recognition thread to process the request */
apr_thread_mutex_lock(recognizer->mutex);
recognizer->request = request;
+ recognizer->message_waiting = TRUE;
apr_thread_cond_signal(recognizer->wait_object);
apr_thread_mutex_unlock(recognizer->mutex);
return TRUE;
/** Initialize pocketsphinx decoder [RECOG] */
static apt_bool_t pocketsphinx_decoder_init(pocketsphinx_recognizer_t *recognizer, const char *grammar)
{
+ const mpf_codec_descriptor_t *descriptor = mrcp_engine_sink_stream_codec_get(recognizer->channel);
const char *model = recognizer->properties.model_8k;
const char *rate = "8000";
- if(recognizer->properties.preferred_model == POCKETSPHINX_MODEL_WIDEBAND) {
+ if(descriptor && descriptor->sampling_rate == 16000) {
model = recognizer->properties.model_16k;
rate = "16000";
}
recognizer->grammar_id,
recognizer->grammar_id,
99,
- recognizer->last_result,
recognizer->last_result);
if(body->buf) {
mrcp_generic_header_t *generic_header;
return TRUE;
}
+/** Load pocketsphinx grammar [RECOG] */
+static mrcp_status_code_e pocketsphinx_grammar_load(pocketsphinx_recognizer_t *recognizer, const char *content_id, const char *content_type, const apt_str_t *content)
+{
+ /* load grammar */
+ mrcp_engine_channel_t *channel = recognizer->channel;
+ const apt_dir_layout_t *dir_layout = channel->engine->dir_layout;
+ const char *grammar_file_path = NULL;
+ const char *grammar_file_name = NULL;
+ apr_file_t *fd = NULL;
+ apr_status_t rv;
+ apr_size_t size;
+
+ /* only JSGF grammar is supported */
+ if(strstr(content_type,"jsgf") == NULL) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Not Supported Content-Type [%s] "APT_SIDRES_FMT,
+ content_type,RECOGNIZER_SIDRES(recognizer));
+ return MRCP_STATUS_CODE_UNSUPPORTED_PARAM_VALUE;
+ }
+
+ grammar_file_name = apr_psprintf(channel->pool,"%s-%s.gram",channel->id.buf,content_id);
+ grammar_file_path = apt_datadir_filepath_get(dir_layout,grammar_file_name,channel->pool);
+
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Create Grammar File [%s] "APT_SIDRES_FMT,
+ grammar_file_path,RECOGNIZER_SIDRES(recognizer));
+ rv = apr_file_open(&fd,grammar_file_path,APR_CREATE|APR_TRUNCATE|APR_WRITE|APR_BINARY,
+ APR_OS_DEFAULT,channel->pool);
+ if(rv != APR_SUCCESS) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot Open Grammar File to Write [%s] "APT_SIDRES_FMT,
+ grammar_file_path,RECOGNIZER_SIDRES(recognizer));
+ return MRCP_STATUS_CODE_METHOD_FAILED;
+ }
+
+ size = content->length;
+ apr_file_write(fd,content->buf,&size);
+ apr_file_close(fd);
+
+ /* init pocketsphinx decoder */
+ if(pocketsphinx_decoder_init(recognizer,grammar_file_path) != TRUE) {
+ apr_file_remove(grammar_file_path,channel->pool);
+ return MRCP_STATUS_CODE_METHOD_FAILED;
+ }
+ recognizer->grammar_id = content_id;
+ apr_table_setn(recognizer->grammar_table,content_id,grammar_file_path);
+ return MRCP_STATUS_CODE_SUCCESS;
+}
+
+/** Unload pocketsphinx grammar [RECOG] */
+static mrcp_status_code_e pocketsphinx_grammar_unload(pocketsphinx_recognizer_t *recognizer, const char *content_id)
+{
+ /* unload grammar */
+ const char *grammar_file_path = apr_table_get(recognizer->grammar_table,content_id);
+ if(!grammar_file_path) {
+ return MRCP_STATUS_CODE_ILLEGAL_PARAM_VALUE;
+ }
+
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Remove Grammar File [%s] "APT_SIDRES_FMT,
+ grammar_file_path,RECOGNIZER_SIDRES(recognizer));
+ apr_file_remove(grammar_file_path,recognizer->channel->pool);
+ apr_table_unset(recognizer->grammar_table,content_id);
+ return MRCP_STATUS_CODE_SUCCESS;
+}
+
/** Process DEFINE-GRAMMAR request [RECOG] */
static apt_bool_t pocketsphinx_define_grammar(pocketsphinx_recognizer_t *recognizer, mrcp_message_t *request, mrcp_message_t *response)
{
const char *content_type = NULL;
const char *content_id = NULL;
- apt_str_t *grammar = NULL;
-
mrcp_engine_channel_t *channel = recognizer->channel;
- /* get recognizer header */
mrcp_generic_header_t *generic_header = mrcp_generic_header_get(request);
mrcp_recog_header_t *recog_header = mrcp_resource_header_prepare(response);
if(!generic_header || !recog_header) {
recog_header->completion_cause = RECOGNIZER_COMPLETION_CAUSE_SUCCESS;
mrcp_resource_header_property_add(response,RECOGNIZER_HEADER_COMPLETION_CAUSE);
-
+
/* content-id must be specified */
if(mrcp_generic_header_property_check(request,GENERIC_HEADER_CONTENT_ID) == TRUE) {
content_id = generic_header->content_id.buf;
return FALSE;
}
- if(mrcp_generic_header_property_check(request,GENERIC_HEADER_CONTENT_LENGTH) == TRUE) {
- grammar = &request->body;
- }
-
- if(grammar) {
- /* load grammar */
- const apt_dir_layout_t *dir_layout = channel->engine->dir_layout;
- const char *grammar_file_path = NULL;
- const char *grammar_file_name = NULL;
- apr_file_t *fd = NULL;
- apr_status_t rv;
- apr_size_t size;
-
+ if(mrcp_generic_header_property_check(request,GENERIC_HEADER_CONTENT_LENGTH) == TRUE &&
+ generic_header->content_length) {
/* content-type must be specified */
if(mrcp_generic_header_property_check(request,GENERIC_HEADER_CONTENT_TYPE) == TRUE) {
content_type = generic_header->content_type.buf;
}
+
if(!content_type) {
apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Missing Content-Type "APT_SIDRES_FMT,RECOGNIZER_SIDRES(recognizer));
response->start_line.status_code = MRCP_STATUS_CODE_MISSING_PARAM;
recog_header->completion_cause = RECOGNIZER_COMPLETION_CAUSE_GRAM_LOAD_FAILURE;
return FALSE;
}
-
- /* only JSGF grammar is supported */
- if(strstr(content_type,"jsgf") == NULL) {
- apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Not Supported Content-Type [%s] "APT_SIDRES_FMT,
- content_type,RECOGNIZER_SIDRES(recognizer));
- response->start_line.status_code = MRCP_STATUS_CODE_UNSUPPORTED_PARAM_VALUE;
- recog_header->completion_cause = RECOGNIZER_COMPLETION_CAUSE_GRAM_LOAD_FAILURE;
- return FALSE;
- }
-
- grammar_file_name = apr_psprintf(channel->pool,"%s-%s.gram",channel->id.buf,content_id);
- grammar_file_path = apt_datadir_filepath_get(dir_layout,grammar_file_name,channel->pool);
- apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Create Grammar File [%s] "APT_SIDRES_FMT,
- grammar_file_path,RECOGNIZER_SIDRES(recognizer));
- rv = apr_file_open(&fd,grammar_file_path,APR_CREATE|APR_TRUNCATE|APR_WRITE|APR_BINARY,
- APR_OS_DEFAULT,channel->pool);
- if(rv != APR_SUCCESS) {
- apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Cannot Open Grammar File to Write [%s] "APT_SIDRES_FMT,
- grammar_file_path,RECOGNIZER_SIDRES(recognizer));
- response->start_line.status_code = MRCP_STATUS_CODE_METHOD_FAILED;
+ response->start_line.status_code = pocketsphinx_grammar_load(recognizer,content_id,content_type,&request->body);
+ if(response->start_line.status_code != MRCP_STATUS_CODE_SUCCESS) {
recog_header->completion_cause = RECOGNIZER_COMPLETION_CAUSE_GRAM_LOAD_FAILURE;
return FALSE;
}
-
- size = grammar->length;
- apr_file_write(fd,grammar->buf,&size);
- apr_file_close(fd);
-
- /* init pocketsphinx decoder */
- if(pocketsphinx_decoder_init(recognizer,grammar_file_path) == TRUE) {
- recognizer->grammar_id = content_id;
- apr_table_setn(recognizer->grammar_table,content_id,grammar_file_path);
- }
- else {
- response->start_line.status_code = MRCP_STATUS_CODE_METHOD_FAILED;
- recog_header->completion_cause = RECOGNIZER_COMPLETION_CAUSE_GRAM_LOAD_FAILURE;
- apr_file_remove(grammar_file_path,channel->pool);
- return FALSE;
- }
}
else {
- /* unload grammar */
- const char *grammar_file_path = apr_table_get(recognizer->grammar_table,content_id);
- if(grammar_file_path) {
- apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Remove Grammar File [%s] "APT_SIDRES_FMT,
- grammar_file_path,RECOGNIZER_SIDRES(recognizer));
- apr_file_remove(grammar_file_path,channel->pool);
- apr_table_unset(recognizer->grammar_table,content_id);
+ response->start_line.status_code = pocketsphinx_grammar_unload(recognizer,content_id);
+ if(response->start_line.status_code != MRCP_STATUS_CODE_SUCCESS) {
+ recog_header->completion_cause = RECOGNIZER_COMPLETION_CAUSE_GRAM_LOAD_FAILURE;
+ return FALSE;
}
}
/** Process RECOGNIZE request [RECOG] */
static apt_bool_t pocketsphinx_recognize(pocketsphinx_recognizer_t *recognizer, mrcp_message_t *request, mrcp_message_t *response)
{
+ const char *content_type = NULL;
mrcp_engine_channel_t *channel = recognizer->channel;
mrcp_recog_header_t *request_recog_header;
mrcp_recog_header_t *response_recog_header = mrcp_resource_header_prepare(response);
- if(!response_recog_header) {
+ mrcp_generic_header_t *generic_header = mrcp_generic_header_get(request);
+ if(!generic_header || !response_recog_header) {
return FALSE;
}
+ /* content-type must be specified */
+ if(mrcp_generic_header_property_check(request,GENERIC_HEADER_CONTENT_TYPE) == TRUE) {
+ content_type = generic_header->content_type.buf;
+ }
+ if(!content_type) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Missing Content-Type "APT_SIDRES_FMT,RECOGNIZER_SIDRES(recognizer));
+ response->start_line.status_code = MRCP_STATUS_CODE_MISSING_PARAM;
+ response_recog_header->completion_cause = RECOGNIZER_COMPLETION_CAUSE_GRAM_LOAD_FAILURE;
+ return FALSE;
+ }
+
+ if(strcmp(content_type,"text/uri-list") == 0) {
+ /* assume the uri-list contains last defined (active) grammar for now */
+ }
+ else {
+ const char *content_id = NULL;
+ /* content-id must be specified */
+ if(mrcp_generic_header_property_check(request,GENERIC_HEADER_CONTENT_ID) == TRUE) {
+ content_id = generic_header->content_id.buf;
+ }
+ if(!content_id) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Missing Content-Id "APT_SIDRES_FMT,RECOGNIZER_SIDRES(recognizer));
+ response->start_line.status_code = MRCP_STATUS_CODE_MISSING_PARAM;
+ response_recog_header->completion_cause = RECOGNIZER_COMPLETION_CAUSE_GRAM_LOAD_FAILURE;
+ return FALSE;
+ }
+
+ response->start_line.status_code = pocketsphinx_grammar_load(recognizer,content_id,content_type,&request->body);
+ if(response->start_line.status_code != MRCP_STATUS_CODE_SUCCESS) {
+ response_recog_header->completion_cause = RECOGNIZER_COMPLETION_CAUSE_GRAM_LOAD_FAILURE;
+ return FALSE;
+ }
+ }
+
if(!recognizer->decoder || ps_start_utt(recognizer->decoder, NULL) < 0) {
response->start_line.status_code = MRCP_STATUS_CODE_METHOD_FAILED;
response_recog_header->completion_cause = RECOGNIZER_COMPLETION_CAUSE_ERROR;
/* check if waveform (utterance) should be saved */
if(recognizer->properties.save_waveform == TRUE) {
apr_status_t rv;
- const char *waveform_file_name = apr_psprintf(channel->pool,"utter-%s-%d.pcm",
- channel->id.buf,request->start_line.request_id);
+ const char *waveform_file_name = apr_psprintf(channel->pool,"utter-%s-%"MRCP_REQUEST_ID_FMT".pcm",
+ channel->id.buf,
+ request->start_line.request_id);
char *waveform_file_path = NULL;
apr_filepath_merge(&waveform_file_path,recognizer->properties.save_waveform_dir,
waveform_file_name,0,channel->pool);
mrcp_engine_channel_open_respond(recognizer->channel,TRUE);
do {
+ apr_thread_mutex_lock(recognizer->mutex);
/** Wait for MRCP requests */
apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Wait for incoming messages "APT_SIDRES_FMT, RECOGNIZER_SIDRES(recognizer));
- apr_thread_mutex_lock(recognizer->mutex);
- apr_thread_cond_wait(recognizer->wait_object,recognizer->mutex);
- apr_thread_mutex_unlock(recognizer->mutex);
+ if (!recognizer->message_waiting) {
+ apr_thread_cond_wait(recognizer->wait_object,recognizer->mutex);
+ }
+ recognizer->message_waiting = FALSE;
if(recognizer->request) {
/* store request message and further dispatch it */
/* end of input detected, get recognition result and raise recognition complete event */
pocketsphinx_recognition_complete(recognizer,recognizer->complete_event);
}
+ apr_thread_mutex_unlock(recognizer->mutex);
}
while(recognizer->close_requested == FALSE);
/* signal recognition thread first */
apr_thread_mutex_lock(recognizer->mutex);
recognizer->complete_event = message;
+ recognizer->message_waiting = TRUE;
apr_thread_cond_signal(recognizer->wait_object);
apr_thread_mutex_unlock(recognizer->mutex);
return TRUE;
* limitations under the License.
*/
+#include <stdlib.h>
#include <apr_xml.h>
#include "pocketsphinx_properties.h"
#include "apt_log.h"
--- /dev/null
+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 = mrcprecorder.la
+
+mrcprecorder_la_SOURCES = src/mrcp_recorder_engine.c
+mrcprecorder_la_LDFLAGS = -module $(PLUGIN_LT_VERSION)
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="mrcprecorder"
+ ProjectGUID="{5AFB8B04-AEB9-408C-B53E-AFBC44B5F3F2}"
+ RootNamespace="mrcprecorder"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(ProjectDir)..\..\build\vsprops\unidebug.vsprops;$(ProjectDir)..\..\build\vsprops\unimrcpplugin.vsprops"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="include"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(ProjectDir)..\..\build\vsprops\unirelease.vsprops;$(ProjectDir)..\..\build\vsprops\unimrcpplugin.vsprops"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="include"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="include"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ </Filter>
+ <Filter
+ Name="src"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ >
+ <File
+ RelativePath=".\src\mrcp_recorder_engine.c"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
--- /dev/null
+/*
+ * 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.
+ */
+
+/*
+ * Mandatory rules concerning plugin implementation.
+ * 1. Each plugin MUST implement a plugin/engine creator function
+ * with the exact signature and name (the main entry point)
+ * MRCP_PLUGIN_DECLARE(mrcp_engine_t*) mrcp_plugin_create(apr_pool_t *pool)
+ * 2. Each plugin MUST declare its version number
+ * MRCP_PLUGIN_VERSION_DECLARE
+ * 3. One and only one response MUST be sent back to the received request.
+ * 4. Methods (callbacks) of the MRCP engine channel MUST not block.
+ * (asynchronous response can be sent from the context of other thread)
+ * 5. Methods (callbacks) of the MPF engine stream MUST not block.
+ */
+
+#include "mrcp_recorder_engine.h"
+#include "mpf_activity_detector.h"
+#include "apt_log.h"
+
+#define RECORDER_ENGINE_TASK_NAME "Recorder Engine"
+
+typedef struct recorder_channel_t recorder_channel_t;
+
+/** Declaration of recorder engine methods */
+static apt_bool_t recorder_engine_destroy(mrcp_engine_t *engine);
+static apt_bool_t recorder_engine_open(mrcp_engine_t *engine);
+static apt_bool_t recorder_engine_close(mrcp_engine_t *engine);
+static mrcp_engine_channel_t* recorder_engine_channel_create(mrcp_engine_t *engine, apr_pool_t *pool);
+
+static const struct mrcp_engine_method_vtable_t engine_vtable = {
+ recorder_engine_destroy,
+ recorder_engine_open,
+ recorder_engine_close,
+ recorder_engine_channel_create
+};
+
+
+/** Declaration of recorder channel methods */
+static apt_bool_t recorder_channel_destroy(mrcp_engine_channel_t *channel);
+static apt_bool_t recorder_channel_open(mrcp_engine_channel_t *channel);
+static apt_bool_t recorder_channel_close(mrcp_engine_channel_t *channel);
+static apt_bool_t recorder_channel_request_process(mrcp_engine_channel_t *channel, mrcp_message_t *request);
+
+static const struct mrcp_engine_channel_method_vtable_t channel_vtable = {
+ recorder_channel_destroy,
+ recorder_channel_open,
+ recorder_channel_close,
+ recorder_channel_request_process
+};
+
+/** Declaration of recorder audio stream methods */
+static apt_bool_t recorder_stream_destroy(mpf_audio_stream_t *stream);
+static apt_bool_t recorder_stream_open(mpf_audio_stream_t *stream, mpf_codec_t *codec);
+static apt_bool_t recorder_stream_close(mpf_audio_stream_t *stream);
+static apt_bool_t recorder_stream_write(mpf_audio_stream_t *stream, const mpf_frame_t *frame);
+
+static const mpf_audio_stream_vtable_t audio_stream_vtable = {
+ recorder_stream_destroy,
+ NULL,
+ NULL,
+ NULL,
+ recorder_stream_open,
+ recorder_stream_close,
+ recorder_stream_write
+};
+
+/** Declaration of recorder channel */
+struct recorder_channel_t {
+ /** Engine channel base */
+ mrcp_engine_channel_t *channel;
+
+ /** Active (in-progress) record request */
+ mrcp_message_t *record_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;
+ /** Max length of the recording in msec */
+ apr_size_t max_time;
+ /** Elapsed time of the recording in msec */
+ apr_size_t cur_time;
+ /** Written size of the recording in bytes */
+ apr_size_t cur_size;
+ /** File name of the recording */
+ const char *file_name;
+ /** File to write to */
+ FILE *audio_out;
+};
+
+
+/** Declare this macro to set plugin version */
+MRCP_PLUGIN_VERSION_DECLARE
+
+/** Declare this macro to use log routine of the server, plugin is loaded from */
+MRCP_PLUGIN_LOGGER_IMPLEMENT
+
+/** Create recorder engine */
+MRCP_PLUGIN_DECLARE(mrcp_engine_t*) mrcp_plugin_create(apr_pool_t *pool)
+{
+ /* create engine base */
+ return mrcp_engine_create(
+ MRCP_RECORDER_RESOURCE, /* MRCP resource identifier */
+ NULL, /* object to associate */
+ &engine_vtable, /* virtual methods table of engine */
+ pool); /* pool to allocate memory from */
+}
+
+/** Destroy recorder engine */
+static apt_bool_t recorder_engine_destroy(mrcp_engine_t *engine)
+{
+ return TRUE;
+}
+
+/** Open recorder engine */
+static apt_bool_t recorder_engine_open(mrcp_engine_t *engine)
+{
+ return TRUE;
+}
+
+/** Close recorder engine */
+static apt_bool_t recorder_engine_close(mrcp_engine_t *engine)
+{
+ return TRUE;
+}
+
+static mrcp_engine_channel_t* recorder_engine_channel_create(mrcp_engine_t *engine, apr_pool_t *pool)
+{
+ mpf_stream_capabilities_t *capabilities;
+ mpf_termination_t *termination;
+
+ /* create recorder channel */
+ recorder_channel_t *recorder_channel = apr_palloc(pool,sizeof(recorder_channel_t));
+ recorder_channel->record_request = NULL;
+ recorder_channel->stop_response = NULL;
+ recorder_channel->detector = mpf_activity_detector_create(pool);
+ recorder_channel->max_time = 0;
+ recorder_channel->cur_time = 0;
+ recorder_channel->cur_size = 0;
+ recorder_channel->file_name = NULL;
+ recorder_channel->audio_out = NULL;
+
+ capabilities = mpf_sink_stream_capabilities_create(pool);
+ mpf_codec_capabilities_add(
+ &capabilities->codecs,
+ MPF_SAMPLE_RATE_8000 | MPF_SAMPLE_RATE_16000,
+ "LPCM");
+
+ /* create media termination */
+ termination = mrcp_engine_audio_termination_create(
+ recorder_channel, /* object to associate */
+ &audio_stream_vtable, /* virtual methods table of audio stream */
+ capabilities, /* stream capabilities */
+ pool); /* pool to allocate memory from */
+
+ /* create engine channel base */
+ recorder_channel->channel = mrcp_engine_channel_create(
+ engine, /* engine */
+ &channel_vtable, /* virtual methods table of engine channel */
+ recorder_channel, /* object to associate */
+ termination, /* associated media termination */
+ pool); /* pool to allocate memory from */
+
+ return recorder_channel->channel;
+}
+
+/** Destroy engine channel */
+static apt_bool_t recorder_channel_destroy(mrcp_engine_channel_t *channel)
+{
+ /* nothing to destrtoy */
+ return TRUE;
+}
+
+/** Open engine channel (asynchronous response MUST be sent)*/
+static apt_bool_t recorder_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 recorder_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);
+}
+
+/** Open file to record */
+static apt_bool_t recorder_file_open(recorder_channel_t *recorder_channel, mrcp_message_t *request)
+{
+ mrcp_engine_channel_t *channel = recorder_channel->channel;
+ const apt_dir_layout_t *dir_layout = channel->engine->dir_layout;
+ const mpf_codec_descriptor_t *descriptor = mrcp_engine_sink_stream_codec_get(channel);
+ char *file_name = apr_psprintf(channel->pool,"rec-%dkHz-%s-%"MRCP_REQUEST_ID_FMT".pcm",
+ descriptor ? descriptor->sampling_rate/1000 : 8,
+ request->channel_id.session_id.buf,
+ request->start_line.request_id);
+ char *file_path = apt_datadir_filepath_get(dir_layout,file_name,channel->pool);
+ if(!file_path) {
+ return FALSE;
+ }
+
+ if(recorder_channel->audio_out) {
+ fclose(recorder_channel->audio_out);
+ recorder_channel->audio_out = NULL;
+ }
+
+ recorder_channel->audio_out = fopen(file_path,"wb");
+ if(!recorder_channel->audio_out) {
+ return FALSE;
+ }
+
+ recorder_channel->file_name = file_name;
+ return TRUE;
+}
+
+/** Set Record-URI header field */
+static apt_bool_t recorder_channel_uri_set(recorder_channel_t *recorder_channel, mrcp_message_t *message)
+{
+ char *record_uri;
+ /* get/allocate recorder header */
+ mrcp_recorder_header_t *recorder_header = mrcp_resource_header_prepare(message);
+ if(!recorder_header) {
+ return FALSE;
+ }
+
+ record_uri = apr_psprintf(message->pool,"<file://mediaserver/data/%s>;size=%d;duration=%d",
+ recorder_channel->file_name,
+ recorder_channel->cur_size,
+ recorder_channel->cur_time);
+
+ apt_string_set(&recorder_header->record_uri,record_uri);
+ mrcp_resource_header_property_add(message,RECORDER_HEADER_RECORD_URI);
+ return TRUE;
+}
+
+/** Process RECORD request */
+static apt_bool_t recorder_channel_record(recorder_channel_t *recorder_channel, mrcp_message_t *request, mrcp_message_t *response)
+{
+ /* process RECORD request */
+ mrcp_recorder_header_t *recorder_header;
+ recorder_channel->timers_started = TRUE;
+
+ /* get recorder header */
+ recorder_header = mrcp_resource_header_get(request);
+ if(recorder_header) {
+ if(mrcp_resource_header_property_check(request,RECORDER_HEADER_START_INPUT_TIMERS) == TRUE) {
+ recorder_channel->timers_started = recorder_header->start_input_timers;
+ }
+ if(mrcp_resource_header_property_check(request,RECORDER_HEADER_NO_INPUT_TIMEOUT) == TRUE) {
+ mpf_activity_detector_noinput_timeout_set(recorder_channel->detector,recorder_header->no_input_timeout);
+ }
+ if(mrcp_resource_header_property_check(request,RECORDER_HEADER_FINAL_SILENCE) == TRUE) {
+ mpf_activity_detector_silence_timeout_set(recorder_channel->detector,recorder_header->final_silence);
+ }
+ if(mrcp_resource_header_property_check(request,RECORDER_HEADER_MAX_TIME) == TRUE) {
+ recorder_channel->max_time = recorder_header->max_time;
+ }
+ }
+
+ /* open file to record */
+ if(recorder_file_open(recorder_channel,request) == FALSE) {
+ 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(recorder_channel->channel,response);
+ return TRUE;
+ }
+
+ recorder_channel->cur_time = 0;
+ recorder_channel->cur_size = 0;
+ response->start_line.request_state = MRCP_REQUEST_STATE_INPROGRESS;
+ /* send asynchronous response */
+ mrcp_engine_channel_message_send(recorder_channel->channel,response);
+ recorder_channel->record_request = request;
+ return TRUE;
+}
+
+/** Process STOP request */
+static apt_bool_t recorder_channel_stop(recorder_channel_t *recorder_channel, mrcp_message_t *request, mrcp_message_t *response)
+{
+ /* store STOP request, make sure there is no more activity and only then send the response */
+ recorder_channel->stop_response = response;
+ return TRUE;
+}
+
+/** Process START-INPUT-TIMERS request */
+static apt_bool_t recorder_channel_timers_start(recorder_channel_t *recorder_channel, mrcp_message_t *request, mrcp_message_t *response)
+{
+ recorder_channel->timers_started = TRUE;
+ return mrcp_engine_channel_message_send(recorder_channel->channel,response);
+}
+
+/** Process MRCP channel request (asynchronous response MUST be sent)*/
+static apt_bool_t recorder_channel_request_process(mrcp_engine_channel_t *channel, mrcp_message_t *request)
+{
+ apt_bool_t processed = FALSE;
+ recorder_channel_t *recorder_channel = channel->method_obj;
+ mrcp_message_t *response = mrcp_response_create(request,request->pool);
+ switch(request->start_line.method_id) {
+ case RECORDER_SET_PARAMS:
+ break;
+ case RECORDER_GET_PARAMS:
+ break;
+ case RECORDER_RECORD:
+ processed = recorder_channel_record(recorder_channel,request,response);
+ break;
+ case RECORDER_STOP:
+ processed = recorder_channel_stop(recorder_channel,request,response);
+ break;
+ case RECORDER_START_INPUT_TIMERS:
+ processed = recorder_channel_timers_start(recorder_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;
+}
+
+/* Raise START-OF-INPUT event */
+static apt_bool_t recorder_start_of_input(recorder_channel_t *recorder_channel)
+{
+ /* create START-OF-INPUT event */
+ mrcp_message_t *message = mrcp_event_create(
+ recorder_channel->record_request,
+ RECORDER_START_OF_INPUT,
+ recorder_channel->record_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(recorder_channel->channel,message);
+}
+
+/* Raise RECORD-COMPLETE event */
+static apt_bool_t recorder_record_complete(recorder_channel_t *recorder_channel, mrcp_recorder_completion_cause_e cause)
+{
+ mrcp_recorder_header_t *recorder_header;
+ /* create RECORD-COMPLETE event */
+ mrcp_message_t *message = mrcp_event_create(
+ recorder_channel->record_request,
+ RECORDER_RECORD_COMPLETE,
+ recorder_channel->record_request->pool);
+ if(!message) {
+ return FALSE;
+ }
+
+ if(recorder_channel->audio_out) {
+ fclose(recorder_channel->audio_out);
+ recorder_channel->audio_out = NULL;
+ }
+
+ /* get/allocate recorder header */
+ recorder_header = mrcp_resource_header_prepare(message);
+ if(recorder_header) {
+ /* set completion cause */
+ recorder_header->completion_cause = cause;
+ mrcp_resource_header_property_add(message,RECORDER_HEADER_COMPLETION_CAUSE);
+ }
+ /* set record-uri */
+ recorder_channel_uri_set(recorder_channel,message);
+ /* set request state */
+ message->start_line.request_state = MRCP_REQUEST_STATE_COMPLETE;
+
+ recorder_channel->record_request = NULL;
+ /* send asynch event */
+ return mrcp_engine_channel_message_send(recorder_channel->channel,message);
+}
+
+/** Callback is called from MPF engine context to destroy any additional data associated with audio stream */
+static apt_bool_t recorder_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 recorder_stream_open(mpf_audio_stream_t *stream, mpf_codec_t *codec)
+{
+ return TRUE;
+}
+
+/** Callback is called from MPF engine context to perform any action after close */
+static apt_bool_t recorder_stream_close(mpf_audio_stream_t *stream)
+{
+ return TRUE;
+}
+
+/** Callback is called from MPF engine context to write/send new frame */
+static apt_bool_t recorder_stream_write(mpf_audio_stream_t *stream, const mpf_frame_t *frame)
+{
+ recorder_channel_t *recorder_channel = stream->obj;
+ if(recorder_channel->stop_response) {
+ if(recorder_channel->audio_out) {
+ fclose(recorder_channel->audio_out);
+ recorder_channel->audio_out = NULL;
+ }
+
+ if(recorder_channel->record_request){
+ /* set record-uri */
+ recorder_channel_uri_set(recorder_channel,recorder_channel->stop_response);
+ }
+ /* send asynchronous response to STOP request */
+ mrcp_engine_channel_message_send(recorder_channel->channel,recorder_channel->stop_response);
+ recorder_channel->stop_response = NULL;
+ recorder_channel->record_request = NULL;
+ return TRUE;
+ }
+
+ if(recorder_channel->record_request) {
+ mpf_detector_event_e det_event = mpf_activity_detector_process(recorder_channel->detector,frame);
+ switch(det_event) {
+ case MPF_DETECTOR_EVENT_ACTIVITY:
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Detected Voice Activity");
+ recorder_start_of_input(recorder_channel);
+ break;
+ case MPF_DETECTOR_EVENT_INACTIVITY:
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Detected Voice Inactivity");
+ recorder_record_complete(recorder_channel,RECORDER_COMPLETION_CAUSE_SUCCESS_SILENCE);
+ break;
+ case MPF_DETECTOR_EVENT_NOINPUT:
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Detected Noinput");
+ if(recorder_channel->timers_started == TRUE) {
+ recorder_record_complete(recorder_channel,RECORDER_COMPLETION_CAUSE_NO_INPUT_TIMEOUT);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if(recorder_channel->audio_out) {
+ fwrite(frame->codec_frame.buffer,1,frame->codec_frame.size,recorder_channel->audio_out);
+
+ recorder_channel->cur_size += frame->codec_frame.size;
+ recorder_channel->cur_time += CODEC_FRAME_TIME_BASE;
+ if(recorder_channel->max_time && recorder_channel->cur_time >= recorder_channel->max_time) {
+ recorder_record_complete(recorder_channel,RECORDER_COMPLETION_CAUSE_SUCCESS_MAXTIME);
+ }
+ }
+ }
+ return TRUE;
+}
#include <apr_thread_cond.h>
#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"
/** 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;
+ /** MPF engine */
+ mpf_engine_t *engine;
/** RTP termination factory */
mpf_termination_factory_t *rtp_termination_factory;
/** File termination factory */
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 apt_bool_t mpf_suite_task_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);
suite_engine = apr_palloc(suite->pool,sizeof(mpf_suite_engine_t));
- engine = mpf_engine_create(suite->pool);
+ engine = mpf_engine_create(1,suite->pool);
if(!engine) {
apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create MPF Engine");
return FALSE;
if(codec_manager) {
mpf_engine_codec_manager_register(engine,codec_manager);
}
- suite_engine->engine_task = mpf_task_get(engine);
+ suite_engine->engine = engine;
config = mpf_rtp_config_create(suite->pool);
apt_string_set(&config->ip,"127.0.0.1");
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->process_msg = mpf_suite_task_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);
+ apt_task_add(task,mpf_task_get(engine));
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);
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;
+ mpf_task_msg_t *task_msg = NULL;
+ void *descriptor;
apr_pool_t *pool = NULL;
consumer_task = apt_task_object_get(task);
session->rtp_mode = TRUE;
apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Create MPF Context");
- session->context = mpf_context_create(session,2,pool);
+ session->context = mpf_engine_context_create(suite_engine->engine,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);
+ descriptor = mpf_file_reader_descriptor_create(session);
+ mpf_engine_termination_message_add(
+ suite_engine->engine,
+ MPF_ADD_TERMINATION,session->context,session->termination1,descriptor,
+ &task_msg);
apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Create Termination [2]");
if(session->rtp_mode == TRUE) {
}
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;
+ descriptor = NULL;
if(session->rtp_mode == TRUE) {
- mpf_message->descriptor = mpf_rtp_local_descriptor_create(session);
+ descriptor = mpf_rtp_local_descriptor_create(session);
}
else {
- mpf_message->descriptor = mpf_file_writer_descriptor_create(session);
+ descriptor = mpf_file_writer_descriptor_create(session);
}
- apt_task_msg_signal(suite_engine->engine_task,msg);
+
+ mpf_engine_termination_message_add(
+ suite_engine->engine,
+ MPF_ADD_TERMINATION,session->context,session->termination2,descriptor,
+ &task_msg);
+
+ mpf_engine_message_send(suite_engine->engine,&task_msg);
}
/** Execution of MPF test suite scenario is terminated */
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)
+/** Process MPF response */
+static apt_bool_t mpf_suite_response_process(mpf_suite_engine_t *suite_engine, const mpf_message_t *mpf_message)
{
- 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);
- }
+ mpf_task_msg_t *task_msg = NULL;
+ apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Process MPF Response");
+ if(mpf_message->command_id == MPF_ADD_TERMINATION) {
+ 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) {
+ void *descriptor = mpf_rtp_remote_descriptor_create(session);
+ mpf_engine_termination_message_add(
+ suite_engine->engine,
+ MPF_MODIFY_TERMINATION,session->context,session->termination2,descriptor,
+ &task_msg);
}
}
}
- 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");
+ else if(mpf_message->command_id == MPF_SUBTRACT_TERMINATION) {
+ 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) {
- 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->termination1 == mpf_message->termination) {
+ session->termination1 = NULL;
}
- 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);
+ if(session->termination2 == mpf_message->termination) {
+ session->termination2 = NULL;
}
+ mpf_termination_destroy(mpf_message->termination);
+
+ if(!session->termination1 && !session->termination2) {
+ mpf_engine_context_destroy(session->context);
+ session->context = NULL;
+ apr_pool_destroy(session->pool);
+
+ 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);
+ }
+ }
+ }
+ return mpf_engine_message_send(suite_engine->engine,&task_msg);
+}
+
+/** Process MPF event */
+static apt_bool_t mpf_suite_event_process(mpf_suite_engine_t *suite_engine, const mpf_message_t *mpf_message)
+{
+ mpf_task_msg_t *task_msg = NULL;
+ 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]");
+ mpf_engine_termination_message_add(
+ suite_engine->engine,
+ MPF_SUBTRACT_TERMINATION,session->context,session->termination1,NULL,
+ &task_msg);
+ }
+ if(session->termination2) {
+ apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Subtract Termination [2]");
+ mpf_engine_termination_message_add(
+ suite_engine->engine,
+ MPF_SUBTRACT_TERMINATION,session->context,session->termination2,NULL,
+ &task_msg);
}
}
+ return mpf_engine_message_send(suite_engine->engine,&task_msg);
+}
+/** Process task messages */
+static apt_bool_t mpf_suite_task_msg_process(apt_task_t *task, apt_task_msg_t *msg)
+{
+ apr_size_t i;
+ const mpf_message_t *mpf_message;
+ apt_task_t *consumer_task = apt_task_object_get(task);
+ mpf_suite_engine_t *suite_engine = apt_task_object_get(consumer_task);
+ const mpf_message_container_t *container = (const mpf_message_container_t*) msg->data;
+ for(i=0; i<container->count; i++) {
+ mpf_message = &container->messages[i];
+ if(mpf_message->message_type == MPF_MESSAGE_TYPE_RESPONSE) {
+ mpf_suite_response_process(suite_engine,mpf_message);
+ }
+ else {
+ mpf_suite_event_process(suite_engine,mpf_message);
+ }
+ }
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 = 96;
- apt_string_set(&codec_descriptor->name,"LPCM");
- codec_descriptor->sampling_rate = 8000;
- codec_descriptor->channel_count = 1;
+ descriptor->codec_descriptor = mpf_codec_lpcm_descriptor_create(8000,1,session->pool);
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 = 96;
- apt_string_set(&codec_descriptor->name,"LPCM");
- codec_descriptor->sampling_rate = 8000;
- codec_descriptor->channel_count = 1;
+ descriptor->codec_descriptor = mpf_codec_lpcm_descriptor_create(8000,1,session->pool);
return descriptor;
}
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;
+ descriptor->local->direction = STREAM_DIRECTION_NONE;
+ apt_string_set(&descriptor->local->ip,"127.0.0.1");
+ descriptor->local->port = 5000;
return descriptor;
}
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;
+ descriptor->remote->direction = STREAM_DIRECTION_DUPLEX;
+ apt_string_set(&descriptor->remote->ip,"127.0.0.1");
+ descriptor->remote->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);
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
- Version="8,00"
+ Version="8.00"
Name="mrcptest"
ProjectGUID="{3CA97077-6210-4362-998A-D15A35EEAA08}"
RootNamespace="mrcptest"
#include <apr_file_io.h>
#include "apt_test_suite.h"
#include "apt_log.h"
-#include "mrcp_default_factory.h"
+#include "mrcp_resource_loader.h"
+#include "mrcp_resource_factory.h"
#include "mrcp_message.h"
#include "mrcp_stream.h"
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);
+ mrcp_resource_factory_t *factory;
+ mrcp_resource_loader_t *resource_loader;
+ resource_loader = mrcp_resource_loader_create(TRUE,suite->pool);
+ if(!resource_loader) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Resource Loader");
+ return FALSE;
+ }
+
+ factory = mrcp_resource_factory_get(resource_loader);
if(!factory) {
apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Resource Factory");
return FALSE;
#include "apt_test_suite.h"
#include "apt_log.h"
/* common includes */
-#include "mrcp_default_factory.h"
+#include "mrcp_resource_loader.h"
+#include "mrcp_resource_factory.h"
#include "mrcp_message.h"
#include "mrcp_generic_header.h"
/* synthesizer includes */
static mrcp_message_t* speak_request_create(mrcp_resource_factory_t *factory, apr_pool_t *pool)
{
mrcp_message_t *message;
+ mrcp_resource_t *resource = mrcp_resource_get(factory,MRCP_SYNTHESIZER_RESOURCE);
+ if(!resource) {
+ return NULL;
+ }
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);
+ message = mrcp_request_create(resource,MRCP_VERSION_2,SYNTHESIZER_SPEAK,pool);
if(message) {
mrcp_generic_header_t *generic_header;
mrcp_synth_header_t *synth_header;
static mrcp_message_t* get_params_request_create(mrcp_resource_factory_t *factory, apr_pool_t *pool)
{
mrcp_message_t *message;
+ mrcp_resource_t *resource = mrcp_resource_get(factory,MRCP_SYNTHESIZER_RESOURCE);
+ if(!resource) {
+ return NULL;
+ }
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);
+ message = mrcp_request_create(resource,MRCP_VERSION_2,SYNTHESIZER_GET_PARAMS,pool);
if(message) {
apt_str_t param_name;
apt_str_t param_value;
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);
+ mrcp_resource_factory_t *factory;
+ mrcp_resource_loader_t *resource_loader;
+ resource_loader = mrcp_resource_loader_create(TRUE,suite->pool);
+ if(!resource_loader) {
+ apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Resource Loader");
+ return FALSE;
+ }
+
+ factory = mrcp_resource_factory_get(resource_loader);
if(!factory) {
apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Resource Factory");
return FALSE;
*/
#include <stdio.h>
+#include <ctype.h>
#include "apt_pool.h"
#include "apt_string_table.h"
#include "apt_text_stream.h"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mrcpsignaling", "libs\mrcp-signaling\mrcpsignaling.vcproj", "{12A49562-BAB9-43A3-A21D-15B60BBB4C31}"
ProjectSection(ProjectDependencies) = postProject
- {1C320193-46A6-4B34-9C56-8AB584FC1B56} = {1C320193-46A6-4B34-9C56-8AB584FC1B56}
{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
- {A9EDAC04-6A5F-4BA7-BC0D-CCE7B255B6EA} = {A9EDAC04-6A5F-4BA7-BC0D-CCE7B255B6EA}
{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
- {746F3632-5BB2-4570-9453-31D6D58A7D8E} = {746F3632-5BB2-4570-9453-31D6D58A7D8E}
- {18B1F35A-10F8-4287-9B37-2D10501B0B38} = {18B1F35A-10F8-4287-9B37-2D10501B0B38}
{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
- {A9EDAC04-6A5F-4BA7-BC0D-CCE7B255B6EA} = {A9EDAC04-6A5F-4BA7-BC0D-CCE7B255B6EA}
{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}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libunimrcpclient", "platforms\libunimrcp-client\libunimrcpclient.vcproj", "{EE157390-1E85-416C-946E-620E32C9AD33}"
ProjectSection(ProjectDependencies) = postProject
- {72782932-37CC-46AE-8C7F-9A7B1A6EE108} = {72782932-37CC-46AE-8C7F-9A7B1A6EE108}
- {746F3632-5BB2-4570-9453-31D6D58A7D8E} = {746F3632-5BB2-4570-9453-31D6D58A7D8E}
{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}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mrcpengine", "libs\mrcp-engine\mrcpengine.vcproj", "{843425BE-9A9A-44F4-A4E3-4B57D6ABD53C}"
ProjectSection(ProjectDependencies) = postProject
- {1C320193-46A6-4B34-9C56-8AB584FC1B56} = {1C320193-46A6-4B34-9C56-8AB584FC1B56}
{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}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mrcpunirtsp", "modules\mrcp-unirtsp\mrcpunirtsp.vcproj", "{DEB01ACB-D65F-4A62-AED9-58C1054499E9}"
ProjectSection(ProjectDependencies) = postProject
- {504B3154-7A4F-459D-9877-B951021C3F1F} = {504B3154-7A4F-459D-9877-B951021C3F1F}
{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}") = "preparesphinx", "build\tools\preparesphinx.vcproj", "{71D62A04-8EF6-4C6B-AC12-0C15A875E53A}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "asrclient", "platforms\asr-client\asrclient.vcproj", "{6B83AC6D-01CE-4E1C-81CE-02AD8116C684}"
+ ProjectSection(ProjectDependencies) = postProject
+ {272FAFA8-2B2F-4716-B95F-3B37CF2E0CB3} = {272FAFA8-2B2F-4716-B95F-3B37CF2E0CB3}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libasrclient", "platforms\libasr-client\libasrclient.vcproj", "{272FAFA8-2B2F-4716-B95F-3B37CF2E0CB3}"
+ ProjectSection(ProjectDependencies) = postProject
+ {EE157390-1E85-416C-946E-620E32C9AD33} = {EE157390-1E85-416C-946E-620E32C9AD33}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "umc", "platforms\umc\umc.vcproj", "{CD1C52C1-D8E1-4654-AE65-6CCAB38DE894}"
+ ProjectSection(ProjectDependencies) = postProject
+ {EE157390-1E85-416C-946E-620E32C9AD33} = {EE157390-1E85-416C-946E-620E32C9AD33}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mrcprecorder", "plugins\mrcp-recorder\mrcprecorder.vcproj", "{5AFB8B04-AEB9-408C-B53E-AFBC44B5F3F2}"
+ ProjectSection(ProjectDependencies) = postProject
+ {843425BE-9A9A-44F4-A4E3-4B57D6ABD53C} = {843425BE-9A9A-44F4-A4E3-4B57D6ABD53C}
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
{56F6FB96-2BC7-4CAE-A8BF-6A0FAEC90556}.Release|Win32.ActiveCfg = Release|Win32
{71D62A04-8EF6-4C6B-AC12-0C15A875E53A}.Debug|Win32.ActiveCfg = Debug|Win32
{71D62A04-8EF6-4C6B-AC12-0C15A875E53A}.Release|Win32.ActiveCfg = Release|Win32
+ {6B83AC6D-01CE-4E1C-81CE-02AD8116C684}.Debug|Win32.ActiveCfg = Debug|Win32
+ {6B83AC6D-01CE-4E1C-81CE-02AD8116C684}.Debug|Win32.Build.0 = Debug|Win32
+ {6B83AC6D-01CE-4E1C-81CE-02AD8116C684}.Release|Win32.ActiveCfg = Release|Win32
+ {6B83AC6D-01CE-4E1C-81CE-02AD8116C684}.Release|Win32.Build.0 = Release|Win32
+ {272FAFA8-2B2F-4716-B95F-3B37CF2E0CB3}.Debug|Win32.ActiveCfg = Debug|Win32
+ {272FAFA8-2B2F-4716-B95F-3B37CF2E0CB3}.Debug|Win32.Build.0 = Debug|Win32
+ {272FAFA8-2B2F-4716-B95F-3B37CF2E0CB3}.Release|Win32.ActiveCfg = Release|Win32
+ {272FAFA8-2B2F-4716-B95F-3B37CF2E0CB3}.Release|Win32.Build.0 = Release|Win32
+ {CD1C52C1-D8E1-4654-AE65-6CCAB38DE894}.Debug|Win32.ActiveCfg = Debug|Win32
+ {CD1C52C1-D8E1-4654-AE65-6CCAB38DE894}.Debug|Win32.Build.0 = Debug|Win32
+ {CD1C52C1-D8E1-4654-AE65-6CCAB38DE894}.Release|Win32.ActiveCfg = Release|Win32
+ {CD1C52C1-D8E1-4654-AE65-6CCAB38DE894}.Release|Win32.Build.0 = Release|Win32
+ {5AFB8B04-AEB9-408C-B53E-AFBC44B5F3F2}.Debug|Win32.ActiveCfg = Debug|Win32
+ {5AFB8B04-AEB9-408C-B53E-AFBC44B5F3F2}.Debug|Win32.Build.0 = Debug|Win32
+ {5AFB8B04-AEB9-408C-B53E-AFBC44B5F3F2}.Release|Win32.ActiveCfg = Release|Win32
+ {5AFB8B04-AEB9-408C-B53E-AFBC44B5F3F2}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
{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}
+ {6B83AC6D-01CE-4E1C-81CE-02AD8116C684} = {8E282AE2-038C-49FE-AC67-BC9615AFD800}
+ {272FAFA8-2B2F-4716-B95F-3B37CF2E0CB3} = {8E282AE2-038C-49FE-AC67-BC9615AFD800}
+ {CD1C52C1-D8E1-4654-AE65-6CCAB38DE894} = {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}
{3C614AE8-B611-4D43-A9AF-1CAA440A9F69} = {09BABD45-8F30-4F99-B8B8-8DD78F6804DB}
{56F6FB96-2BC7-4CAE-A8BF-6A0FAEC90556} = {09BABD45-8F30-4F99-B8B8-8DD78F6804DB}
+ {5AFB8B04-AEB9-408C-B53E-AFBC44B5F3F2} = {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}