diff --git a/libs/unimrcp/AUTHORS b/libs/unimrcp/AUTHORS index 365eaf0026..d032c9e71a 100644 --- a/libs/unimrcp/AUTHORS +++ b/libs/unimrcp/AUTHORS @@ -4,9 +4,13 @@ Author(s): Contributor(s): Kamil Shakirov - Anthony Masse + Anthony Masse Vlad Socaciu Garmt - Patrick - Bayram - Mahmoud Hassan \ No newline at end of file + Patrick Nunes + Bayram Boyraz + Mahmoud Hassan + Michael Jerris + Carlos Pina Soares + Chaitanya Chokkareddy + Tomas Valenta diff --git a/libs/unimrcp/INSTALL b/libs/unimrcp/INSTALL index 50826f313a..b29a738be6 100644 --- a/libs/unimrcp/INSTALL +++ b/libs/unimrcp/INSTALL @@ -1,22 +1,31 @@ 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 =================== @@ -36,9 +45,11 @@ $ make install 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 @@ -100,4 +111,6 @@ libraries and the default configuration to the output directory. Output directory layout bin - binaries (unimrcpserver, unimrcpclient) and all the required dlls conf - configuration files -plugins - run-time loadable modules \ No newline at end of file +data - data files +log - log files +plugin - run-time loadable modules diff --git a/libs/unimrcp/Makefile.am b/libs/unimrcp/Makefile.am index a720714d6d..d2609d008f 100644 --- a/libs/unimrcp/Makefile.am +++ b/libs/unimrcp/Makefile.am @@ -31,7 +31,7 @@ def-conf: 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 diff --git a/libs/unimrcp/build/tools/prepare.vcproj b/libs/unimrcp/build/tools/prepare.vcproj index d2a792cfad..2708a8648a 100644 --- a/libs/unimrcp/build/tools/prepare.vcproj +++ b/libs/unimrcp/build/tools/prepare.vcproj @@ -25,7 +25,7 @@ > diff --git a/libs/unimrcp/conf/umcscenarios.xml b/libs/unimrcp/conf/umcscenarios.xml new file mode 100644 index 0000000000..ab02487165 --- /dev/null +++ b/libs/unimrcp/conf/umcscenarios.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + --> + + + + + + + + + + + + + diff --git a/libs/unimrcp/conf/unimrcpclient.xml b/libs/unimrcp/conf/unimrcpclient.xml index fc903ffbd8..42a18d3552 100644 --- a/libs/unimrcp/conf/unimrcpclient.xml +++ b/libs/unimrcp/conf/unimrcpclient.xml @@ -1,5 +1,12 @@ + + + + + + + @@ -50,7 +57,21 @@ - + + + + + + + + + + + diff --git a/libs/unimrcp/conf/unimrcpserver.xml b/libs/unimrcp/conf/unimrcpserver.xml index eac086bd4f..7e7c094e27 100644 --- a/libs/unimrcp/conf/unimrcpserver.xml +++ b/libs/unimrcp/conf/unimrcpserver.xml @@ -1,5 +1,12 @@ + + + + + + + @@ -46,8 +53,22 @@ - + + + + + + + + + + + @@ -58,6 +79,7 @@ + diff --git a/libs/unimrcp/configure.ac b/libs/unimrcp/configure.ac index dd7fe6ac1d..ac8174e752 100644 --- a/libs/unimrcp/configure.ac +++ b/libs/unimrcp/configure.ac @@ -3,7 +3,7 @@ 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]) @@ -31,7 +31,7 @@ AC_PROG_LIBTOOL # Get version information get_version="build/get-version.sh" version_hdr="build/uni_version.h" -plugin_version_hdr="libs/mrcp-engine/include/mrcp_resource_plugin.h" +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`" @@ -117,6 +117,15 @@ AC_ARG_ENABLE(demorecog-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])], @@ -182,6 +191,7 @@ AC_CONFIG_FILES([ 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 @@ -189,6 +199,9 @@ AC_CONFIG_FILES([ 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 diff --git a/libs/unimrcp/data/demo-16kHz.pcm b/libs/unimrcp/data/demo-16kHz.pcm new file mode 100644 index 0000000000..01ace40952 Binary files /dev/null and b/libs/unimrcp/data/demo-16kHz.pcm differ diff --git a/libs/unimrcp/data/demo.pcm b/libs/unimrcp/data/demo-8kHz.pcm similarity index 100% rename from libs/unimrcp/data/demo.pcm rename to libs/unimrcp/data/demo-8kHz.pcm diff --git a/libs/unimrcp/data/grammar.jsgf b/libs/unimrcp/data/grammar.jsgf new file mode 100644 index 0000000000..df19bb9fd9 --- /dev/null +++ b/libs/unimrcp/data/grammar.jsgf @@ -0,0 +1,3 @@ +#JSGF V1.0; +grammar digits; +public = (one | two | three); diff --git a/libs/unimrcp/data/one-16kHz.pcm b/libs/unimrcp/data/one-16kHz.pcm new file mode 100644 index 0000000000..44e0b3431a Binary files /dev/null and b/libs/unimrcp/data/one-16kHz.pcm differ diff --git a/libs/unimrcp/data/one.pcm b/libs/unimrcp/data/one-8kHz.pcm similarity index 100% rename from libs/unimrcp/data/one.pcm rename to libs/unimrcp/data/one-8kHz.pcm diff --git a/libs/unimrcp/data/speak.txt b/libs/unimrcp/data/speak.txt new file mode 100644 index 0000000000..3245b20dc9 --- /dev/null +++ b/libs/unimrcp/data/speak.txt @@ -0,0 +1 @@ +Hello World. \ No newline at end of file diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_cyclic_queue.h b/libs/unimrcp/libs/apr-toolkit/include/apt_cyclic_queue.h index c36fcd21c8..7c6ea3ab08 100644 --- a/libs/unimrcp/libs/apr-toolkit/include/apt_cyclic_queue.h +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_cyclic_queue.h @@ -26,6 +26,7 @@ APT_BEGIN_EXTERN_C +/** Default size (number of elements) of cyclic queue */ #define CYCLIC_QUEUE_DEFAULT_SIZE 100 /** Opaque cyclic queue declaration */ diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_log.h b/libs/unimrcp/libs/apr-toolkit/include/apt_log.h index d3b38abf83..aee0adb219 100644 --- a/libs/unimrcp/libs/apr-toolkit/include/apt_log.h +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_log.h @@ -98,12 +98,12 @@ APT_DECLARE(apt_bool_t) apt_log_instance_create(apt_log_output_e mode, apt_log_p /** * 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. @@ -128,7 +128,7 @@ APT_DECLARE(apt_bool_t) apt_log_file_open( /** * 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. diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_net.h b/libs/unimrcp/libs/apr-toolkit/include/apt_net.h index d3792f2dd1..7c9d109851 100644 --- a/libs/unimrcp/libs/apr-toolkit/include/apt_net.h +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_net.h @@ -33,6 +33,14 @@ APT_BEGIN_EXTERN_C */ 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__*/ diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_nlsml_doc.h b/libs/unimrcp/libs/apr-toolkit/include/apt_nlsml_doc.h index 85f8476db4..ab8724598f 100644 --- a/libs/unimrcp/libs/apr-toolkit/include/apt_nlsml_doc.h +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_nlsml_doc.h @@ -30,16 +30,16 @@ APT_BEGIN_EXTERN_C /** Load NLSML document */ APT_DECLARE(apr_xml_doc*) nlsml_doc_load(const apt_str_t *data, apr_pool_t *pool); -/** Get the first element */ +/** Get the first interpretation element */ APT_DECLARE(apr_xml_elem*) nlsml_first_interpret_get(const apr_xml_doc *doc); -/** Get the next element */ +/** Get the next interpretation element */ APT_DECLARE(apr_xml_elem*) nlsml_next_interpret_get(const apr_xml_elem *interpret); -/** Get and elements of 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 */ +/** 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); diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_pool.h b/libs/unimrcp/libs/apr-toolkit/include/apt_pool.h index 085195bee4..b2bff52688 100644 --- a/libs/unimrcp/libs/apr-toolkit/include/apt_pool.h +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_pool.h @@ -34,7 +34,7 @@ APT_BEGIN_EXTERN_C /** * Create APR pool */ -APT_DECLARE(apr_pool_t*) apt_pool_create(); +APT_DECLARE(apr_pool_t*) apt_pool_create(void); /** * Create APR subpool pool diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_task.h b/libs/unimrcp/libs/apr-toolkit/include/apt_task.h index a2117d72d3..29c2dea399 100644 --- a/libs/unimrcp/libs/apr-toolkit/include/apt_task.h +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_task.h @@ -55,8 +55,8 @@ APT_DECLARE(apt_task_t*) apt_task_create( 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); @@ -157,6 +157,19 @@ APT_DECLARE(void) apt_task_name_set(apt_task_t *task, const char *name); */ 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 diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_test_suite.h b/libs/unimrcp/libs/apr-toolkit/include/apt_test_suite.h index a703de7c03..3e1b2cbeca 100644 --- a/libs/unimrcp/libs/apr-toolkit/include/apt_test_suite.h +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_test_suite.h @@ -65,7 +65,7 @@ typedef struct apt_test_framework_t apt_test_framework_t; /** * 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. diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_consumer_task.c b/libs/unimrcp/libs/apr-toolkit/src/apt_consumer_task.c index 42a8754347..3b96373a74 100644 --- a/libs/unimrcp/libs/apr-toolkit/src/apt_consumer_task.c +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_consumer_task.c @@ -16,6 +16,7 @@ #include #include "apt_consumer_task.h" +#include "apt_log.h" struct apt_consumer_task_t { void *obj; @@ -85,6 +86,7 @@ static apt_bool_t apt_consumer_task_run(apt_task_t *task) } 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) { diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_log.c b/libs/unimrcp/libs/apr-toolkit/src/apt_log.c index 25a5abf5d5..a78e61c54c 100644 --- a/libs/unimrcp/libs/apr-toolkit/src/apt_log.c +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_log.c @@ -257,7 +257,7 @@ static apt_bool_t apt_do_log(const char *file, int line, apt_log_priority_e prio 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) { diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_net.c b/libs/unimrcp/libs/apr-toolkit/src/apt_net.c index bf5b8a9f92..733f0e20bc 100644 --- a/libs/unimrcp/libs/apr-toolkit/src/apt_net.c +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_net.c @@ -33,3 +33,20 @@ apt_bool_t apt_ip_get(char **addr, apr_pool_t *pool) } 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); +} diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_net_client_task.c b/libs/unimrcp/libs/apr-toolkit/src/apt_net_client_task.c index 6e59440cbd..6effe42f3c 100644 --- a/libs/unimrcp/libs/apr-toolkit/src/apt_net_client_task.c +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_net_client_task.c @@ -74,6 +74,7 @@ APT_DECLARE(apt_net_client_task_t*) apt_net_client_task_create( 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); @@ -279,6 +280,9 @@ static apt_bool_t apt_net_client_task_run(apt_task_t *base) 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) { diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_net_server_task.c b/libs/unimrcp/libs/apr-toolkit/src/apt_net_server_task.c index 4d994f4bed..93cc9f5d69 100644 --- a/libs/unimrcp/libs/apr-toolkit/src/apt_net_server_task.c +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_net_server_task.c @@ -89,6 +89,7 @@ APT_DECLARE(apt_net_server_task_t*) apt_net_server_task_create( 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); @@ -324,6 +325,9 @@ static apt_bool_t apt_net_server_task_run(apt_task_t *base) 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) { diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_pair.c b/libs/unimrcp/libs/apr-toolkit/src/apt_pair.c index 816eea264e..982ab1093a 100644 --- a/libs/unimrcp/libs/apr-toolkit/src/apt_pair.c +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_pair.c @@ -34,8 +34,8 @@ APT_DECLARE(apt_pair_arr_t*) apt_pair_array_copy(const apt_pair_arr_t *src_arr, } arr = apr_array_copy(pool,src_arr); for(i=0; inelts; i++) { - pair = (apt_pair_t*)arr->elts + i; - src_pair = (const apt_pair_t*)src_arr->elts + i; + 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; @@ -62,7 +62,7 @@ APT_DECLARE(const apt_pair_t*) apt_pair_array_find(const apt_pair_arr_t *arr, co int i; apt_pair_t *pair; for(i=0; inelts; 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; } diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_pool.c b/libs/unimrcp/libs/apr-toolkit/src/apt_pool.c index 9de0e3fa93..8f75400836 100644 --- a/libs/unimrcp/libs/apr-toolkit/src/apt_pool.c +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_pool.c @@ -16,7 +16,7 @@ #include "apt_pool.h" -//#define OWN_ALLOCATOR_PER_POOL +#define OWN_ALLOCATOR_PER_POOL APT_DECLARE(apr_pool_t*) apt_pool_create() { @@ -31,6 +31,7 @@ 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 diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_task.c b/libs/unimrcp/libs/apr-toolkit/src/apt_task.c index 0c8f3d0465..355a7a62c9 100644 --- a/libs/unimrcp/libs/apr-toolkit/src/apt_task.c +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_task.c @@ -40,6 +40,7 @@ struct apt_task_t { 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 */ }; @@ -75,6 +76,7 @@ APT_DECLARE(apt_task_t*) apt_task_create( task->child_tasks = apt_list_create(pool); task->pending_start = 0; task->pending_term = 0; + task->auto_ready = TRUE; task->name = "Task"; return task; } @@ -386,6 +388,23 @@ APT_DECLARE(apt_bool_t) apt_task_child_terminate(apt_task_t *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; @@ -398,8 +417,10 @@ static void* APR_THREAD_FUNC apt_task_run(apr_thread_t *thread_handle, void *dat 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) { diff --git a/libs/unimrcp/libs/mpf/Makefile.am b/libs/unimrcp/libs/mpf/Makefile.am index 4a73d91325..d153905278 100644 --- a/libs/unimrcp/libs/mpf/Makefile.am +++ b/libs/unimrcp/libs/mpf/Makefile.am @@ -18,17 +18,24 @@ include_HEADERS = codecs/g711/g711.h \ 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 \ @@ -39,8 +46,9 @@ include_HEADERS = codecs/g711/g711.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 \ @@ -52,14 +60,23 @@ libmpf_la_SOURCES = codecs/g711/g711.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 diff --git a/libs/unimrcp/libs/mpf/include/mpf_activity_detector.h b/libs/unimrcp/libs/mpf/include/mpf_activity_detector.h index 57729877c4..1b90b558d5 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_activity_detector.h +++ b/libs/unimrcp/libs/mpf/include/mpf_activity_detector.h @@ -23,7 +23,7 @@ */ #include "mpf_frame.h" -#include "mpf_codec.h" +#include "mpf_codec_descriptor.h" APT_BEGIN_EXTERN_C @@ -51,8 +51,11 @@ MPF_DECLARE(void) mpf_activity_detector_level_set(mpf_activity_detector_t *detec /** 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); diff --git a/libs/unimrcp/libs/mpf/include/mpf_audio_file_descriptor.h b/libs/unimrcp/libs/mpf/include/mpf_audio_file_descriptor.h index 4dfb6d3ebb..0d9c439c4c 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_audio_file_descriptor.h +++ b/libs/unimrcp/libs/mpf/include/mpf_audio_file_descriptor.h @@ -23,31 +23,30 @@ */ #include -#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 diff --git a/libs/unimrcp/libs/mpf/include/mpf_bridge.h b/libs/unimrcp/libs/mpf/include/mpf_bridge.h index ceb00713b9..395aaa966a 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_bridge.h +++ b/libs/unimrcp/libs/mpf/include/mpf_bridge.h @@ -30,17 +30,14 @@ APT_BEGIN_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 diff --git a/libs/unimrcp/libs/mpf/include/mpf_codec.h b/libs/unimrcp/libs/mpf/include/mpf_codec.h index 282ab693d8..04d9b27079 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_codec.h +++ b/libs/unimrcp/libs/mpf/include/mpf_codec.h @@ -39,9 +39,6 @@ struct mpf_codec_t { 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 */ @@ -77,7 +74,6 @@ static APR_INLINE mpf_codec_t* mpf_codec_create( codec->vtable = vtable; codec->attribs = attribs; codec->static_descriptor = descriptor; - codec->descriptor = NULL; return codec; } @@ -92,7 +88,6 @@ static APR_INLINE mpf_codec_t* mpf_codec_clone(mpf_codec_t *src_codec, apr_pool_ codec->vtable = src_codec->vtable; codec->attribs = src_codec->attribs; codec->static_descriptor = src_codec->static_descriptor; - codec->descriptor = src_codec->descriptor; return codec; } @@ -100,13 +95,8 @@ static APR_INLINE mpf_codec_t* mpf_codec_clone(mpf_codec_t *src_codec, apr_pool_ 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; } diff --git a/libs/unimrcp/libs/mpf/include/mpf_codec_descriptor.h b/libs/unimrcp/libs/mpf/include/mpf_codec_descriptor.h index 09fd325404..9f3a6ea1d7 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_codec_descriptor.h +++ b/libs/unimrcp/libs/mpf/include/mpf_codec_descriptor.h @@ -41,17 +41,22 @@ typedef enum { 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 list declaration */ -typedef struct mpf_codec_list_t mpf_codec_list_t; -/** Codec frame declaration */ -typedef struct mpf_codec_frame_t mpf_codec_frame_t; /** Codec attributes declaration */ typedef struct mpf_codec_attribs_t mpf_codec_attribs_t; +/** Codec 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 descriptor */ @@ -65,17 +70,37 @@ struct mpf_codec_descriptor_t { /** 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; + /** 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 */ +struct mpf_codec_attribs_t { + /** Codec name */ + apt_str_t name; + /** Bits per sample */ + 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 */ @@ -86,16 +111,6 @@ struct mpf_codec_frame_t { apr_size_t size; }; -/** Codec attributes */ -struct mpf_codec_attribs_t { - /** Codec name */ - apt_str_t name; - /** Bits per sample */ - apr_byte_t bits_per_samples; - /** Supported sampling rates (mpf_sample_rates_e) */ - int sample_rates; -}; - /** Initialize codec descriptor */ static APR_INLINE void mpf_codec_descriptor_init(mpf_codec_descriptor_t *descriptor) @@ -104,14 +119,22 @@ static APR_INLINE void mpf_codec_descriptor_init(mpf_codec_descriptor_t *descrip 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 */ } @@ -127,17 +150,22 @@ static APR_INLINE apr_size_t mpf_codec_linear_frame_size_calculate(apr_uint16_t 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 */ @@ -149,7 +177,7 @@ static APR_INLINE void mpf_codec_list_copy(mpf_codec_list_t *codec_list, const m /** 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; } @@ -161,20 +189,91 @@ static APR_INLINE apt_bool_t mpf_codec_list_is_empty(const mpf_codec_list_t *cod } /** 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 diff --git a/libs/unimrcp/libs/mpf/include/mpf_codec_manager.h b/libs/unimrcp/libs/mpf/include/mpf_codec_manager.h index ec2fdd8d19..2b14070b52 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_codec_manager.h +++ b/libs/unimrcp/libs/mpf/include/mpf_codec_manager.h @@ -39,9 +39,6 @@ MPF_DECLARE(apt_bool_t) mpf_codec_manager_codec_register(mpf_codec_manager_t *co /** 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); diff --git a/libs/unimrcp/libs/mpf/include/mpf_context.h b/libs/unimrcp/libs/mpf/include/mpf_context.h index 49f99e27b1..d9a20bf9ad 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_context.h +++ b/libs/unimrcp/libs/mpf/include/mpf_context.h @@ -22,31 +22,52 @@ * @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); -/** 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; +/** + * Destroy factory of media contexts. + */ +MPF_DECLARE(void) mpf_context_factory_destroy(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; -}; +/** + * Process factory of media contexts. + */ +MPF_DECLARE(apt_bool_t) mpf_context_factory_process(mpf_context_factory_t *factory); +/** + * 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. @@ -63,22 +84,42 @@ MPF_DECLARE(apt_bool_t) mpf_context_termination_add(mpf_context_t *context, mpf_ MPF_DECLARE(apt_bool_t) mpf_context_termination_subtract(mpf_context_t *context, mpf_termination_t *termination); /** - * Apply topology. - * @param context the context which holds the termination - * @param termination the termination to apply toplogy for + * 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_topology_apply(mpf_context_t *context, mpf_termination_t *termination); +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 to apply topology for + */ +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); diff --git a/libs/unimrcp/libs/mpf/include/mpf_decoder.h b/libs/unimrcp/libs/mpf/include/mpf_decoder.h index 0d40bd346c..ea99db8ad1 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_decoder.h +++ b/libs/unimrcp/libs/mpf/include/mpf_decoder.h @@ -29,9 +29,10 @@ APT_BEGIN_EXTERN_C /** * 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 diff --git a/libs/unimrcp/libs/mpf/include/mpf_dtmf_detector.h b/libs/unimrcp/libs/mpf/include/mpf_dtmf_detector.h new file mode 100644 index 0000000000..6c6d95768d --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_dtmf_detector.h @@ -0,0 +1,121 @@ +/* + * 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__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_dtmf_generator.h b/libs/unimrcp/libs/mpf/include/mpf_dtmf_generator.h new file mode 100644 index 0000000000..e5c8548bd6 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_dtmf_generator.h @@ -0,0 +1,131 @@ +/* + * 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__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_encoder.h b/libs/unimrcp/libs/mpf/include/mpf_encoder.h index 36a819da6a..529f9a0b7e 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_encoder.h +++ b/libs/unimrcp/libs/mpf/include/mpf_encoder.h @@ -29,9 +29,10 @@ APT_BEGIN_EXTERN_C /** * 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 diff --git a/libs/unimrcp/libs/mpf/include/mpf_engine.h b/libs/unimrcp/libs/mpf/include/mpf_engine.h index d1d38a61ea..e784d07d6c 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_engine.h +++ b/libs/unimrcp/libs/mpf/include/mpf_engine.h @@ -27,11 +27,15 @@ 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. @@ -46,6 +50,31 @@ MPF_DECLARE(mpf_codec_manager_t*) mpf_engine_codec_manager_create(apr_pool_t *po */ 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 @@ -59,6 +88,60 @@ MPF_DECLARE(apt_task_t*) mpf_task_get(mpf_engine_t *engine); */ 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 diff --git a/libs/unimrcp/libs/mpf/include/mpf_file_termination_factory.h b/libs/unimrcp/libs/mpf/include/mpf_file_termination_factory.h index f824328039..df8a05abd7 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_file_termination_factory.h +++ b/libs/unimrcp/libs/mpf/include/mpf_file_termination_factory.h @@ -22,7 +22,7 @@ * @brief MPF File Termination Factory */ -#include "mpf_types.h" +#include "mpf_termination_factory.h" APT_BEGIN_EXTERN_C diff --git a/libs/unimrcp/libs/mpf/include/mpf_frame.h b/libs/unimrcp/libs/mpf/include/mpf_frame.h index b1c8c438ac..3abba6e282 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_frame.h +++ b/libs/unimrcp/libs/mpf/include/mpf_frame.h @@ -23,6 +23,7 @@ */ #include "mpf_codec_descriptor.h" +#include "mpf_named_event.h" APT_BEGIN_EXTERN_C @@ -31,42 +32,26 @@ typedef enum { MEDIA_FRAME_TYPE_NONE = 0x0, /**< none */ MEDIA_FRAME_TYPE_AUDIO = 0x1, /**< audio frame */ MEDIA_FRAME_TYPE_VIDEO = 0x2, /**< video frame */ - MEDIA_FRAME_TYPE_EVENT = 0x4 /**< named event frame (RFC2833) */ + 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 */ diff --git a/libs/unimrcp/libs/mpf/include/mpf_jitter_buffer.h b/libs/unimrcp/libs/mpf/include/mpf_jitter_buffer.h index d638439824..a4cb4c8ba2 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_jitter_buffer.h +++ b/libs/unimrcp/libs/mpf/include/mpf_jitter_buffer.h @@ -41,7 +41,7 @@ typedef struct mpf_jitter_buffer_t mpf_jitter_buffer_t; /** Create jitter buffer */ -mpf_jitter_buffer_t* mpf_jitter_buffer_create(mpf_jb_config_t *jb_config, mpf_codec_t *codec, apr_pool_t *pool); +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); @@ -50,10 +50,10 @@ 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); diff --git a/libs/unimrcp/libs/mpf/include/mpf_media_descriptor.h b/libs/unimrcp/libs/mpf/include/mpf_media_descriptor.h deleted file mode 100644 index af54224ceb..0000000000 --- a/libs/unimrcp/libs/mpf/include/mpf_media_descriptor.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2008 Arsen Chaloyan - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __MPF_MEDIA_DESCRIPTOR_H__ -#define __MPF_MEDIA_DESCRIPTOR_H__ - -/** - * @file mpf_media_descriptor.h - * @brief Media Descriptor Base - */ - -#include -#include "apt_string.h" - -APT_BEGIN_EXTERN_C - -/** MPF media state */ -typedef enum { - MPF_MEDIA_DISABLED, /**< disabled media */ - MPF_MEDIA_ENABLED /**< enabled media */ -} mpf_media_state_e; - -/** MPF media descriptor declaration */ -typedef struct mpf_media_descriptor_t mpf_media_descriptor_t; - -/** MPF media descriptor */ -struct mpf_media_descriptor_t { - /** Media state (disabled/enabled)*/ - mpf_media_state_e state; - - /** Ip address */ - apt_str_t ip; - /** External (NAT) Ip address */ - apt_str_t ext_ip; - /** Port */ - apr_port_t port; - /** Identifier (0,1,...) */ - apr_size_t id; -}; - -/** Initialize MPF media descriptor */ -static APR_INLINE void mpf_media_descriptor_init(mpf_media_descriptor_t *descriptor) -{ - descriptor->state = MPF_MEDIA_DISABLED; - apt_string_reset(&descriptor->ip); - apt_string_reset(&descriptor->ext_ip); - descriptor->port = 0; - descriptor->id = 0; -} - -APT_END_EXTERN_C - -#endif /*__MPF_MEDIA_DESCRIPTOR_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_message.h b/libs/unimrcp/libs/mpf/include/mpf_message.h index ae69638519..601b306a9e 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_message.h +++ b/libs/unimrcp/libs/mpf/include/mpf_message.h @@ -26,6 +26,9 @@ 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 */ @@ -40,16 +43,22 @@ typedef enum { } 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 { @@ -64,10 +73,20 @@ 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__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_mixer.h b/libs/unimrcp/libs/mpf/include/mpf_mixer.h new file mode 100644 index 0000000000..fa48f5b9a9 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_mixer.h @@ -0,0 +1,47 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MPF_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__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_multiplier.h b/libs/unimrcp/libs/mpf/include/mpf_multiplier.h new file mode 100644 index 0000000000..380d5b5f16 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_multiplier.h @@ -0,0 +1,47 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MPF_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__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_named_event.h b/libs/unimrcp/libs/mpf/include/mpf_named_event.h new file mode 100644 index 0000000000..d8d4dc6750 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_named_event.h @@ -0,0 +1,71 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_object.h b/libs/unimrcp/libs/mpf/include/mpf_object.h index f4136b0ed2..8e3e4eee95 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_object.h +++ b/libs/unimrcp/libs/mpf/include/mpf_object.h @@ -23,29 +23,51 @@ */ #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 diff --git a/libs/unimrcp/libs/mpf/include/mpf_timer.h b/libs/unimrcp/libs/mpf/include/mpf_resampler.h similarity index 54% rename from libs/unimrcp/libs/mpf/include/mpf_timer.h rename to libs/unimrcp/libs/mpf/include/mpf_resampler.h index a58af07f50..d674336139 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_timer.h +++ b/libs/unimrcp/libs/mpf/include/mpf_resampler.h @@ -14,31 +14,27 @@ * 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__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_rtcp_packet.h b/libs/unimrcp/libs/mpf/include/mpf_rtcp_packet.h new file mode 100644 index 0000000000..b43e4a1213 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_rtcp_packet.h @@ -0,0 +1,200 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_rtp_attribs.h b/libs/unimrcp/libs/mpf/include/mpf_rtp_attribs.h index 5fe3d96067..dbce6b706f 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_rtp_attribs.h +++ b/libs/unimrcp/libs/mpf/include/mpf_rtp_attribs.h @@ -22,8 +22,7 @@ * @brief RTP Attributes (SDP) */ -#include "mpf_media_descriptor.h" -#include "mpf_stream_mode.h" +#include "mpf_rtp_descriptor.h" APT_BEGIN_EXTERN_C @@ -47,8 +46,8 @@ MPF_DECLARE(const apt_str_t*) mpf_rtp_attrib_str_get(mpf_rtp_attrib_e attrib_id) /** Find audio media attribute identifier by attribute name */ MPF_DECLARE(mpf_rtp_attrib_e) mpf_rtp_attrib_id_find(const apt_str_t *attrib); -/** Get string by stream mode (send/receive) */ -MPF_DECLARE(const apt_str_t*) mpf_stream_mode_str_get(mpf_stream_mode_e direction); +/** 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 diff --git a/libs/unimrcp/libs/mpf/include/mpf_rtp_defs.h b/libs/unimrcp/libs/mpf/include/mpf_rtp_defs.h index 9fc7925fc7..883a1f5896 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_rtp_defs.h +++ b/libs/unimrcp/libs/mpf/include/mpf_rtp_defs.h @@ -41,56 +41,58 @@ APT_BEGIN_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)); @@ -98,13 +100,12 @@ static APR_INLINE void mpf_rtp_rx_periodic_history_reset(rtp_rx_periodic_history /** 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; @@ -115,10 +116,6 @@ struct rtp_receiver_t { /** 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; @@ -135,24 +132,25 @@ struct rtp_transmitter_t { 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); @@ -161,8 +159,6 @@ static APR_INLINE void rtp_receiver_init(rtp_receiver_t *receiver) /** 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; @@ -172,11 +168,12 @@ static APR_INLINE void rtp_transmitter_init(rtp_transmitter_t *transmitter) 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 diff --git a/libs/unimrcp/libs/mpf/include/mpf_rtp_descriptor.h b/libs/unimrcp/libs/mpf/include/mpf_rtp_descriptor.h index 46655e4c27..d7fe3afedc 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_rtp_descriptor.h +++ b/libs/unimrcp/libs/mpf/include/mpf_rtp_descriptor.h @@ -22,9 +22,9 @@ * @brief MPF RTP Stream Descriptor */ -#include "mpf_stream_mode.h" -#include "mpf_media_descriptor.h" -#include "mpf_codec_descriptor.h" +#include +#include "apt_string.h" +#include "mpf_stream_descriptor.h" APT_BEGIN_EXTERN_C @@ -39,23 +39,38 @@ typedef struct mpf_rtp_config_t mpf_rtp_config_t; /** 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 */ @@ -82,43 +97,62 @@ struct mpf_jb_config_t { 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 */ @@ -149,7 +183,12 @@ static APR_INLINE mpf_rtp_config_t* mpf_rtp_config_create(apr_pool_t *pool) 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; } diff --git a/libs/unimrcp/libs/mpf/include/mpf_rtp_header.h b/libs/unimrcp/libs/mpf/include/mpf_rtp_header.h index 070bf8c99c..398d844dce 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_rtp_header.h +++ b/libs/unimrcp/libs/mpf/include/mpf_rtp_header.h @@ -26,7 +26,7 @@ APT_BEGIN_EXTERN_C -/** Protocol version. */ +/** Protocol version */ #define RTP_VERSION 2 /** RTP header declaration */ @@ -74,8 +74,7 @@ struct rtp_header_t { }; /** RTP extension header */ -struct rtp_extension_header_t -{ +struct rtp_extension_header_t { /** profile */ apr_uint16_t profile; /** length */ diff --git a/libs/unimrcp/libs/mpf/include/mpf_rtp_pt.h b/libs/unimrcp/libs/mpf/include/mpf_rtp_pt.h new file mode 100644 index 0000000000..27114c5e6e --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_rtp_pt.h @@ -0,0 +1,44 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MPF_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__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_rtp_stat.h b/libs/unimrcp/libs/mpf/include/mpf_rtp_stat.h index 248a4b5c66..eef43adcd4 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_rtp_stat.h +++ b/libs/unimrcp/libs/mpf/include/mpf_rtp_stat.h @@ -19,60 +19,90 @@ /** * @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(tx_stat,0,sizeof(rtp_tx_stat_t)); + memset(sr_stat,0,sizeof(rtcp_sr_stat_t)); } -/** Reset RTP receive statistics */ +/** Reset RTCP RR statistics */ +static APR_INLINE void mpf_rtcp_rr_stat_reset(rtcp_rr_stat_t *rr_stat) +{ + memset(rr_stat,0,sizeof(rtcp_rr_stat_t)); +} + +/** 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)); diff --git a/libs/unimrcp/libs/mpf/include/mpf_rtp_stream.h b/libs/unimrcp/libs/mpf/include/mpf_rtp_stream.h index 848cb54c84..803ab34577 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_rtp_stream.h +++ b/libs/unimrcp/libs/mpf/include/mpf_rtp_stream.h @@ -35,6 +35,18 @@ APT_BEGIN_EXTERN_C */ 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 diff --git a/libs/unimrcp/libs/mpf/include/mpf_rtp_termination_factory.h b/libs/unimrcp/libs/mpf/include/mpf_rtp_termination_factory.h index 2f9f171e3c..f881c445af 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_rtp_termination_factory.h +++ b/libs/unimrcp/libs/mpf/include/mpf_rtp_termination_factory.h @@ -22,8 +22,7 @@ * @brief MPF RTP Termination Factory */ -#include -#include "mpf_types.h" +#include "mpf_termination_factory.h" #include "mpf_rtp_descriptor.h" APT_BEGIN_EXTERN_C diff --git a/libs/unimrcp/libs/mpf/include/mpf_scheduler.h b/libs/unimrcp/libs/mpf/include/mpf_scheduler.h new file mode 100644 index 0000000000..87122661f9 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_scheduler.h @@ -0,0 +1,61 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __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__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_stream.h b/libs/unimrcp/libs/mpf/include/mpf_stream.h index 9df864ecfc..4decae5235 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_stream.h +++ b/libs/unimrcp/libs/mpf/include/mpf_stream.h @@ -23,13 +23,14 @@ */ #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 */ @@ -40,20 +41,28 @@ struct mpf_audio_stream_t { 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 */ @@ -62,33 +71,39 @@ struct mpf_audio_stream_vtable_t { 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) @@ -98,15 +113,15 @@ 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) @@ -122,15 +137,15 @@ static APR_INLINE apt_bool_t mpf_audio_stream_frame_read(mpf_audio_stream_t *str 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) @@ -146,6 +161,9 @@ static APR_INLINE apt_bool_t mpf_audio_stream_frame_write(mpf_audio_stream_t *st 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__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_stream_descriptor.h b/libs/unimrcp/libs/mpf/include/mpf_stream_descriptor.h new file mode 100644 index 0000000000..86b56b1b73 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_stream_descriptor.h @@ -0,0 +1,88 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MPF_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__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_stream_mode.h b/libs/unimrcp/libs/mpf/include/mpf_stream_mode.h deleted file mode 100644 index 9b7cf0da55..0000000000 --- a/libs/unimrcp/libs/mpf/include/mpf_stream_mode.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2008 Arsen Chaloyan - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __MPF_STREAM_MODE_H__ -#define __MPF_STREAM_MODE_H__ - -/** - * @file mpf_stream_mode.h - * @brief MPF Stream Mode (Send/Receive) - */ - -#include "mpf.h" - -APT_BEGIN_EXTERN_C - -/** Enumeration of stream modes */ -typedef enum { - STREAM_MODE_NONE = 0x0, /**< none */ - STREAM_MODE_SEND = 0x1, /**< send */ - STREAM_MODE_RECEIVE = 0x2, /**< receive */ - - STREAM_MODE_SEND_RECEIVE = STREAM_MODE_SEND | STREAM_MODE_RECEIVE /**< send and receive */ -} mpf_stream_mode_e; - -static APR_INLINE mpf_stream_mode_e mpf_stream_mode_negotiate(mpf_stream_mode_e remote_mode) -{ - mpf_stream_mode_e local_mode = remote_mode; - if(local_mode == STREAM_MODE_SEND) { - local_mode = STREAM_MODE_RECEIVE; - } - else if(local_mode == STREAM_MODE_RECEIVE) { - local_mode = STREAM_MODE_SEND; - } - return local_mode; -} - - -APT_END_EXTERN_C - -#endif /*__MPF_STREAM_MODE_H__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_termination.h b/libs/unimrcp/libs/mpf/include/mpf_termination.h index df51fbbbf6..06ede3bfd8 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_termination.h +++ b/libs/unimrcp/libs/mpf/include/mpf_termination.h @@ -29,6 +29,23 @@ APT_BEGIN_EXTERN_C /** Prototype of termination event handler */ typedef apt_bool_t (*mpf_termination_event_handler_f)(mpf_termination_t *termination, int event_id, void *descriptor); +/** 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 */ @@ -41,6 +58,8 @@ struct mpf_termination_t { 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 */ @@ -54,15 +73,6 @@ struct mpf_termination_t { 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 @@ -80,6 +90,13 @@ MPF_DECLARE(mpf_termination_t*) mpf_termination_base_create( mpf_video_stream_t *video_stream, apr_pool_t *pool); +/** + * Add MPF termination. + * @param termination the termination to add + * @param descriptor the termination specific descriptor + */ +MPF_DECLARE(apt_bool_t) mpf_termination_add(mpf_termination_t *termination, void *descriptor); + /** * Modify MPF termination. * @param termination the termination to modify @@ -88,47 +105,11 @@ MPF_DECLARE(mpf_termination_t*) mpf_termination_base_create( MPF_DECLARE(apt_bool_t) mpf_termination_modify(mpf_termination_t *termination, void *descriptor); /** - * Validate MPF termination. - * @param termination the termination to validate + * Subtract MPF termination. + * @param termination the termination to subtract */ -MPF_DECLARE(apt_bool_t) mpf_termination_validate(mpf_termination_t *termination); +MPF_DECLARE(apt_bool_t) mpf_termination_subtract(mpf_termination_t *termination); -/** - * Destroy MPF termination. - * @param termination the termination to destroy - */ -MPF_DECLARE(apt_bool_t) mpf_termination_destroy(mpf_termination_t *termination); - -/** - * Get associated object. - * @param termination the termination to get object from - */ -MPF_DECLARE(void*) mpf_termination_object_get(mpf_termination_t *termination); - - -/** - * Create MPF termination by termination factory. - * @param termination_factory the termination factory to create termination from - * @param obj the external object associated with termination - * @param pool the pool to allocate memory from - */ -MPF_DECLARE(mpf_termination_t*) mpf_termination_create( - mpf_termination_factory_t *termination_factory, - void *obj, - apr_pool_t *pool); - -/** - * Create raw MPF termination. - * @param obj the external object associated with termination - * @param audio_stream the audio stream of the termination - * @param video_stream the video stream of the termination - * @param pool the pool to allocate memory from - */ -MPF_DECLARE(mpf_termination_t*) mpf_raw_termination_create( - void *obj, - mpf_audio_stream_t *audio_stream, - mpf_video_stream_t *video_stream, - apr_pool_t *pool); APT_END_EXTERN_C diff --git a/libs/unimrcp/libs/mpf/include/mpf_termination_factory.h b/libs/unimrcp/libs/mpf/include/mpf_termination_factory.h new file mode 100644 index 0000000000..564518657a --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_termination_factory.h @@ -0,0 +1,88 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MPF_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__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_timer_manager.h b/libs/unimrcp/libs/mpf/include/mpf_timer_manager.h new file mode 100644 index 0000000000..4de738c4f6 --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_timer_manager.h @@ -0,0 +1,53 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MPF_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__*/ diff --git a/libs/unimrcp/libs/mpf/include/mpf_types.h b/libs/unimrcp/libs/mpf/include/mpf_types.h index 721fa61c36..0b9814edb5 100644 --- a/libs/unimrcp/libs/mpf/include/mpf_types.h +++ b/libs/unimrcp/libs/mpf/include/mpf_types.h @@ -29,9 +29,18 @@ APT_BEGIN_EXTERN_C /** 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; @@ -47,16 +56,6 @@ typedef struct mpf_audio_stream_t mpf_audio_stream_t; /** Opaque MPF video stream declaration */ typedef struct mpf_video_stream_t mpf_video_stream_t; -/** Termination vtable declaration */ -typedef struct mpf_termination_vtable_t mpf_termination_vtable_t; - -/** Table of termination virtual methods */ -struct mpf_termination_vtable_t { - /** Virtual termination destroy method */ - apt_bool_t (*destroy)(mpf_termination_t *termination); - /** Virtual termination modify method */ - apt_bool_t (*modify)(mpf_termination_t *termination, void *descriptor); -}; APT_END_EXTERN_C diff --git a/libs/unimrcp/libs/mpf/include/mpf_user.h b/libs/unimrcp/libs/mpf/include/mpf_user.h deleted file mode 100644 index 0d6ab83e92..0000000000 --- a/libs/unimrcp/libs/mpf/include/mpf_user.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2008 Arsen Chaloyan - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __MPF_USER_H__ -#define __MPF_USER_H__ - -/** - * @file mpf_user.h - * @brief MPF User Interface - */ - -#include "mpf_types.h" - -APT_BEGIN_EXTERN_C - -/** - * Create MPF context. - * @param obj the external object associated with context - * @param max_termination_count the max number of terminations in context - * @param pool the pool to allocate memory from - */ -MPF_DECLARE(mpf_context_t*) mpf_context_create(void *obj, apr_size_t max_termination_count, apr_pool_t *pool); - -/** - * Destroy MPF context. - * @param context the context to destroy - */ -MPF_DECLARE(apt_bool_t) mpf_context_destroy(mpf_context_t *context); - -/** - * Get external object associated with MPF context. - * @param context the context to get object from - */ -MPF_DECLARE(void*) mpf_context_object_get(mpf_context_t *context); - - -APT_END_EXTERN_C - -#endif /*__MPF_USER_H__*/ diff --git a/libs/unimrcp/libs/mpf/mpf.vcproj b/libs/unimrcp/libs/mpf/mpf.vcproj index 6efaf489b8..c1d1d8557d 100644 --- a/libs/unimrcp/libs/mpf/mpf.vcproj +++ b/libs/unimrcp/libs/mpf/mpf.vcproj @@ -191,6 +191,14 @@ RelativePath=".\include\mpf_decoder.h" > + + + + @@ -215,18 +223,34 @@ RelativePath=".\include\mpf_jitter_buffer.h" > - - + + + + + + + + + + @@ -243,6 +267,10 @@ RelativePath=".\include\mpf_rtp_header.h" > + + @@ -255,12 +283,16 @@ RelativePath=".\include\mpf_rtp_termination_factory.h" > + + + + - - + + + + @@ -344,6 +384,22 @@ RelativePath=".\src\mpf_jitter_buffer.c" > + + + + + + + + @@ -356,12 +412,24 @@ RelativePath=".\src\mpf_rtp_termination_factory.c" > + + + + + + diff --git a/libs/unimrcp/libs/mpf/src/mpf_activity_detector.c b/libs/unimrcp/libs/mpf/src/mpf_activity_detector.c index 8c27801069..4aba1579e2 100644 --- a/libs/unimrcp/libs/mpf/src/mpf_activity_detector.c +++ b/libs/unimrcp/libs/mpf/src/mpf_activity_detector.c @@ -28,17 +28,19 @@ typedef enum { /** 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 */ @@ -46,7 +48,8 @@ MPF_DECLARE(mpf_activity_detector_t*) mpf_activity_detector_create(apr_pool_t *p { 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; @@ -72,10 +75,16 @@ MPF_DECLARE(void) mpf_activity_detector_noinput_timeout_set(mpf_activity_detecto 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; } @@ -133,7 +142,7 @@ MPF_DECLARE(mpf_detector_event_e) mpf_activity_detector_process(mpf_activity_det 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); @@ -160,7 +169,7 @@ MPF_DECLARE(mpf_detector_event_e) mpf_activity_detector_process(mpf_activity_det } 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); diff --git a/libs/unimrcp/libs/mpf/src/mpf_audio_file_stream.c b/libs/unimrcp/libs/mpf/src/mpf_audio_file_stream.c index e52d156cb7..54010b69b4 100644 --- a/libs/unimrcp/libs/mpf/src/mpf_audio_file_stream.c +++ b/libs/unimrcp/libs/mpf/src/mpf_audio_file_stream.c @@ -50,7 +50,7 @@ static apt_bool_t mpf_audio_file_destroy(mpf_audio_stream_t *stream) 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; } @@ -76,7 +76,7 @@ static apt_bool_t mpf_audio_file_frame_read(mpf_audio_stream_t *stream, mpf_fram } -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; } @@ -116,15 +116,21 @@ static const mpf_audio_stream_vtable_t vtable = { 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) @@ -136,12 +142,9 @@ MPF_DECLARE(apt_bool_t) mpf_file_stream_modify(mpf_audio_stream_t *stream, mpf_a } 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) { @@ -150,12 +153,9 @@ MPF_DECLARE(apt_bool_t) mpf_file_stream_modify(mpf_audio_stream_t *stream, mpf_a 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; } diff --git a/libs/unimrcp/libs/mpf/src/mpf_bridge.c b/libs/unimrcp/libs/mpf/src/mpf_bridge.c index 45aa1c754c..f4028063d3 100644 --- a/libs/unimrcp/libs/mpf/src/mpf_bridge.c +++ b/libs/unimrcp/libs/mpf/src/mpf_bridge.c @@ -15,98 +15,199 @@ */ #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 = source->rx_codec; - frame_size = mpf_codec_frame_size_calculate(codec->descriptor,codec->attribs); + codec = mpf_codec_manager_codec_get(codec_manager,source->rx_descriptor,pool); + if(!codec) { + return NULL; + } + + 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); } diff --git a/libs/unimrcp/libs/mpf/src/mpf_codec_descriptor.c b/libs/unimrcp/libs/mpf/src/mpf_codec_descriptor.c index 0a811ac058..2995cf94a5 100644 --- a/libs/unimrcp/libs/mpf/src/mpf_codec_descriptor.c +++ b/libs/unimrcp/libs/mpf/src/mpf_codec_descriptor.c @@ -15,12 +15,84 @@ */ #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; } @@ -36,37 +108,158 @@ MPF_DECLARE(apt_bool_t) mpf_codec_descriptor_match(const mpf_codec_descriptor_t return match; } -/** Intersect two codec lists */ -MPF_DECLARE(apt_bool_t) mpf_codec_list_intersect(mpf_codec_list_t *codec_list1, mpf_codec_list_t *codec_list2) +/** 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) { - int i; - int j; - mpf_codec_descriptor_t *descriptor1; - mpf_codec_descriptor_t *descriptor2; - codec_list1->preffered = NULL; - codec_list2->preffered = NULL; - /* find only one match, set the matched codec as preffered, disable the others */ - for(i=0; idescriptor_arr->nelts; i++) { - descriptor1 = (mpf_codec_descriptor_t*)codec_list1->descriptor_arr->elts + i; - if(codec_list1->preffered) { - descriptor1->enabled = FALSE; - continue; + 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; } - - for(j=0; jdescriptor_arr->nelts; j++) { - descriptor2 = (mpf_codec_descriptor_t*)codec_list2->descriptor_arr->elts + j; - - descriptor1->enabled = mpf_codec_descriptor_match(descriptor1,descriptor2); - if(descriptor1->enabled == TRUE) { - codec_list1->preffered = descriptor1; - codec_list2->preffered = descriptor2; - break; + } + else { + if(apt_string_compare(&attribs->name,&descriptor->name) == TRUE) { + if(mpf_sampling_rate_check(descriptor->sampling_rate,attribs->sample_rates) == TRUE) { + match = TRUE; } } } - for(j=0; jdescriptor_arr->nelts; j++) { - descriptor2 = (mpf_codec_descriptor_t*)codec_list2->descriptor_arr->elts + j; - descriptor2->enabled = (codec_list2->preffered == descriptor2) ? TRUE : FALSE; + return 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; idescriptor_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; iattrib_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; idescriptor_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_lists_intersect(mpf_codec_list_t *codec_list1, mpf_codec_list_t *codec_list2) +{ + int i; + mpf_codec_descriptor_t *descriptor1; + mpf_codec_descriptor_t *descriptor2; + 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; idescriptor_arr->nelts; i++) { + 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; + } + + /* 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(i=0; idescriptor_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; diff --git a/libs/unimrcp/libs/mpf/src/mpf_codec_g711.c b/libs/unimrcp/libs/mpf/src/mpf_codec_g711.c index da644c7c91..ab20a278f9 100644 --- a/libs/unimrcp/libs/mpf/src/mpf_codec_g711.c +++ b/libs/unimrcp/libs/mpf/src/mpf_codec_g711.c @@ -15,6 +15,7 @@ */ #include "mpf_codec.h" +#include "mpf_rtp_pt.h" #include "g711/g711.h" #define G711u_CODEC_NAME "PCMU" @@ -35,14 +36,14 @@ static apt_bool_t g711_close(mpf_codec_t *codec) 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; isize; i++) { encode_buf[i] = linear_to_ulaw(decode_buf[i]); @@ -53,14 +54,14 @@ static apt_bool_t g711u_encode(mpf_codec_t *codec, const mpf_codec_frame_t *fram 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; isize; i++) { decode_buf[i] = ulaw_to_linear(encode_buf[i]); @@ -71,14 +72,14 @@ static apt_bool_t g711u_decode(mpf_codec_t *codec, const mpf_codec_frame_t *fram 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; isize; i++) { encode_buf[i] = linear_to_alaw(decode_buf[i]); @@ -89,14 +90,14 @@ static apt_bool_t g711a_encode(mpf_codec_t *codec, const mpf_codec_frame_t *fram 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; isize; i++) { decode_buf[i] = alaw_to_linear(encode_buf[i]); @@ -122,20 +123,20 @@ static const mpf_codec_vtable_t g711a_vtable = { }; 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 }; diff --git a/libs/unimrcp/libs/mpf/src/mpf_codec_linear.c b/libs/unimrcp/libs/mpf/src/mpf_codec_linear.c index c968c74c18..7f342b0a31 100644 --- a/libs/unimrcp/libs/mpf/src/mpf_codec_linear.c +++ b/libs/unimrcp/libs/mpf/src/mpf_codec_linear.c @@ -17,10 +17,7 @@ #define APR_WANT_BYTEFUNC #include #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" @@ -40,9 +37,9 @@ static apt_bool_t l16_close(mpf_codec_t *codec) 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; @@ -56,9 +53,9 @@ static apt_bool_t l16_encode(mpf_codec_t *codec, const mpf_codec_frame_t *frame_ 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; @@ -69,12 +66,6 @@ static apt_bool_t l16_decode(mpf_codec_t *codec, const mpf_codec_frame_t *frame_ return TRUE; } - - -static const mpf_codec_vtable_t lpcm_vtable = { - NULL -}; - static const mpf_codec_vtable_t l16_vtable = { l16_open, l16_close, @@ -83,13 +74,6 @@ static const mpf_codec_vtable_t l16_vtable = { 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 */ @@ -97,23 +81,6 @@ static const mpf_codec_attribs_t l16_attribs = { 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); diff --git a/libs/unimrcp/libs/mpf/src/mpf_codec_manager.c b/libs/unimrcp/libs/mpf/src/mpf_codec_manager.c index 15bda89c51..b83581a25a 100644 --- a/libs/unimrcp/libs/mpf/src/mpf_codec_manager.c +++ b/libs/unimrcp/libs/mpf/src/mpf_codec_manager.c @@ -16,25 +16,28 @@ #include #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; } @@ -45,62 +48,32 @@ MPF_DECLARE(void) mpf_codec_manager_destroy(mpf_codec_manager_t *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; icodec_arr->nelts; i++) { - codec = ((mpf_codec_t**)codec_manager->codec_arr->elts)[i]; - if(descriptor->payload_type < 96) { - if(codec->static_descriptor && codec->static_descriptor->payload_type == descriptor->payload_type) { - descriptor->name = codec->static_descriptor->name; - descriptor->sampling_rate = codec->static_descriptor->sampling_rate; - descriptor->channel_count = codec->static_descriptor->channel_count; - break; - } - } - else { - if(apt_string_compare(&codec->attribs->name,&descriptor->name) == TRUE) { - /* sampling rate must be checked as well */ - break; - } + 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) @@ -112,7 +85,7 @@ MPF_DECLARE(apt_bool_t) mpf_codec_manager_codec_list_get(const mpf_codec_manager mpf_codec_list_init(codec_list,codec_manager->codec_arr->nelts,pool); for(i=0; icodec_arr->nelts; i++) { - codec = ((mpf_codec_t**)codec_manager->codec_arr->elts)[i]; + 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); @@ -121,6 +94,12 @@ MPF_DECLARE(apt_bool_t) mpf_codec_manager_codec_list_get(const mpf_codec_manager } } } + if(codec_manager->event_descriptor) { + descriptor = mpf_codec_list_add(codec_list); + if(descriptor) { + *descriptor = *codec_manager->event_descriptor; + } + } return TRUE; } @@ -138,26 +117,35 @@ static apt_bool_t mpf_codec_manager_codec_parse(const mpf_codec_manager_t *codec 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; - } + if(codec) { + descriptor = mpf_codec_list_add(codec_list); + descriptor->name = name; - 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) { @@ -200,7 +188,7 @@ MPF_DECLARE(const mpf_codec_t*) mpf_codec_manager_codec_find(const mpf_codec_man int i; mpf_codec_t *codec; for(i=0; icodec_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; } diff --git a/libs/unimrcp/libs/mpf/src/mpf_context.c b/libs/unimrcp/libs/mpf/src/mpf_context.c index b209154753..c6978d8e37 100644 --- a/libs/unimrcp/libs/mpf/src/mpf_context.c +++ b/libs/unimrcp/libs/mpf/src/mpf_context.c @@ -14,30 +14,126 @@ * limitations under the License. */ +#ifdef WIN32 +#pragma warning(disable: 4127) +#endif +#include #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; itable[i] = apr_palloc(pool,sizeof(table_item_t)*max_termination_count); - for(j=0; jtable[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; icapacity; 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; jcapacity; j++) { + matrix_item = &context->matrix[i][j]; + matrix_item->on = 0; } } @@ -47,15 +143,12 @@ MPF_DECLARE(mpf_context_t*) mpf_context_create(void *obj, apr_size_t max_termina MPF_DECLARE(apt_bool_t) mpf_context_destroy(mpf_context_t *context) { apr_size_t i; - apr_size_t count = context->max_termination_count; mpf_termination_t *termination; - for(i=0; itable[i][i]; + for(i=0; icapacity; 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; @@ -69,164 +162,376 @@ MPF_DECLARE(void*) mpf_context_object_get(mpf_context_t *context) MPF_DECLARE(apt_bool_t) mpf_context_termination_add(mpf_context_t *context, mpf_termination_t *termination) { apr_size_t i; - apr_size_t count = context->max_termination_count; - for(i=0; itable[i][i]) { - apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Add Termination"); - context->table[i][i] = termination; - termination->slot = i; - context->termination_count++; - return TRUE; + header_item_t *header_item; + for(i=0; icapacity; 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; jcapacity && kcount; 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_association_add(mpf_context_t *context, mpf_termination_t *termination1, mpf_termination_t *termination2) +{ + 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; + } + + 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_association_remove(mpf_context_t *context, mpf_termination_t *termination1, mpf_termination_t *termination2) +{ + 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; + } + + 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 == 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_associations_reset(mpf_context_t *context) +{ + 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); + + /* reset assigned associations */ + for(i=0,k=0; icapacity && kcount; 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; jcapacity; 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--; + } + + item = &context->matrix[j][i]; + if(item->on) { + item->on = 0; + header_item2->tx_count--; + header_item1->rx_count--; + } + } + } + return TRUE; +} + +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); + + for(i=0,k=0; icapacity && kcount; 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); + } + 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; +} + +MPF_DECLARE(apt_bool_t) mpf_context_topology_destroy(mpf_context_t *context) +{ + if(context->mpf_objects->nelts) { + int i; + mpf_object_t *object; + for(i=0; impf_objects->nelts; i++) { + object = APR_ARRAY_IDX(context->mpf_objects,i,mpf_object_t*); + mpf_object_destroy(object); + } + apr_array_clear(context->mpf_objects); + } return TRUE; } MPF_DECLARE(apt_bool_t) mpf_context_process(mpf_context_t *context) { + int i; mpf_object_t *object; - apr_size_t i,j; - for(i=0; imax_termination_count; i++) { - for(j=0; jmax_termination_count; j++) { - if(i==j) continue; - - object = context->table[i][j]; - if(object && object->process) { - object->process(object); - } + for(i=0; impf_objects->nelts; i++) { + object = APR_ARRAY_IDX(context->mpf_objects,i,mpf_object_t*); + if(object && object->process) { + object->process(object); } } return TRUE; } -MPF_DECLARE(apt_bool_t) mpf_context_topology_apply(mpf_context_t *context, mpf_termination_t *termination) + +static mpf_object_t* mpf_context_bridge_create(mpf_context_t *context, apr_size_t i) { - 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 */ + header_item_t *header_item1 = &context->header[i]; + header_item_t *header_item2; + matrix_item_t *item; + apr_size_t j; + for(j=0; jcapacity; 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; jcapacity && ktx_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; icapacity && krx_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; } - - i = termination->slot; - for(j=0; jmax_termination_count; j++) { - if(i == j) continue; - - sink_termination = context->table[j][j]; - object = mpf_context_connection_create(context,termination,sink_termination); - if(object) { - context->table[i][j] = object; - } - } - - j = termination->slot; - for(i=0; imax_termination_count; i++) { - if(i == j) continue; - - source_termination = context->table[i][i]; - object = mpf_context_connection_create(context,source_termination,termination); - if(object) { - context->table[i][j] = object; - } - } - - return TRUE; -} - -MPF_DECLARE(apt_bool_t) mpf_context_topology_destroy(mpf_context_t *context, mpf_termination_t *termination) -{ - apr_size_t i,j; - mpf_object_t *object; - if(context->termination_count <= 1) { - /* at least 2 terminations are required to destroy topology */ - return TRUE; - } - - i = termination->slot; - for(j=0; jmax_termination_count; j++) { - if(i == j) continue; - - object = context->table[i][j]; - if(object) { - if(object->destroy) { - object->destroy(object); - } - context->table[i][j] = NULL; - } - } - - j = termination->slot; - for(i=0; imax_termination_count; i++) { - if(i == j) continue; - - object = context->table[i][j]; - if(object) { - if(object->destroy) { - object->destroy(object); - } - context->table[i][j] = NULL; - } - } - return TRUE; -} - -static mpf_object_t* mpf_context_connection_create(mpf_context_t *context, mpf_termination_t *src_termination, mpf_termination_t *sink_termination) -{ - mpf_object_t *object = NULL; - mpf_audio_stream_t *source; - mpf_audio_stream_t *sink; - if(!src_termination || !sink_termination) { - return NULL; - } - source = src_termination->audio_stream; - sink = sink_termination->audio_stream; - if(source && (source->mode & STREAM_MODE_RECEIVE) == STREAM_MODE_RECEIVE && - sink && (sink->mode & STREAM_MODE_SEND) == STREAM_MODE_SEND) { - mpf_codec_t *rx_codec = source->rx_codec; - mpf_codec_t *tx_codec = sink->tx_codec; - if(rx_codec && tx_codec) { - if(mpf_codec_descriptor_match(rx_codec->descriptor,tx_codec->descriptor) == TRUE) { - object = mpf_null_bridge_create(source,sink,context->pool); - } - else { - if(rx_codec->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 object; + return FALSE; } diff --git a/libs/unimrcp/libs/mpf/src/mpf_decoder.c b/libs/unimrcp/libs/mpf/src/mpf_decoder.c index 2add5e43d0..878dbbf459 100644 --- a/libs/unimrcp/libs/mpf/src/mpf_decoder.c +++ b/libs/unimrcp/libs/mpf/src/mpf_decoder.c @@ -22,6 +22,7 @@ typedef struct mpf_decoder_t mpf_decoder_t; struct mpf_decoder_t { mpf_audio_stream_t *base; mpf_audio_stream_t *source; + mpf_codec_t *codec; mpf_frame_t frame_in; }; @@ -32,10 +33,10 @@ static apt_bool_t mpf_decoder_destroy(mpf_audio_stream_t *stream) 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) @@ -47,20 +48,41 @@ 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, @@ -69,24 +91,34 @@ static const mpf_audio_stream_vtable_t vtable = { 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); - decoder->source = source; + 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; - codec = source->rx_codec; - frame_size = mpf_codec_frame_size_calculate(codec->descriptor,codec->attribs); - decoder->base->rx_codec = codec; + decoder->source = source; + decoder->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; diff --git a/libs/unimrcp/libs/mpf/src/mpf_dtmf_detector.c b/libs/unimrcp/libs/mpf/src/mpf_dtmf_detector.c new file mode 100644 index 0000000000..e40096a2ed --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_dtmf_detector.c @@ -0,0 +1,299 @@ +/* + * 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 + +#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; +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_dtmf_generator.c b/libs/unimrcp/libs/mpf/src/mpf_dtmf_generator.c new file mode 100644 index 0000000000..66d870f4b8 --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_dtmf_generator.c @@ -0,0 +1,347 @@ +/* + * 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 + +#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; +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_encoder.c b/libs/unimrcp/libs/mpf/src/mpf_encoder.c index 0c85ac9dc7..797600192f 100644 --- a/libs/unimrcp/libs/mpf/src/mpf_encoder.c +++ b/libs/unimrcp/libs/mpf/src/mpf_encoder.c @@ -22,6 +22,7 @@ typedef struct mpf_encoder_t mpf_encoder_t; struct mpf_encoder_t { mpf_audio_stream_t *base; mpf_audio_stream_t *sink; + mpf_codec_t *codec; mpf_frame_t frame_out; }; @@ -32,10 +33,10 @@ static apt_bool_t mpf_encoder_destroy(mpf_audio_stream_t *stream) 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) @@ -49,15 +50,35 @@ static apt_bool_t mpf_encoder_process(mpf_audio_stream_t *stream, const mpf_fram 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, @@ -66,24 +87,34 @@ static const mpf_audio_stream_vtable_t vtable = { 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; diff --git a/libs/unimrcp/libs/mpf/src/mpf_engine.c b/libs/unimrcp/libs/mpf/src/mpf_engine.c index 2d34b67cfe..ad78904d5b 100644 --- a/libs/unimrcp/libs/mpf/src/mpf_engine.c +++ b/libs/unimrcp/libs/mpf/src/mpf_engine.c @@ -15,13 +15,13 @@ */ #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" @@ -34,36 +34,35 @@ struct mpf_engine_t { 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); @@ -84,14 +83,36 @@ MPF_DECLARE(mpf_engine_t*) mpf_engine_create(apr_pool_t *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; @@ -102,12 +123,109 @@ MPF_DECLARE(void) mpf_engine_task_msg_type_set(mpf_engine_t *engine, apt_task_ms 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; @@ -117,7 +235,7 @@ static apt_bool_t mpf_engine_start(apt_task_t *task) { mpf_engine_t *engine = apt_task_object_get(task); - engine->timer = mpf_timer_start(CODEC_FRAME_TIME_BASE,mpf_engine_main,engine,engine->pool); + mpf_scheduler_start(engine->scheduler); apt_task_child_start(task); return TRUE; } @@ -126,28 +244,16 @@ static apt_bool_t mpf_engine_terminate(apt_task_t *task) { mpf_engine_t *engine = apt_task_object_get(task); - mpf_timer_stop(engine->timer); - mpf_engine_contexts_destroy(engine); + 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) { @@ -156,13 +262,16 @@ static apt_bool_t mpf_engine_event_raise(mpf_termination_t *termination, int eve 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); } @@ -181,86 +290,103 @@ 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) { + 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); - } - mpf_termination_validate(termination); - if(mpf_context_termination_add(context,termination) == FALSE) { - response->status_code = MPF_STATUS_CODE_FAILURE; + for(i=0; icount; 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_context_topology_apply(context,termination); - if(context->termination_count == 1) { - apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Add Context"); - context->elem = apt_list_push_back(engine->contexts,context,context->pool); - } - break; - } - case MPF_COMMAND_SUBTRACT: - { - mpf_context_topology_destroy(context,termination); - if(mpf_context_termination_subtract(context,termination) == FALSE) { - response->status_code = MPF_STATUS_CODE_FAILURE; + case MPF_MODIFY_TERMINATION: + { + mpf_termination_modify(termination,mpf_request->descriptor); 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_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_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_ADD_ASSOCIATION: + { + mpf_context_association_add(context,termination,mpf_request->assoc_termination); + break; + } + case MPF_REMOVE_ASSOCIATION: + { + mpf_context_association_remove(context,termination,mpf_request->assoc_termination); + break; + } + 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); @@ -273,24 +399,15 @@ static void mpf_engine_main(mpf_timer_t *timer, void *data) } 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); diff --git a/libs/unimrcp/libs/mpf/src/mpf_file_termination_factory.c b/libs/unimrcp/libs/mpf/src/mpf_file_termination_factory.c index 4bab083ffa..dd1bc6e280 100644 --- a/libs/unimrcp/libs/mpf/src/mpf_file_termination_factory.c +++ b/libs/unimrcp/libs/mpf/src/mpf_file_termination_factory.c @@ -23,8 +23,9 @@ static apt_bool_t mpf_file_termination_destroy(mpf_termination_t *termination) return TRUE; } -static apt_bool_t mpf_file_termination_modify(mpf_termination_t *termination, void *descriptor) +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); @@ -34,12 +35,36 @@ static apt_bool_t mpf_file_termination_modify(mpf_termination_t *termination, vo 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( diff --git a/libs/unimrcp/libs/mpf/src/mpf_frame_buffer.c b/libs/unimrcp/libs/mpf/src/mpf_frame_buffer.c index bec7e5a0bc..d93e5c56aa 100644 --- a/libs/unimrcp/libs/mpf/src/mpf_frame_buffer.c +++ b/libs/unimrcp/libs/mpf/src/mpf_frame_buffer.c @@ -33,6 +33,7 @@ struct mpf_frame_buffer_t { 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; @@ -40,8 +41,10 @@ mpf_frame_buffer_t* mpf_frame_buffer_create(apr_size_t frame_size, apr_size_t fr 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; iframe_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; @@ -102,6 +105,7 @@ apt_bool_t mpf_frame_buffer_read(mpf_frame_buffer_t *buffer, mpf_frame_t *media_ /* 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( @@ -113,11 +117,13 @@ apt_bool_t mpf_frame_buffer_read(mpf_frame_buffer_t *buffer, mpf_frame_t *media_ 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; diff --git a/libs/unimrcp/libs/mpf/src/mpf_jitter_buffer.c b/libs/unimrcp/libs/mpf/src/mpf_jitter_buffer.c index 55a90bfc60..1cc6b33141 100644 --- a/libs/unimrcp/libs/mpf/src/mpf_jitter_buffer.c +++ b/libs/unimrcp/libs/mpf/src/mpf_jitter_buffer.c @@ -16,30 +16,56 @@ #include "mpf_jitter_buffer.h" -struct mpf_jitter_buffer_t { - mpf_jb_config_t *config; +#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 */ @@ -62,24 +88,31 @@ mpf_jitter_buffer_t* mpf_jitter_buffer_create(mpf_jb_config_t *jb_config, mpf_co } } 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; iframe_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; } @@ -92,6 +125,11 @@ apt_bool_t mpf_jitter_buffer_restart(mpf_jitter_buffer_t *jb) jb->write_sync = 1; jb->write_ts_offset = 0; jb->write_ts = jb->read_ts; + + 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; } @@ -101,10 +139,8 @@ static APR_INLINE mpf_frame_t* mpf_jitter_buffer_frame_get(mpf_jitter_buffer_t * 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; @@ -115,40 +151,47 @@ static APR_INLINE jb_result_t mpf_jitter_buffer_write_prepare(mpf_jitter_buffer_ /* 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; } @@ -167,9 +210,84 @@ jb_result_t mpf_jitter_buffer_write(mpf_jitter_buffer_t *jb, mpf_codec_t *codec, 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) @@ -177,7 +295,9 @@ apt_bool_t mpf_jitter_buffer_read(mpf_jitter_buffer_t *jb, mpf_frame_t *media_fr 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); @@ -188,10 +308,13 @@ apt_bool_t mpf_jitter_buffer_read(mpf_jitter_buffer_t *jb, mpf_frame_t *media_fr } 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; } diff --git a/libs/unimrcp/libs/mpf/src/mpf_mixer.c b/libs/unimrcp/libs/mpf/src/mpf_mixer.c new file mode 100644 index 0000000000..0d5c0f1833 --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_mixer.c @@ -0,0 +1,205 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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; itype |= 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; isource_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; isource_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; isource_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; irx_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; +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_multiplier.c b/libs/unimrcp/libs/mpf/src/mpf_multiplier.c new file mode 100644 index 0000000000..289a194b1b --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_multiplier.c @@ -0,0 +1,182 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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; isink_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; isink_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; isink_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; itx_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; +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_named_event.c b/libs/unimrcp/libs/mpf/src/mpf_named_event.c new file mode 100644 index 0000000000..f8bc75363f --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_named_event.c @@ -0,0 +1,74 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "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 */ +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_resampler.c b/libs/unimrcp/libs/mpf/src/mpf_resampler.c new file mode 100644 index 0000000000..113ffac8fb --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_resampler.c @@ -0,0 +1,26 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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; +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_rtp_attribs.c b/libs/unimrcp/libs/mpf/src/mpf_rtp_attribs.c index 5b5931546f..a34f05fc93 100644 --- a/libs/unimrcp/libs/mpf/src/mpf_rtp_attribs.c +++ b/libs/unimrcp/libs/mpf/src/mpf_rtp_attribs.c @@ -38,17 +38,17 @@ MPF_DECLARE(mpf_rtp_attrib_e) mpf_rtp_attrib_id_find(const apt_str_t *attrib) return apt_string_table_id_find(mpf_rtp_attrib_table,RTP_ATTRIB_COUNT,attrib); } -MPF_DECLARE(const apt_str_t*) mpf_stream_mode_str_get(mpf_stream_mode_e direction) +MPF_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: diff --git a/libs/unimrcp/libs/mpf/src/mpf_rtp_stream.c b/libs/unimrcp/libs/mpf/src/mpf_rtp_stream.c index d97b1a81cb..9e98c4144a 100644 --- a/libs/unimrcp/libs/mpf/src/mpf_rtp_stream.c +++ b/libs/unimrcp/libs/mpf/src/mpf_rtp_stream.c @@ -15,13 +15,32 @@ */ #include +#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; @@ -30,24 +49,31 @@ struct 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); @@ -61,67 +87,102 @@ static const mpf_audio_stream_vtable_t vtable = { 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(); - return rtp_stream->base; + 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 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; } @@ -141,20 +202,24 @@ static apt_bool_t mpf_rtp_stream_local_media_create(mpf_rtp_stream_t *rtp_stream &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; } } @@ -163,6 +228,9 @@ static apt_bool_t mpf_rtp_stream_local_media_update(mpf_rtp_stream_t *rtp_stream 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; @@ -172,20 +240,33 @@ static apt_bool_t mpf_rtp_stream_local_media_update(mpf_rtp_stream_t *rtp_stream 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) { + 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) { - 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; + /* 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); } } @@ -195,33 +276,112 @@ static apt_bool_t mpf_rtp_stream_remote_media_update(mpf_rtp_stream_t *rtp_strea 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; + local_media->id = remote_media->id; + local_media->mid = remote_media->mid; + local_media->ptime = remote_media->ptime; - rtp_stream->local_media->mid = rtp_stream->remote_media->mid; - rtp_stream->local_media->ptime = rtp_stream->remote_media->ptime; - rtp_stream->local_media->mode = mpf_stream_mode_negotiate(rtp_stream->remote_media->mode); + if(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); + } + + 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); + } } - /* intersect local and remote codecs */ - if(rtp_stream->config->own_preferrence == TRUE) { - mpf_codec_list_intersect(&rtp_stream->local_media->codec_list,&rtp_stream->remote_media->codec_list); - } - else { - mpf_codec_list_intersect(&rtp_stream->remote_media->codec_list,&rtp_stream->local_media->codec_list); + 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; } @@ -235,11 +395,11 @@ MPF_DECLARE(apt_bool_t) mpf_rtp_stream_modify(mpf_audio_stream_t *stream, mpf_rt 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) { @@ -250,21 +410,23 @@ MPF_DECLARE(apt_bool_t) mpf_rtp_stream_modify(mpf_audio_stream_t *stream, mpf_rt 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) { @@ -273,35 +435,31 @@ MPF_DECLARE(apt_bool_t) mpf_rtp_stream_modify(mpf_audio_stream_t *stream, mpf_rt 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; } @@ -310,6 +468,11 @@ static apt_bool_t mpf_rtp_rx_stream_close(mpf_audio_stream_t *stream) { mpf_rtp_stream_t *rtp_stream = stream->obj; rtp_receiver_t *receiver = &rtp_stream->receiver; + + 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 + @@ -320,14 +483,14 @@ static apt_bool_t mpf_rtp_rx_stream_close(mpf_audio_stream_t *stream) } 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; } @@ -341,7 +504,7 @@ static APR_INLINE void rtp_rx_overall_stat_reset(rtp_receiver_t *receiver) 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; @@ -385,6 +548,54 @@ static rtp_header_t* rtp_rx_header_skip(void **buffer, apr_size_t *size) 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, @@ -393,7 +604,7 @@ typedef enum { 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 */ @@ -405,7 +616,7 @@ static APR_INLINE rtp_ssrc_result_e rtp_rx_ssrc_update(rtp_receiver_t *receiver, 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 { @@ -449,12 +660,6 @@ static APR_INLINE rtp_seq_result_e rtp_rx_seq_update(rtp_receiver_t *receiver, a } 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; } @@ -481,18 +686,16 @@ static APR_INLINE rtp_ts_result_e rtp_rx_ts_update(rtp_receiver_t *receiver, mpf 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; } @@ -510,8 +713,10 @@ static APR_INLINE void rtp_rx_failure_threshold_check(rtp_receiver_t *receiver) } } -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); @@ -527,12 +732,10 @@ static apt_bool_t rtp_rx_packet_receive(rtp_receiver_t *receiver, mpf_codec_t *c 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); @@ -550,28 +753,29 @@ static apt_bool_t rtp_rx_packet_receive(rtp_receiver_t *receiver, mpf_codec_t *c 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 { @@ -584,11 +788,11 @@ static apt_bool_t rtp_rx_packet_receive(rtp_receiver_t *receiver, mpf_codec_t *c 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--; @@ -605,15 +809,17 @@ static apt_bool_t mpf_rtp_stream_receive(mpf_audio_stream_t *stream, mpf_frame_t } - - - -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; } @@ -629,59 +835,128 @@ static apt_bool_t mpf_rtp_tx_stream_open(mpf_audio_stream_t *stream) 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) @@ -692,80 +967,433 @@ static apt_bool_t mpf_rtp_stream_transmit(mpf_audio_stream_t *stream, const mpf_ transmitter->timestamp += transmitter->samples_per_frame; - if(transmitter->current_frames == 0) { - if(frame->type == MEDIA_FRAME_TYPE_NONE) { - transmitter->inactivity = 1; - } - else { - rtp_header_prepare(transmitter,stream->tx_codec->descriptor->payload_type); - } - } - - if(!transmitter->inactivity) { - memcpy( - transmitter->packet_data + transmitter->packet_size, - frame->codec_frame.buffer, - frame->codec_frame.size); - transmitter->packet_size += frame->codec_frame.size; - - if(++transmitter->current_frames == transmitter->packet_frames) { - if(apr_socket_sendto( - rtp_stream->socket, - rtp_stream->remote_sockaddr, - 0, - transmitter->packet_data, - &transmitter->packet_size) == APR_SUCCESS) { - transmitter->stat.sent_packets++; + 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 { - status = FALSE; + /* ptime allignment */ + status = mpf_rtp_data_send(rtp_stream,transmitter,frame); } - transmitter->current_frames = 0; } + return status; + } + + 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 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; + } + } + 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); - return FALSE; + 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(&stream->socket,stream->local_sockaddr->family,SOCK_DGRAM,0,stream->pool) != APR_SUCCESS) { - apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Failed to Create Socket %s:%hu", - local_media->base.ip.buf, - local_media->base.port); - return FALSE; + 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(stream->socket,APR_SO_NONBLOCK,1); - apr_socket_timeout_set(stream->socket,0); - apr_socket_opt_set(stream->socket,APR_SO_REUSEADDR,1); + 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(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; + 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; + } + + 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); + + 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); +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_rtp_termination_factory.c b/libs/unimrcp/libs/mpf/src/mpf_rtp_termination_factory.c index 5f5e058f6a..0202e2e498 100644 --- a/libs/unimrcp/libs/mpf/src/mpf_rtp_termination_factory.c +++ b/libs/unimrcp/libs/mpf/src/mpf_rtp_termination_factory.c @@ -30,8 +30,9 @@ static apt_bool_t mpf_rtp_termination_destroy(mpf_termination_t *termination) return TRUE; } -static apt_bool_t mpf_rtp_termination_modify(mpf_termination_t *termination, void *descriptor) +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) { @@ -43,12 +44,43 @@ static apt_bool_t mpf_rtp_termination_modify(mpf_termination_t *termination, voi 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) diff --git a/libs/unimrcp/libs/mpf/src/mpf_scheduler.c b/libs/unimrcp/libs/mpf/src/mpf_scheduler.c new file mode 100644 index 0000000000..aefc5132ae --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_scheduler.c @@ -0,0 +1,250 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include + +#ifndef TIME_KILL_SYNCHRONOUS +#define TIME_KILL_SYNCHRONOUS 0x0100 +#endif + +#else +#include +#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 diff --git a/libs/unimrcp/libs/mpf/src/mpf_stream.c b/libs/unimrcp/libs/mpf/src/mpf_stream.c new file mode 100644 index 0000000000..1135e46e01 --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_stream.c @@ -0,0 +1,158 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#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); + } + } +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_termination.c b/libs/unimrcp/libs/mpf/src/mpf_termination.c index 291d92cae3..05eff5c419 100644 --- a/libs/unimrcp/libs/mpf/src/mpf_termination.c +++ b/libs/unimrcp/libs/mpf/src/mpf_termination.c @@ -32,6 +32,7 @@ MPF_DECLARE(mpf_termination_t*) mpf_termination_base_create( 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; @@ -46,19 +47,14 @@ MPF_DECLARE(mpf_termination_t*) mpf_termination_base_create( 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) { @@ -67,54 +63,10 @@ MPF_DECLARE(apt_bool_t) mpf_termination_modify(mpf_termination_t *termination, v 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); -} diff --git a/libs/unimrcp/libs/mpf/src/mpf_termination_factory.c b/libs/unimrcp/libs/mpf/src/mpf_termination_factory.c new file mode 100644 index 0000000000..ba89f51064 --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_termination_factory.c @@ -0,0 +1,66 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "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; +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_timer.c b/libs/unimrcp/libs/mpf/src/mpf_timer.c deleted file mode 100644 index aaca492eb2..0000000000 --- a/libs/unimrcp/libs/mpf/src/mpf_timer.c +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright 2008 Arsen Chaloyan - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mpf_timer.h" - -#ifdef WIN32 -#define ENABLE_MULTIMEDIA_TIMERS -#endif - -#ifdef ENABLE_MULTIMEDIA_TIMERS - -#pragma warning(disable:4201) -#include -#include - -#ifndef TIME_KILL_SYNCHRONOUS -#define TIME_KILL_SYNCHRONOUS 0x0100 -#endif - -struct mpf_timer_t { - unsigned int timer_id; - mpf_timer_proc_f timer_proc; - void *obj; -}; - -#define MAX_MEDIA_TIMERS 10 - -static mpf_timer_t media_timer_set[MAX_MEDIA_TIMERS]; - -static void CALLBACK mm_timer_proc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2); - -MPF_DECLARE(mpf_timer_t*) mpf_timer_start(unsigned long timeout, mpf_timer_proc_f timer_proc, void *obj, apr_pool_t *pool) -{ - mpf_timer_t *timer = NULL; - size_t i; - for(i = 0; itimer_proc = timer_proc; - timer->obj = obj; - timer->timer_id = timeSetEvent(timeout, 0, mm_timer_proc, i, - TIME_PERIODIC | TIME_CALLBACK_FUNCTION | TIME_KILL_SYNCHRONOUS); - if(!timer->timer_id) { - timer = NULL; - } - } - return timer; -} - -MPF_DECLARE(void) mpf_timer_stop(mpf_timer_t *timer) -{ - if(timer) { - timeKillEvent(timer->timer_id); - timer->timer_id = 0; - timer->timer_proc = NULL; - timer->obj = NULL; - } -} - -static void CALLBACK mm_timer_proc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) -{ - mpf_timer_t *timer; - if(dwUser >= MAX_MEDIA_TIMERS) { - return; - } - timer = &media_timer_set[dwUser]; - timer->timer_proc(timer,timer->obj); -} - -#else - -#include - -struct mpf_timer_t { - apr_thread_t *thread; - apr_byte_t running; - - unsigned long timeout; - mpf_timer_proc_f timer_proc; - void *obj; -}; - -static void* APR_THREAD_FUNC timer_thread_proc(apr_thread_t *thread, void *data); - -MPF_DECLARE(mpf_timer_t*) mpf_timer_start(unsigned long timeout, mpf_timer_proc_f timer_proc, void *obj, apr_pool_t *pool) -{ - mpf_timer_t *timer = apr_palloc(pool,sizeof(mpf_timer_t)); - timer->timeout = timeout; - timer->timer_proc = timer_proc; - timer->obj = obj; - timer->running = 1; - if(apr_thread_create(&timer->thread,NULL,timer_thread_proc,timer,pool) != APR_SUCCESS) { - return NULL; - } - return timer; -} - -MPF_DECLARE(void) mpf_timer_stop(mpf_timer_t *timer) -{ - if(timer) { - timer->running = 0; - if(timer->thread) { - apr_status_t s; - apr_thread_join(&s,timer->thread); - timer->thread = NULL; - } - } -} - -static void* APR_THREAD_FUNC timer_thread_proc(apr_thread_t *thread, void *data) -{ - mpf_timer_t *timer = data; - apr_interval_time_t timeout = timer->timeout * 1000; - apr_interval_time_t time_drift = 0; - apr_time_t time_now, time_last; - - time_now = apr_time_now(); - while(timer->running) { - time_last = time_now; - timer->timer_proc(timer,timer->obj); - - if(timeout > time_drift) { - apr_sleep(timeout - time_drift); - } - - time_now = apr_time_now(); - time_drift += time_now - time_last - timeout; -#if 0 - printf("time_drift=%d\n",time_drift); -#endif - } - - return NULL; -} - -#endif diff --git a/libs/unimrcp/libs/mpf/src/mpf_timer_manager.c b/libs/unimrcp/libs/mpf/src/mpf_timer_manager.c new file mode 100644 index 0000000000..4e570cada4 --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_timer_manager.c @@ -0,0 +1,188 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WIN32 +#pragma warning(disable: 4127) +#endif +#include +#include "mpf_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)); +} diff --git a/libs/unimrcp/libs/mrcp-client/include/mrcp_application.h b/libs/unimrcp/libs/mrcp-client/include/mrcp_application.h index fbd4c425ea..92bea0a3d4 100644 --- a/libs/unimrcp/libs/mrcp-client/include/mrcp_application.h +++ b/libs/unimrcp/libs/mrcp-client/include/mrcp_application.h @@ -173,12 +173,31 @@ MRCP_DECLARE(const apt_dir_layout_t*) mrcp_application_dir_layout_get(mrcp_appli */ 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 @@ -225,6 +244,18 @@ MRCP_DECLARE(void*) mrcp_application_channel_object_get(mrcp_channel_t *channel) */ 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 @@ -268,23 +299,38 @@ MRCP_DECLARE(apt_bool_t) mrcp_application_resource_discover(mrcp_session_t *sess */ 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, diff --git a/libs/unimrcp/libs/mrcp-client/include/mrcp_client_session.h b/libs/unimrcp/libs/mrcp-client/include/mrcp_client_session.h index c12820119d..40a444a825 100644 --- a/libs/unimrcp/libs/mrcp-client/include/mrcp_client_session.h +++ b/libs/unimrcp/libs/mrcp-client/include/mrcp_client_session.h @@ -25,7 +25,7 @@ #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" @@ -37,47 +37,59 @@ typedef struct rtp_termination_slot_t rtp_termination_slot_t; /** 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; + 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; + /** 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 */ @@ -86,10 +98,6 @@ struct mrcp_channel_t { 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 */ @@ -115,6 +123,10 @@ struct rtp_termination_slot_t { 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; }; @@ -149,7 +161,7 @@ mrcp_client_session_t* mrcp_client_session_create(mrcp_application_t *applicatio /** 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); @@ -160,13 +172,11 @@ mrcp_app_message_t* mrcp_client_app_signaling_request_create(mrcp_sig_command_e 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); @@ -187,6 +197,8 @@ apt_bool_t mrcp_client_on_channel_modify(mrcp_channel_t *channel, mrcp_control_d 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 diff --git a/libs/unimrcp/libs/mrcp-client/src/mrcp_client.c b/libs/unimrcp/libs/mrcp-client/src/mrcp_client.c index e2e303fc1e..e289b8e62a 100644 --- a/libs/unimrcp/libs/mrcp-client/src/mrcp_client.c +++ b/libs/unimrcp/libs/mrcp-client/src/mrcp_client.c @@ -17,12 +17,13 @@ #include #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" @@ -112,6 +113,7 @@ typedef enum { 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; @@ -126,12 +128,14 @@ static apt_bool_t mrcp_client_channel_add_signal(mrcp_control_channel_t *channel 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 */ @@ -497,6 +501,24 @@ MRCP_DECLARE(mrcp_session_t*) mrcp_application_session_create(mrcp_application_t 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) { @@ -507,6 +529,14 @@ 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) @@ -546,14 +576,28 @@ MRCP_DECLARE(mrcp_channel_t*) mrcp_application_channel_create( 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; } @@ -565,7 +609,8 @@ MRCP_DECLARE(mrcp_channel_t*) mrcp_application_channel_create( 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 */ @@ -581,11 +626,40 @@ MRCP_DECLARE(void*) mrcp_application_channel_object_get(mrcp_channel_t *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) { @@ -619,18 +693,18 @@ MRCP_DECLARE(mrcp_message_t*) mrcp_application_message_create(mrcp_session_t *se 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; } @@ -643,6 +717,47 @@ MRCP_DECLARE(apt_bool_t) mrcp_application_message_send(mrcp_session_t *session, 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, @@ -650,19 +765,33 @@ MRCP_DECLARE(mpf_termination_t*) mrcp_application_source_termination_create( 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 */ @@ -677,19 +806,32 @@ MRCP_DECLARE(mpf_termination_t*) mrcp_application_sink_termination_create( 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 */ @@ -793,6 +935,9 @@ static apt_bool_t mrcp_client_msg_process(apt_task_t *task, apt_task_msg_t *msg) 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; } @@ -800,9 +945,9 @@ static apt_bool_t mrcp_client_msg_process(apt_task_t *task, apt_task_msg_t *msg) } 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: @@ -981,3 +1126,14 @@ static apt_bool_t mrcp_client_message_signal(mrcp_control_channel_t *channel, mr 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); +} diff --git a/libs/unimrcp/libs/mrcp-client/src/mrcp_client_session.c b/libs/unimrcp/libs/mrcp-client/src/mrcp_client_session.c index 0832fc85b9..845694c5ba 100644 --- a/libs/unimrcp/libs/mrcp-client/src/mrcp_client_session.c +++ b/libs/unimrcp/libs/mrcp-client/src/mrcp_client_session.c @@ -15,18 +15,16 @@ */ #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" @@ -38,7 +36,7 @@ void mrcp_client_session_remove(mrcp_client_t *client, mrcp_client_session_t *se 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); @@ -49,12 +47,45 @@ static apt_bool_t mrcp_client_av_media_answer_process(mrcp_client_session_t *ses 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) { @@ -73,37 +104,39 @@ mrcp_client_session_t* mrcp_client_session_create(mrcp_application_t *applicatio 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; @@ -111,15 +144,14 @@ mrcp_channel_t* mrcp_client_channel_create( 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]", @@ -128,9 +160,20 @@ apt_bool_t mrcp_client_session_answer_process(mrcp_client_session_t *session, mr 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 { @@ -138,12 +181,24 @@ apt_bool_t mrcp_client_session_answer_process(mrcp_client_session_t *session, mr 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; @@ -153,11 +208,7 @@ apt_bool_t mrcp_client_session_terminate_response_process(mrcp_client_session_t { 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; @@ -165,15 +216,33 @@ apt_bool_t mrcp_client_session_terminate_response_process(mrcp_client_session_t 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); @@ -203,27 +272,23 @@ apt_bool_t mrcp_client_session_discover_response_process(mrcp_client_session_t * 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; @@ -247,12 +312,9 @@ apt_bool_t mrcp_client_on_channel_add(mrcp_channel_t *channel, mrcp_control_desc 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; } @@ -265,15 +327,12 @@ apt_bool_t mrcp_client_on_channel_modify(mrcp_channel_t *channel, mrcp_control_d 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; } @@ -286,13 +345,10 @@ apt_bool_t mrcp_client_on_channel_remove(mrcp_channel_t *channel, apt_bool_t sta 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; } @@ -303,6 +359,12 @@ apt_bool_t mrcp_client_on_message_receive(mrcp_channel_t *channel, mrcp_message_ 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)); @@ -328,15 +390,6 @@ mrcp_app_message_t* mrcp_client_app_control_message_create(apr_pool_t *pool) 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; @@ -376,7 +429,7 @@ static apt_bool_t mrcp_app_session_terminate_raise(mrcp_client_session_t *sessio int i; mrcp_channel_t *channel; for(i=0; ichannels->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) { @@ -387,10 +440,13 @@ static apt_bool_t mrcp_app_session_terminate_raise(mrcp_client_session_t *sessio 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; @@ -398,12 +454,12 @@ static apt_bool_t mrcp_app_sig_response_raise(mrcp_client_session_t *session, mr 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) { @@ -468,8 +524,9 @@ static apt_bool_t mrcp_app_control_message_raise(mrcp_client_session_t *session, 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; ichannels->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; @@ -485,7 +542,7 @@ static rtp_termination_slot_t* mrcp_client_rtp_termination_find(mrcp_client_sess int i; rtp_termination_slot_t *slot; for(i=0; iterminations->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; } @@ -493,25 +550,12 @@ static rtp_termination_slot_t* mrcp_client_rtp_termination_find(mrcp_client_sess return NULL; } -static int mrcp_client_audio_media_find_by_mid(const mrcp_session_descriptor_t *descriptor, apr_size_t mid) -{ - int i; - mpf_rtp_media_descriptor_t *media; - for(i=0; iaudio_media_arr->nelts; i++) { - media = ((mpf_rtp_media_descriptor_t**)descriptor->audio_media_arr->elts)[i]; - if(media->mid == mid) { - return i; - } - } - return -1; -} - static mrcp_channel_t* mrcp_client_channel_termination_find(mrcp_client_session_t *session, mpf_termination_t *termination) { int i; mrcp_channel_t *channel; for(i=0; ichannels->nelts; i++) { - channel = ((mrcp_channel_t**)session->channels->elts)[i]; + channel = APR_ARRAY_IDX(session->channels,i,mrcp_channel_t*); if(!channel) continue; if(channel->termination == termination) { @@ -526,10 +570,10 @@ static mrcp_channel_t* mrcp_client_channel_find_by_name(mrcp_client_session_t *s int i; mrcp_channel_t *channel; for(i=0; ichannels->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; } } @@ -548,9 +592,9 @@ static apt_bool_t mrcp_client_message_send(mrcp_client_session_t *session, mrcp_ 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) { @@ -571,48 +615,47 @@ static apt_bool_t mrcp_client_channel_modify(mrcp_client_session_t *session, mrc 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) { @@ -622,93 +665,108 @@ static apt_bool_t mrcp_client_channel_add(mrcp_client_session_t *session, mrcp_c 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; } - /* send add termination request (add to media context) */ - if(mrcp_client_mpf_request_send(profile->media_engine,MPF_COMMAND_ADD,session->context,termination,rtp_descriptor) == TRUE) { - termination_slot->waiting = TRUE; - session->offer_flag_count++; - } - } - termination_slot->descriptor = rtp_descriptor; - channel->rtp_termination_slot = termination_slot; - if(!session->offer_flag_count) { + 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(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; + } + } + } + } + + slot->descriptor = rtp_descriptor; + channel->rtp_termination_slot = slot; + + if(!session->subrequest_count) { /* send offer to server */ mrcp_client_session_offer_send(session); } @@ -733,10 +791,21 @@ static apt_bool_t mrcp_client_session_terminate(mrcp_client_session_t *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; ichannels->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) { @@ -744,37 +813,45 @@ static apt_bool_t mrcp_client_session_terminate(mrcp_client_session_t *session) 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++; - } - } - } - } - - /* subtract existing terminations */ - for(i=0; iterminations->nelts; i++) { - /* get existing termination */ - slot = &((rtp_termination_slot_t*)session->terminations->elts)[i]; - if(!slot || !slot->termination) continue; - /* send subtract termination request */ - apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Subtract Termination "APT_PTRSID_FMT, MRCP_SESSION_PTRSID(&session->base)); - if(mrcp_client_mpf_request_send(profile->media_engine,MPF_COMMAND_SUBTRACT,session->context,slot->termination,NULL) == TRUE) { - slot->waiting = TRUE; - session->terminate_flag_count++; + 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); + } } } - session->terminate_flag_count++; + if(session->context) { + /* subtract existing terminations */ + for(i=0; iterminations->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(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); + } + + mrcp_client_session_subrequest_add(session); mrcp_session_terminate_request(&session->base); return TRUE; } @@ -785,34 +862,37 @@ static apt_bool_t mrcp_client_resource_discover(mrcp_client_session_t *session) 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; iprofile->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) { @@ -828,17 +908,15 @@ static apt_bool_t mrcp_client_on_termination_add(mrcp_client_session_t *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 { @@ -846,19 +924,16 @@ static apt_bool_t mrcp_client_on_termination_add(mrcp_client_session_t *session, 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) { @@ -873,25 +948,21 @@ static apt_bool_t mrcp_client_on_termination_modify(mrcp_client_session_t *sessi 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) { @@ -904,11 +975,8 @@ static apt_bool_t mrcp_client_on_termination_subtract(mrcp_client_session_t *ses 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 { @@ -916,47 +984,65 @@ static apt_bool_t mrcp_client_on_termination_subtract(mrcp_client_session_t *ses 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; icount; 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; } @@ -998,7 +1084,7 @@ static apt_bool_t mrcp_client_control_media_answer_process(mrcp_client_session_t /* update existing control channels */ for(i=0; ichannels->elts + i); + channel = APR_ARRAY_IDX(session->channels,i,mrcp_channel_t*); if(!channel) continue; /* get control descriptor */ @@ -1007,7 +1093,7 @@ static apt_bool_t mrcp_client_control_media_answer_process(mrcp_client_session_t 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; @@ -1029,7 +1115,7 @@ static apt_bool_t mrcp_client_av_media_answer_process(mrcp_client_session_t *ses 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); @@ -1045,9 +1131,20 @@ static apt_bool_t mrcp_client_av_media_answer_process(mrcp_client_session_t *ses /* 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); + } } } } @@ -1056,6 +1153,15 @@ static apt_bool_t mrcp_client_av_media_answer_process(mrcp_client_session_t *ses 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); @@ -1063,6 +1169,7 @@ static apt_bool_t mrcp_app_request_dispatch(mrcp_client_session_t *session, cons 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: { @@ -1192,29 +1299,3 @@ MRCP_DECLARE(apt_bool_t) mrcp_application_message_dispatch(const mrcp_app_messag } return status; } - -static apt_bool_t mrcp_client_mpf_request_send( - mpf_engine_t *engine, - mpf_command_type_e command_id, - mpf_context_t *context, - mpf_termination_t *termination, - void *descriptor) -{ - apt_task_t *media_task; - apt_task_msg_t *msg; - mpf_message_t *mpf_message; - if(!engine) { - return FALSE; - } - media_task = mpf_task_get(engine); - msg = apt_task_msg_get(media_task); - msg->type = TASK_MSG_USER; - mpf_message = (mpf_message_t*) msg->data; - - mpf_message->message_type = MPF_MESSAGE_TYPE_REQUEST; - mpf_message->command_id = command_id; - mpf_message->context = context; - mpf_message->termination = termination; - mpf_message->descriptor = descriptor; - return apt_task_msg_signal(media_task,msg); -} diff --git a/libs/unimrcp/libs/mrcp-engine/Makefile.am b/libs/unimrcp/libs/mrcp-engine/Makefile.am index 3dfab1b177..74bac2d06b 100644 --- a/libs/unimrcp/libs/mrcp-engine/Makefile.am +++ b/libs/unimrcp/libs/mrcp-engine/Makefile.am @@ -11,7 +11,25 @@ INCLUDES = -I$(top_srcdir)/libs/mrcp-engine/include \ 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 diff --git a/libs/unimrcp/libs/mrcp-engine/include/mrcp_engine_factory.h b/libs/unimrcp/libs/mrcp-engine/include/mrcp_engine_factory.h new file mode 100644 index 0000000000..ac8ccd78da --- /dev/null +++ b/libs/unimrcp/libs/mrcp-engine/include/mrcp_engine_factory.h @@ -0,0 +1,57 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#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__*/ diff --git a/libs/unimrcp/libs/mrcp-engine/include/mrcp_engine_iface.h b/libs/unimrcp/libs/mrcp-engine/include/mrcp_engine_iface.h new file mode 100644 index 0000000000..ee123fee0f --- /dev/null +++ b/libs/unimrcp/libs/mrcp-engine/include/mrcp_engine_iface.h @@ -0,0 +1,93 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#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__*/ diff --git a/libs/unimrcp/libs/mrcp-engine/include/mrcp_engine_impl.h b/libs/unimrcp/libs/mrcp-engine/include/mrcp_engine_impl.h new file mode 100644 index 0000000000..32de2ad6b2 --- /dev/null +++ b/libs/unimrcp/libs/mrcp-engine/include/mrcp_engine_impl.h @@ -0,0 +1,121 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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__*/ diff --git a/libs/unimrcp/libs/mrcp-engine/include/mrcp_engine_loader.h b/libs/unimrcp/libs/mrcp-engine/include/mrcp_engine_loader.h new file mode 100644 index 0000000000..a4fc9d2e39 --- /dev/null +++ b/libs/unimrcp/libs/mrcp-engine/include/mrcp_engine_loader.h @@ -0,0 +1,48 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __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__*/ diff --git a/libs/unimrcp/libs/mrcp-engine/include/mrcp_resource_plugin.h b/libs/unimrcp/libs/mrcp-engine/include/mrcp_engine_plugin.h similarity index 76% rename from libs/unimrcp/libs/mrcp-engine/include/mrcp_resource_plugin.h rename to libs/unimrcp/libs/mrcp-engine/include/mrcp_engine_plugin.h index 4dcc3be182..103944eb1f 100644 --- a/libs/unimrcp/libs/mrcp-engine/include/mrcp_resource_plugin.h +++ b/libs/unimrcp/libs/mrcp-engine/include/mrcp_engine_plugin.h @@ -14,16 +14,17 @@ * 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 @@ -34,17 +35,17 @@ 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 */ @@ -52,6 +53,11 @@ typedef apt_bool_t (*mrcp_plugin_log_accessor_f)(apt_logger_t *logger); 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 @@ -64,7 +70,7 @@ typedef apt_bool_t (*mrcp_plugin_log_accessor_f)(apt_logger_t *logger); * 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. @@ -107,4 +113,4 @@ static APR_INLINE int mrcp_plugin_version_check(mrcp_plugin_version_t *version) APT_END_EXTERN_C -#endif /*__MRCP_RESOURCE_PLUGIN_H__*/ +#endif /*__MRCP_ENGINE_PLUGIN_H__*/ diff --git a/libs/unimrcp/libs/mrcp-engine/include/mrcp_engine_types.h b/libs/unimrcp/libs/mrcp-engine/include/mrcp_engine_types.h new file mode 100644 index 0000000000..eec9ec89a5 --- /dev/null +++ b/libs/unimrcp/libs/mrcp-engine/include/mrcp_engine_types.h @@ -0,0 +1,141 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MRCP_ENGINE_TYPES_H__ +#define __MRCP_ENGINE_TYPES_H__ + +/** + * @file mrcp_engine_types.h + * @brief MRCP Engine Types + */ + +#include +#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__*/ diff --git a/libs/unimrcp/libs/mrcp-engine/include/mrcp_recog_engine.h b/libs/unimrcp/libs/mrcp-engine/include/mrcp_recog_engine.h new file mode 100644 index 0000000000..2e58c0663c --- /dev/null +++ b/libs/unimrcp/libs/mrcp-engine/include/mrcp_recog_engine.h @@ -0,0 +1,33 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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__*/ diff --git a/libs/unimrcp/libs/mrcp/resources/include/mrcp_recog_state_machine.h b/libs/unimrcp/libs/mrcp-engine/include/mrcp_recog_state_machine.h similarity index 72% rename from libs/unimrcp/libs/mrcp/resources/include/mrcp_recog_state_machine.h rename to libs/unimrcp/libs/mrcp-engine/include/mrcp_recog_state_machine.h index ee04601c22..32d5482593 100644 --- a/libs/unimrcp/libs/mrcp/resources/include/mrcp_recog_state_machine.h +++ b/libs/unimrcp/libs/mrcp-engine/include/mrcp_recog_state_machine.h @@ -26,11 +26,8 @@ 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 diff --git a/libs/unimrcp/libs/mrcp-engine/include/mrcp_recorder_engine.h b/libs/unimrcp/libs/mrcp-engine/include/mrcp_recorder_engine.h new file mode 100644 index 0000000000..3e4e818d4d --- /dev/null +++ b/libs/unimrcp/libs/mrcp-engine/include/mrcp_recorder_engine.h @@ -0,0 +1,33 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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__*/ diff --git a/libs/unimrcp/libs/mrcp/resources/include/mrcp_default_factory.h b/libs/unimrcp/libs/mrcp-engine/include/mrcp_recorder_state_machine.h similarity index 62% rename from libs/unimrcp/libs/mrcp/resources/include/mrcp_default_factory.h rename to libs/unimrcp/libs/mrcp-engine/include/mrcp_recorder_state_machine.h index 7820aae47b..ccd6096d95 100644 --- a/libs/unimrcp/libs/mrcp/resources/include/mrcp_default_factory.h +++ b/libs/unimrcp/libs/mrcp-engine/include/mrcp_recorder_state_machine.h @@ -14,22 +14,21 @@ * 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__*/ diff --git a/libs/unimrcp/libs/mrcp-engine/include/mrcp_resource_engine.h b/libs/unimrcp/libs/mrcp-engine/include/mrcp_resource_engine.h index 4432a64b51..c69e5d0b93 100644 --- a/libs/unimrcp/libs/mrcp-engine/include/mrcp_resource_engine.h +++ b/libs/unimrcp/libs/mrcp-engine/include/mrcp_resource_engine.h @@ -19,197 +19,31 @@ /** * @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) +/** + * 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 engine->method_vtable->destroy(engine); + return mrcp_engine_create(resource_id,obj,vtable,pool); } -/** Open resource engine */ -static APR_INLINE apt_bool_t mrcp_resource_engine_open(mrcp_resource_engine_t *engine) -{ - return engine->method_vtable->open(engine); -} - -/** Close resource engine */ -static APR_INLINE apt_bool_t mrcp_resource_engine_close(mrcp_resource_engine_t *engine) -{ - return engine->method_vtable->close(engine); -} - -/** Create engine channel */ -mrcp_engine_channel_t* mrcp_engine_channel_create( - mrcp_resource_engine_t *engine, - const mrcp_engine_channel_method_vtable_t *method_vtable, - void *method_obj, - mpf_termination_t *termination, - apr_pool_t *pool); - -/** Create engine channel and source media termination */ -mrcp_engine_channel_t* mrcp_engine_source_channel_create( - mrcp_resource_engine_t *engine, - const mrcp_engine_channel_method_vtable_t *channel_vtable, - const mpf_audio_stream_vtable_t *stream_vtable, - void *method_obj, - mpf_codec_descriptor_t *codec_descriptor, - apr_pool_t *pool); - -/** Create engine channel and sink media termination */ -mrcp_engine_channel_t* mrcp_engine_sink_channel_create( - mrcp_resource_engine_t *engine, - const mrcp_engine_channel_method_vtable_t *channel_vtable, - const mpf_audio_stream_vtable_t *stream_vtable, - void *method_obj, - mpf_codec_descriptor_t *codec_descriptor, - apr_pool_t *pool); - -/** Destroy engine channel */ -static APR_INLINE apt_bool_t mrcp_engine_channel_destroy(mrcp_engine_channel_t *channel) -{ - return channel->method_vtable->destroy(channel); -} - -/** Open engine channel */ -static APR_INLINE apt_bool_t mrcp_engine_channel_open(mrcp_engine_channel_t *channel) -{ - return channel->method_vtable->open(channel); -} - -/** Close engine channel */ -static APR_INLINE apt_bool_t mrcp_engine_channel_close(mrcp_engine_channel_t *channel) -{ - return channel->method_vtable->close(channel); -} - -/** Process request */ -static APR_INLINE apt_bool_t mrcp_engine_channel_request_process(mrcp_engine_channel_t *channel, mrcp_message_t *message) -{ - return channel->method_vtable->process_request(channel,message); -} - -/** Send channel open response */ -static APR_INLINE apt_bool_t mrcp_engine_channel_open_respond(mrcp_engine_channel_t *channel, apt_bool_t status) -{ - return channel->event_vtable->on_open(channel,status); -} - -/** Send channel close response */ -static APR_INLINE apt_bool_t mrcp_engine_channel_close_respond(mrcp_engine_channel_t *channel) -{ - return channel->event_vtable->on_close(channel); -} - -/** Send response/event message */ -static APR_INLINE apt_bool_t mrcp_engine_channel_message_send(mrcp_engine_channel_t *channel, mrcp_message_t *message) -{ - return channel->event_vtable->on_message(channel,message); -} - -/** Get codec of the audio source stream */ -mpf_codec_t* mrcp_engine_source_stream_codec_get(mrcp_engine_channel_t *channel); - -/** Get codec of the audio sink stream */ -mpf_codec_t* mrcp_engine_sink_stream_codec_get(mrcp_engine_channel_t *channel); - - APT_END_EXTERN_C #endif /*__MRCP_RESOURCE_ENGINE_H__*/ diff --git a/libs/unimrcp/libs/mrcp/control/include/mrcp_state_machine.h b/libs/unimrcp/libs/mrcp-engine/include/mrcp_state_machine.h similarity index 100% rename from libs/unimrcp/libs/mrcp/control/include/mrcp_state_machine.h rename to libs/unimrcp/libs/mrcp-engine/include/mrcp_state_machine.h diff --git a/libs/unimrcp/libs/mrcp-engine/include/mrcp_synth_engine.h b/libs/unimrcp/libs/mrcp-engine/include/mrcp_synth_engine.h new file mode 100644 index 0000000000..305f7eadaa --- /dev/null +++ b/libs/unimrcp/libs/mrcp-engine/include/mrcp_synth_engine.h @@ -0,0 +1,33 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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__*/ diff --git a/libs/unimrcp/libs/mrcp/resources/include/mrcp_synth_state_machine.h b/libs/unimrcp/libs/mrcp-engine/include/mrcp_synth_state_machine.h similarity index 72% rename from libs/unimrcp/libs/mrcp/resources/include/mrcp_synth_state_machine.h rename to libs/unimrcp/libs/mrcp-engine/include/mrcp_synth_state_machine.h index eed266e2a4..efe26dcc73 100644 --- a/libs/unimrcp/libs/mrcp/resources/include/mrcp_synth_state_machine.h +++ b/libs/unimrcp/libs/mrcp-engine/include/mrcp_synth_state_machine.h @@ -26,11 +26,8 @@ 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 diff --git a/libs/unimrcp/libs/mrcp-engine/mrcpengine.vcproj b/libs/unimrcp/libs/mrcp-engine/mrcpengine.vcproj index b5fa4aba9e..ed21f76c04 100644 --- a/libs/unimrcp/libs/mrcp-engine/mrcpengine.vcproj +++ b/libs/unimrcp/libs/mrcp-engine/mrcpengine.vcproj @@ -129,12 +129,60 @@ Filter="h;hpp;hxx;hm;inl;inc;xsd" UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > + + + + + + + + + + + + + + + + + + + + + + + + @@ -143,7 +191,31 @@ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" > + + + + + + + + + + + + diff --git a/libs/unimrcp/libs/mrcp-engine/src/mrcp_engine_factory.c b/libs/unimrcp/libs/mrcp-engine/src/mrcp_engine_factory.c new file mode 100644 index 0000000000..2faaabfd2c --- /dev/null +++ b/libs/unimrcp/libs/mrcp-engine/src/mrcp_engine_factory.c @@ -0,0 +1,147 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "mrcp_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; +} diff --git a/libs/unimrcp/libs/mrcp-engine/src/mrcp_engine_iface.c b/libs/unimrcp/libs/mrcp-engine/src/mrcp_engine_iface.c new file mode 100644 index 0000000000..137af17827 --- /dev/null +++ b/libs/unimrcp/libs/mrcp-engine/src/mrcp_engine_iface.c @@ -0,0 +1,55 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#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; +} diff --git a/libs/unimrcp/libs/mrcp-engine/src/mrcp_engine_impl.c b/libs/unimrcp/libs/mrcp-engine/src/mrcp_engine_impl.c new file mode 100644 index 0000000000..e2c7a24dae --- /dev/null +++ b/libs/unimrcp/libs/mrcp-engine/src/mrcp_engine_impl.c @@ -0,0 +1,241 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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; +} diff --git a/libs/unimrcp/libs/mrcp-engine/src/mrcp_engine_loader.c b/libs/unimrcp/libs/mrcp-engine/src/mrcp_engine_loader.c new file mode 100644 index 0000000000..dfa5bd617a --- /dev/null +++ b/libs/unimrcp/libs/mrcp-engine/src/mrcp_engine_loader.c @@ -0,0 +1,164 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "mrcp_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; +} diff --git a/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_server_state_machine.c b/libs/unimrcp/libs/mrcp-engine/src/mrcp_recog_state_machine.c similarity index 92% rename from libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_server_state_machine.c rename to libs/unimrcp/libs/mrcp-engine/src/mrcp_recog_state_machine.c index 206512967a..1965192d96 100644 --- a/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_server_state_machine.c +++ b/libs/unimrcp/libs/mrcp-engine/src/mrcp_recog_state_machine.c @@ -95,26 +95,30 @@ static APR_INLINE void recog_state_change(mrcp_recog_state_machine_t *state_mach 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); @@ -131,13 +135,15 @@ static apt_bool_t recog_request_define_grammar(mrcp_recog_state_machine_t *state 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; @@ -151,7 +157,8 @@ static apt_bool_t recog_request_recognize(mrcp_recog_state_machine_t *state_mach 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); @@ -160,13 +167,15 @@ static apt_bool_t recog_request_recognize(mrcp_recog_state_machine_t *state_mach 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); @@ -184,7 +193,8 @@ static apt_bool_t recog_request_get_result(mrcp_recog_state_machine_t *state_mac 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); } @@ -196,7 +206,8 @@ static apt_bool_t recog_request_get_result(mrcp_recog_state_machine_t *state_mac 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); } @@ -205,7 +216,8 @@ static apt_bool_t recog_request_recognition_start_timers(mrcp_recog_state_machin 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); } @@ -217,7 +229,8 @@ static apt_bool_t recog_request_recognition_start_timers(mrcp_recog_state_machin 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); } @@ -270,7 +283,8 @@ static apt_bool_t recog_request_stop(mrcp_recog_state_machine_t *state_machine, 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); } } @@ -288,7 +302,8 @@ static apt_bool_t recog_response_stop(mrcp_recog_state_machine_t *state_machine, { 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); @@ -318,7 +333,8 @@ static apt_bool_t recog_event_start_of_input(mrcp_recog_state_machine_t *state_m 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); } @@ -327,12 +343,14 @@ static apt_bool_t recog_event_recognition_complete(mrcp_recog_state_machine_t *s { 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; } @@ -341,7 +359,8 @@ static apt_bool_t recog_event_recognition_complete(mrcp_recog_state_machine_t *s 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; @@ -478,19 +497,21 @@ static apt_bool_t recog_state_deactivate(mrcp_state_machine_t *base) /* 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)); diff --git a/libs/unimrcp/libs/mrcp-engine/src/mrcp_recorder_state_machine.c b/libs/unimrcp/libs/mrcp-engine/src/mrcp_recorder_state_machine.c new file mode 100644 index 0000000000..9692d33244 --- /dev/null +++ b/libs/unimrcp/libs/mrcp-engine/src/mrcp_recorder_state_machine.c @@ -0,0 +1,392 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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; +} diff --git a/libs/unimrcp/libs/mrcp-engine/src/mrcp_resource_engine.c b/libs/unimrcp/libs/mrcp-engine/src/mrcp_resource_engine.c deleted file mode 100644 index 54939d9eb6..0000000000 --- a/libs/unimrcp/libs/mrcp-engine/src/mrcp_resource_engine.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2008 Arsen Chaloyan - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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; -} diff --git a/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_server_state_machine.c b/libs/unimrcp/libs/mrcp-engine/src/mrcp_synth_state_machine.c similarity index 91% rename from libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_server_state_machine.c rename to libs/unimrcp/libs/mrcp-engine/src/mrcp_synth_state_machine.c index 73ed63e953..01ff0af30f 100644 --- a/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_server_state_machine.c +++ b/libs/unimrcp/libs/mrcp-engine/src/mrcp_synth_state_machine.c @@ -95,26 +95,30 @@ static APR_INLINE void synth_state_change(mrcp_synth_state_machine_t *state_mach 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); @@ -125,7 +129,8 @@ static apt_bool_t synth_request_speak(mrcp_synth_state_machine_t *state_machine, 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); @@ -134,13 +139,15 @@ static apt_bool_t synth_request_speak(mrcp_synth_state_machine_t *state_machine, 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); @@ -207,7 +214,8 @@ static apt_bool_t synth_request_stop(mrcp_synth_state_machine_t *state_machine, 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); } } @@ -222,7 +230,8 @@ static apt_bool_t synth_response_stop(mrcp_synth_state_machine_t *state_machine, { 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); @@ -265,7 +274,8 @@ static apt_bool_t synth_request_pause(mrcp_synth_state_machine_t *state_machine, 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 */ @@ -302,7 +312,8 @@ static apt_bool_t synth_request_resume(mrcp_synth_state_machine_t *state_machine 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 */ @@ -327,7 +338,8 @@ static apt_bool_t synth_request_barge_in_occurred(mrcp_synth_state_machine_t *st } 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); } } @@ -340,7 +352,8 @@ static apt_bool_t synth_request_barge_in_occurred(mrcp_synth_state_machine_t *st 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); @@ -353,7 +366,8 @@ static apt_bool_t synth_request_control(mrcp_synth_state_machine_t *state_machin { 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); } @@ -365,7 +379,8 @@ static apt_bool_t synth_request_control(mrcp_synth_state_machine_t *state_machin 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); @@ -376,7 +391,8 @@ static apt_bool_t synth_request_define_lexicon(mrcp_synth_state_machine_t *state { 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); } @@ -388,7 +404,8 @@ static apt_bool_t synth_request_define_lexicon(mrcp_synth_state_machine_t *state 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); } @@ -404,7 +421,8 @@ static apt_bool_t synth_event_speech_marker(mrcp_synth_state_machine_t *state_ma 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); } @@ -413,12 +431,14 @@ static apt_bool_t synth_event_speak_complete(mrcp_synth_state_machine_t *state_m { 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; } @@ -427,7 +447,8 @@ static apt_bool_t synth_event_speak_complete(mrcp_synth_state_machine_t *state_m 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; @@ -565,19 +586,21 @@ static apt_bool_t synth_state_deactivate(mrcp_state_machine_t *base) /* 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)); diff --git a/libs/unimrcp/libs/mrcp-server/include/mrcp_server.h b/libs/unimrcp/libs/mrcp-server/include/mrcp_server.h index e4946364b1..59a33db059 100644 --- a/libs/unimrcp/libs/mrcp-server/include/mrcp_server.h +++ b/libs/unimrcp/libs/mrcp-server/include/mrcp_server.h @@ -23,7 +23,7 @@ */ #include "mrcp_server_types.h" -#include "mrcp_resource_engine.h" +#include "mrcp_engine_iface.h" #include "apt_task.h" APT_BEGIN_EXTERN_C @@ -62,12 +62,12 @@ MRCP_DECLARE(apt_bool_t) mrcp_server_destroy(mrcp_server_t *server); 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. @@ -127,7 +127,7 @@ MRCP_DECLARE(mrcp_profile_t*) mrcp_server_profile_create( * 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( @@ -137,12 +137,12 @@ 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. @@ -150,13 +150,6 @@ MRCP_DECLARE(apt_bool_t) mrcp_server_plugin_register(mrcp_server_t *server, cons */ 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 diff --git a/libs/unimrcp/libs/mrcp-server/include/mrcp_server_session.h b/libs/unimrcp/libs/mrcp-server/include/mrcp_server_session.h index 7af9884e5d..4cd88f6b3e 100644 --- a/libs/unimrcp/libs/mrcp-server/include/mrcp_server_session.h +++ b/libs/unimrcp/libs/mrcp-server/include/mrcp_server_session.h @@ -24,7 +24,7 @@ #include #include "mrcp_session.h" -#include "mpf_message.h" +#include "mpf_engine.h" #include "apt_task.h" #include "apt_obj_list.h" @@ -61,45 +61,55 @@ struct mrcp_signaling_message_t { 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; + 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; + /** 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; @@ -114,12 +124,12 @@ struct mrcp_profile_t { }; /** 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); @@ -127,6 +137,8 @@ apt_bool_t mrcp_server_on_channel_modify(mrcp_channel_t *channel, mrcp_control_d 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); diff --git a/libs/unimrcp/libs/mrcp-server/src/mrcp_server.c b/libs/unimrcp/libs/mrcp-server/src/mrcp_server.c index 01d03c8d87..0c8d2a8a07 100644 --- a/libs/unimrcp/libs/mrcp-server/src/mrcp_server.c +++ b/libs/unimrcp/libs/mrcp-server/src/mrcp_server.c @@ -14,11 +14,13 @@ * limitations under the License. */ -#include #include "mrcp_server.h" #include "mrcp_server_session.h" #include "mrcp_message.h" #include "mrcp_resource_factory.h" +#include "mrcp_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" @@ -36,10 +38,13 @@ struct mrcp_server_t { /** 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*) */ @@ -50,16 +55,14 @@ struct mrcp_server_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; @@ -73,7 +76,7 @@ struct mrcp_server_t { 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; @@ -95,7 +98,7 @@ typedef enum { 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; @@ -110,24 +113,26 @@ static apt_bool_t mrcp_server_channel_add_signal(mrcp_control_channel_t *channel 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; @@ -170,16 +175,16 @@ MRCP_DECLARE(mrcp_server_t*) mrcp_server_create(apt_dir_layout_t *dir_layout) 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); @@ -197,14 +202,15 @@ MRCP_DECLARE(mrcp_server_t*) mrcp_server_create(apt_dir_layout_t *dir_layout) 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; @@ -255,6 +261,10 @@ MRCP_DECLARE(apt_bool_t) mrcp_server_destroy(mrcp_server_t *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); @@ -273,26 +283,20 @@ MRCP_DECLARE(apt_bool_t) mrcp_server_resource_factory_register(mrcp_server_t *se 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 */ @@ -424,48 +428,37 @@ MRCP_DECLARE(mrcp_profile_t*) mrcp_server_profile_create( 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; iresource_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); } } @@ -484,6 +477,10 @@ MRCP_DECLARE(apt_bool_t) mrcp_server_profile_register( 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); @@ -517,74 +514,27 @@ MRCP_DECLARE(mrcp_profile_t*) mrcp_server_profile_get(mrcp_server_t *server, con 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; + } + + engine = mrcp_engine_loader_plugin_load(server->engine_loader,path,config->name); + if(!engine) { 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); - } - } + if(!server->engine_msg_pool) { + server->engine_msg_pool = apt_task_msg_pool_create_dynamic(sizeof(engine_task_msg_data_t),server->pool); } - else { - char derr[512] = ""; - apr_dso_error(plugin,derr,sizeof(derr)); - apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Load DSO: %s", derr); - return FALSE; - } - - if(plugin_creator) { - mrcp_resource_engine_t *engine = plugin_creator(server->pool); - if(engine) { - if(mrcp_plugin_version_check(&engine->plugin_version)) { - status = TRUE; - mrcp_server_resource_engine_register(server,engine,name); - apr_hash_set(server->plugin_table,name,APR_HASH_KEY_STRING,plugin); - } - else { - apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Incompatible Plugin Version [%d.%d.%d] < ["PLUGIN_VERSION_STRING"]", - engine->plugin_version.major, - engine->plugin_version.minor, - engine->plugin_version.patch); - } - } - else { - apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Create Resource Engine"); - } - } - else { - apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"No Entry Point Found for Plugin"); - } - - if(status == FALSE) { - apr_dso_unload(plugin); - } - return status; + 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) @@ -613,22 +563,13 @@ static APR_INLINE mrcp_server_session_t* mrcp_server_session_find(mrcp_server_t 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"); } @@ -636,28 +577,8 @@ static void mrcp_server_on_terminate_complete(apt_task_t *task) { apt_consumer_task_t *consumer_task = apt_task_object_get(task); mrcp_server_t *server = apt_consumer_task_object_get(consumer_task); - mrcp_resource_engine_t *resource_engine; - apr_dso_handle_t *plugin; - apr_hash_index_t *it; - void *val; - apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Close Resource Engines"); - it=apr_hash_first(server->pool,server->resource_engine_table); - for(; it; it = apr_hash_next(it)) { - apr_hash_this(it,NULL,NULL,&val); - resource_engine = val; - if(resource_engine) { - mrcp_resource_engine_close(resource_engine); - } - } - apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Unload Plugins"); - it=apr_hash_first(server->pool,server->plugin_table); - for(; it; it = apr_hash_next(it)) { - apr_hash_this(it,NULL,NULL,&val); - plugin = val; - if(plugin) { - apr_dso_unload(plugin); - } - } + + mrcp_engine_factory_close(server->engine_factory); apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,SERVER_TASK_NAME" Terminated"); } @@ -696,23 +617,28 @@ static apt_bool_t mrcp_server_msg_process(apt_task_t *task, apt_task_msg_t *msg) 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: @@ -722,9 +648,9 @@ static apt_bool_t mrcp_server_msg_process(apt_task_t *task, apt_task_msg_t *msg) } 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: @@ -781,7 +707,7 @@ static apt_bool_t mrcp_server_connection_task_msg_signal( } 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) @@ -790,16 +716,16 @@ static apt_bool_t mrcp_server_engine_task_msg_signal( 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); } @@ -902,10 +828,22 @@ static apt_bool_t mrcp_server_message_signal(mrcp_control_channel_t *channel, mr 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); @@ -914,7 +852,7 @@ static apt_bool_t mrcp_server_channel_open_signal(mrcp_engine_channel_t *channel 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); @@ -923,7 +861,7 @@ static apt_bool_t mrcp_server_channel_close_signal(mrcp_engine_channel_t *channe 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); diff --git a/libs/unimrcp/libs/mrcp-server/src/mrcp_server_session.c b/libs/unimrcp/libs/mrcp-server/src/mrcp_server_session.c index 7269ddf60f..b254300921 100644 --- a/libs/unimrcp/libs/mrcp-server/src/mrcp_server_session.c +++ b/libs/unimrcp/libs/mrcp-server/src/mrcp_server_session.c @@ -18,16 +18,15 @@ #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" @@ -37,19 +36,19 @@ struct mrcp_channel_t { /** Memory pool */ apr_pool_t *pool; /** MRCP resource */ - apt_str_t resource_name; - /** MRCP resource */ mrcp_resource_t *resource; /** MRCP session entire channel belongs to */ mrcp_session_t *session; /** MRCP control channel */ mrcp_control_channel_t *control_channel; - /** MRCP resource engine channel */ + /** MRCP engine channel */ 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 */ @@ -60,11 +59,16 @@ typedef struct mrcp_termination_slot_t mrcp_termination_slot_t; struct mrcp_termination_slot_t { /** RTP termination */ - mpf_termination_t *termination; - /** media descriptor id */ - apr_size_t id; + 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; @@ -78,21 +82,13 @@ static apt_bool_t mrcp_server_resource_offer_process(mrcp_server_session_t *sess 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); @@ -107,27 +103,44 @@ mrcp_server_session_t* mrcp_server_session_create() 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; @@ -140,37 +153,23 @@ static mrcp_channel_t* mrcp_server_channel_create(mrcp_server_session_t *session 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; @@ -178,7 +177,7 @@ static mrcp_channel_t* mrcp_server_channel_create(mrcp_server_session_t *session 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; } } @@ -195,6 +194,48 @@ static mrcp_channel_t* mrcp_server_channel_create(mrcp_server_session_t *session 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; @@ -227,13 +268,7 @@ apt_bool_t mrcp_server_on_channel_modify(mrcp_channel_t *channel, mrcp_control_d 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; } @@ -245,12 +280,7 @@ apt_bool_t mrcp_server_on_channel_remove(mrcp_channel_t *channel, apt_bool_t sta 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; } @@ -267,6 +297,12 @@ apt_bool_t mrcp_server_on_channel_message(mrcp_channel_t *channel, mrcp_message_ 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; @@ -274,13 +310,7 @@ apt_bool_t mrcp_server_on_engine_channel_open(mrcp_channel_t *channel, apt_bool_ 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; } @@ -288,12 +318,7 @@ apt_bool_t mrcp_server_on_engine_channel_close(mrcp_channel_t *channel) { mrcp_server_session_t *session = (mrcp_server_session_t*)channel->session; apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"On Engine Channel Close"); - if(session->terminate_flag_count) { - session->terminate_flag_count--; - if(!session->terminate_flag_count) { - mrcp_server_session_terminate_send(session); - } - } + mrcp_server_session_subrequest_remove(session); return TRUE; } @@ -307,41 +332,9 @@ apt_bool_t mrcp_server_on_engine_channel_message(mrcp_channel_t *channel, mrcp_m } -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); @@ -351,18 +344,15 @@ static mrcp_session_descriptor_t* mrcp_session_answer_create(mrcp_session_descri answer->status = offer->status; answer->control_media_arr = apr_array_make(pool,offer->control_media_arr->nelts,sizeof(void*)); for(i=0; icontrol_media_arr->nelts; i++) { - control_slot = apr_array_push(answer->control_media_arr); - *control_slot = NULL; + 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; iaudio_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; ivideo_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; } @@ -376,7 +366,7 @@ static apt_bool_t mrcp_server_session_offer_process(mrcp_server_session_t *sessi } 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), @@ -388,7 +378,17 @@ static apt_bool_t mrcp_server_session_offer_process(mrcp_server_session_t *sessi 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); } @@ -400,8 +400,17 @@ static apt_bool_t mrcp_server_session_offer_process(mrcp_server_session_t *sessi 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); } @@ -414,8 +423,21 @@ static apt_bool_t mrcp_server_session_terminate_process(mrcp_server_session_t *s 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; ichannels->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 */ @@ -423,7 +445,7 @@ static apt_bool_t mrcp_server_session_terminate_process(mrcp_server_session_t *s 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); } } @@ -432,33 +454,44 @@ static apt_bool_t mrcp_server_session_terminate_process(mrcp_server_session_t *s /* 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; iterminations->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); } @@ -470,16 +503,17 @@ static apt_bool_t mrcp_server_session_deactivate(mrcp_server_session_t *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; ichannels->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); } @@ -523,12 +557,63 @@ static apt_bool_t mrcp_server_signaling_message_dispatch(mrcp_server_session_t * 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; ichannels->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) { @@ -536,36 +621,22 @@ static apt_bool_t mrcp_server_resource_offer_process(mrcp_server_session_t *sess 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); } } } @@ -589,8 +660,7 @@ static apt_bool_t mrcp_server_control_media_offer_process(mrcp_server_session_t /* update existing control channels */ for(i=0; ichannels->elts + i); + channel = APR_ARRAY_IDX(session->channels,i,mrcp_channel_t*); if(!channel) continue; channel->waiting_for_channel = FALSE; @@ -603,7 +673,7 @@ static apt_bool_t mrcp_server_control_media_offer_process(mrcp_server_session_t /* 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); } } @@ -617,26 +687,23 @@ static apt_bool_t mrcp_server_control_media_offer_process(mrcp_server_session_t /* add new control channels */ for(; icontrol_media_arr->nelts; i++) { - mrcp_channel_t **slot; /* get control descriptor */ control_descriptor = mrcp_session_control_media_get(descriptor,i); if(!control_descriptor) continue; /* create new MRCP channel instance */ - channel = mrcp_server_channel_create(session,&control_descriptor->resource_name,i); + 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); } } @@ -647,19 +714,15 @@ static apt_bool_t mrcp_server_control_media_offer_process(mrcp_server_session_t 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); } } } @@ -667,8 +730,92 @@ static apt_bool_t mrcp_server_control_media_offer_process(mrcp_server_session_t 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; ichannels->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; ichannels->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; @@ -684,28 +831,30 @@ static apt_bool_t mrcp_server_av_media_offer_process(mrcp_server_session_t *sess /* update existing terminations */ for(i=0; iterminations->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(; iaudio_media_arr->nelts; i++) { - mpf_rtp_termination_descriptor_t *rtp_descriptor; mpf_termination_t *termination; /* create new RTP termination instance */ termination = mpf_termination_create(session->profile->rtp_termination_factory,session,session->base.pool); @@ -713,20 +862,26 @@ static apt_bool_t mrcp_server_av_media_offer_process(mrcp_server_session_t *sess 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; } @@ -757,7 +912,7 @@ static apt_bool_t mrcp_server_session_terminate_send(mrcp_server_session_t *sess int i; mrcp_channel_t *channel; for(i=0; ichannels->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) { @@ -765,7 +920,7 @@ static apt_bool_t mrcp_server_session_terminate_send(mrcp_server_session_t *sess 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; } } @@ -780,7 +935,7 @@ static mrcp_termination_slot_t* mrcp_server_rtp_termination_find(mrcp_server_ses int i; mrcp_termination_slot_t *slot; for(i=0; iterminations->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; } @@ -793,7 +948,7 @@ static mrcp_channel_t* mrcp_server_channel_termination_find(mrcp_server_session_ int i; mrcp_channel_t *channel; for(i=0; ichannels->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) { @@ -808,10 +963,10 @@ static mrcp_channel_t* mrcp_server_channel_find(mrcp_server_session_t *session, int i; mrcp_channel_t *channel; for(i=0; ichannels->nelts; i++) { - channel = ((mrcp_channel_t**)session->channels->elts)[i]; + 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; } } @@ -834,30 +989,18 @@ static apt_bool_t mrcp_server_on_termination_modify(mrcp_server_session_t *sessi 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; @@ -876,25 +1019,61 @@ static apt_bool_t mrcp_server_on_termination_subtract(mrcp_server_session_t *ses 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; icount; 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; } @@ -904,7 +1083,7 @@ static apt_bool_t state_machine_on_message_dispatch(mrcp_state_machine_t *state_ 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); } @@ -944,33 +1123,6 @@ static apt_bool_t state_machine_on_deactivate(mrcp_state_machine_t *state_machin { 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); -} diff --git a/libs/unimrcp/libs/mrcp-signaling/include/mrcp_session.h b/libs/unimrcp/libs/mrcp-signaling/include/mrcp_session.h index 192da1c812..173d1c39b2 100644 --- a/libs/unimrcp/libs/mrcp-signaling/include/mrcp_session.h +++ b/libs/unimrcp/libs/mrcp-signaling/include/mrcp_session.h @@ -27,10 +27,13 @@ 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) diff --git a/libs/unimrcp/libs/mrcp-signaling/include/mrcp_session_descriptor.h b/libs/unimrcp/libs/mrcp-signaling/include/mrcp_session_descriptor.h index 4a2fa19b53..7a94a42496 100644 --- a/libs/unimrcp/libs/mrcp-signaling/include/mrcp_session_descriptor.h +++ b/libs/unimrcp/libs/mrcp-signaling/include/mrcp_session_descriptor.h @@ -69,8 +69,7 @@ static APR_INLINE apr_size_t mrcp_session_media_count_get(const mrcp_session_des 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; } @@ -79,7 +78,7 @@ static APR_INLINE void* mrcp_session_control_media_get(const mrcp_session_descri 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) @@ -87,15 +86,14 @@ static APR_INLINE apt_bool_t mrcp_session_control_media_set(mrcp_session_descrip 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; } @@ -104,7 +102,7 @@ static APR_INLINE mpf_rtp_media_descriptor_t* mrcp_session_audio_media_get(const 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) @@ -112,15 +110,14 @@ static APR_INLINE apt_bool_t mrcp_session_audio_media_set(const mrcp_session_des 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; } @@ -129,7 +126,7 @@ static APR_INLINE mpf_rtp_media_descriptor_t* mrcp_session_video_media_get(const 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) @@ -137,7 +134,7 @@ static APR_INLINE apt_bool_t mrcp_session_video_media_set(mrcp_session_descripto 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; } diff --git a/libs/unimrcp/libs/mrcp/Makefile.am b/libs/unimrcp/libs/mrcp/Makefile.am index 2d823331b7..014261f81d 100644 --- a/libs/unimrcp/libs/mrcp/Makefile.am +++ b/libs/unimrcp/libs/mrcp/Makefile.am @@ -11,32 +11,31 @@ noinst_LTLIBRARIES = libmrcp.la 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 diff --git a/libs/unimrcp/libs/mrcp/control/include/mrcp_resource.h b/libs/unimrcp/libs/mrcp/control/include/mrcp_resource.h index b46851b2ac..dd7a7f8abd 100644 --- a/libs/unimrcp/libs/mrcp/control/include/mrcp_resource.h +++ b/libs/unimrcp/libs/mrcp/control/include/mrcp_resource.h @@ -24,7 +24,6 @@ #include "mrcp_types.h" #include "mrcp_header_accessor.h" -#include "mrcp_state_machine.h" APT_BEGIN_EXTERN_C @@ -32,33 +31,48 @@ 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 diff --git a/libs/unimrcp/libs/mrcp/control/include/mrcp_resource_factory.h b/libs/unimrcp/libs/mrcp/control/include/mrcp_resource_factory.h index 59af8e8ae0..f0349a7e2e 100644 --- a/libs/unimrcp/libs/mrcp/control/include/mrcp_resource_factory.h +++ b/libs/unimrcp/libs/mrcp/control/include/mrcp_resource_factory.h @@ -22,7 +22,6 @@ * @brief Aggregation of MRCP Resources */ -#include "apt_string_table.h" #include "apt_text_stream.h" #include "mrcp_types.h" @@ -34,27 +33,14 @@ MRCP_DECLARE(mrcp_resource_factory_t*) mrcp_resource_factory_create(apr_size_t r /** 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 diff --git a/libs/unimrcp/libs/mrcp/control/include/mrcp_resource_loader.h b/libs/unimrcp/libs/mrcp/control/include/mrcp_resource_loader.h new file mode 100644 index 0000000000..e88cca4305 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/control/include/mrcp_resource_loader.h @@ -0,0 +1,51 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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__*/ diff --git a/libs/unimrcp/libs/mrcp/control/src/mrcp_resource_factory.c b/libs/unimrcp/libs/mrcp/control/src/mrcp_resource_factory.c index 7280d3df41..894f89f158 100644 --- a/libs/unimrcp/libs/mrcp/control/src/mrcp_resource_factory.c +++ b/libs/unimrcp/libs/mrcp/control/src/mrcp_resource_factory.c @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include "mrcp_resource_factory.h" #include "mrcp_message.h" #include "mrcp_resource.h" @@ -21,12 +22,12 @@ /** 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 */ @@ -44,8 +45,7 @@ MRCP_DECLARE(mrcp_resource_factory_t*) mrcp_resource_factory_create(apr_size_t r for(i=0; iresource_array[i] = NULL; } - resource_factory->string_table = NULL; - + resource_factory->resource_hash = apr_hash_make(pool); return resource_factory; } @@ -59,30 +59,23 @@ MRCP_DECLARE(apt_bool_t) mrcp_resource_factory_destroy(mrcp_resource_factory_t * 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; } @@ -95,57 +88,12 @@ MRCP_DECLARE(mrcp_resource_t*) mrcp_resource_get(mrcp_resource_factory_t *resour return resource_factory->resource_array[resource_id]; } - -/** Set header accessor interface */ -static APR_INLINE void mrcp_generic_header_accessor_set(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) { - message->header.generic_header_accessor.vtable = mrcp_generic_header_vtable_get(message->start_line.version); -} - -/** Associate MRCP resource specific data by resource identifier */ -MRCP_DECLARE(apt_bool_t) mrcp_message_resourcify_by_id(mrcp_resource_factory_t *resource_factory, mrcp_message_t *message) -{ - mrcp_resource_t *resource; - const apt_str_t *name; - resource = mrcp_resource_get(resource_factory,message->channel_id.resource_id); - if(!resource) { - return FALSE; - } - name = mrcp_resource_name_get(resource_factory,resource->id); - if(!name) { - return FALSE; - } - /* associate resource_name and resource_id */ - message->channel_id.resource_name = *name; - - mrcp_generic_header_accessor_set(message); - return resource->resourcify_message_by_id(resource,message); -} - -/** Associate MRCP resource specific data by resource name */ -MRCP_DECLARE(apt_bool_t) mrcp_message_resourcify_by_name(mrcp_resource_factory_t *resource_factory, mrcp_message_t *message) -{ - mrcp_resource_t *resource; - /* associate resource_name and resource_id */ - const apt_str_t *name = &message->channel_id.resource_name; - message->channel_id.resource_id = mrcp_resource_id_find(resource_factory,name); - resource = mrcp_resource_get(resource_factory,message->channel_id.resource_id); - if(!resource) { - return FALSE; + 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); } diff --git a/libs/unimrcp/libs/mrcp/control/src/mrcp_resource_loader.c b/libs/unimrcp/libs/mrcp/control/src/mrcp_resource_loader.c new file mode 100644 index 0000000000..c670b8ba79 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/control/src/mrcp_resource_loader.c @@ -0,0 +1,133 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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; idpool); + 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; +} diff --git a/libs/unimrcp/libs/mrcp/control/src/mrcp_stream.c b/libs/unimrcp/libs/mrcp/control/src/mrcp_stream.c index f90d068642..43844161f5 100644 --- a/libs/unimrcp/libs/mrcp/control/src/mrcp_stream.c +++ b/libs/unimrcp/libs/mrcp/control/src/mrcp_stream.c @@ -121,6 +121,8 @@ static mrcp_stream_result_e mrcp_message_body_generate(mrcp_message_t *message, /** 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; @@ -130,7 +132,13 @@ MRCP_DECLARE(apt_bool_t) mrcp_message_parse(mrcp_resource_factory_t *resource_fa 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; } @@ -145,11 +153,6 @@ MRCP_DECLARE(apt_bool_t) mrcp_message_parse(mrcp_resource_factory_t *resource_fa /** 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; @@ -170,7 +173,10 @@ MRCP_DECLARE(apt_bool_t) mrcp_message_generate(mrcp_resource_factory_t *resource } /* 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; } diff --git a/libs/unimrcp/libs/mrcp/include/mrcp_types.h b/libs/unimrcp/libs/mrcp/include/mrcp_types.h index 9b381ab2c3..3177077b39 100644 --- a/libs/unimrcp/libs/mrcp/include/mrcp_types.h +++ b/libs/unimrcp/libs/mrcp/include/mrcp_types.h @@ -31,22 +31,39 @@ typedef enum { MRCP_VERSION_UNKNOWN = 0, /**< Unknown version */ MRCP_VERSION_1 = 1, /**< MRCPv1 (RFC4463) */ - MRCP_VERSION_2 = 2 /**< MRCPv2 (draft-ietf-speechsc-mrcpv2-15) */ + MRCP_VERSION_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; diff --git a/libs/unimrcp/libs/mrcp/message/include/mrcp_message.h b/libs/unimrcp/libs/mrcp/message/include/mrcp_message.h index 769ab08989..497a14d7c3 100644 --- a/libs/unimrcp/libs/mrcp/message/include/mrcp_message.h +++ b/libs/unimrcp/libs/mrcp/message/include/mrcp_message.h @@ -23,100 +23,22 @@ */ #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 */ @@ -172,34 +94,33 @@ MRCP_DECLARE(apt_bool_t) mrcp_message_header_inherit(mrcp_message_header_t *mess /** 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); diff --git a/libs/unimrcp/libs/mrcp/message/include/mrcp_start_line.h b/libs/unimrcp/libs/mrcp/message/include/mrcp_start_line.h new file mode 100644 index 0000000000..1c6db22d40 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/message/include/mrcp_start_line.h @@ -0,0 +1,114 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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__*/ diff --git a/libs/unimrcp/libs/mrcp/message/src/mrcp_generic_header.c b/libs/unimrcp/libs/mrcp/message/src/mrcp_generic_header.c index fe1b796dcd..3f2c57ab10 100644 --- a/libs/unimrcp/libs/mrcp/message/src/mrcp_generic_header.c +++ b/libs/unimrcp/libs/mrcp/message/src/mrcp_generic_header.c @@ -15,6 +15,7 @@ */ #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[] = { @@ -36,7 +37,6 @@ 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) { @@ -49,7 +49,7 @@ static apt_bool_t mrcp_request_id_list_parse(mrcp_request_id_list_t *request_id_ 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; @@ -60,7 +60,7 @@ static apt_bool_t mrcp_request_id_list_generate(mrcp_request_id_list_t *request_ { size_t i; for(i=0; icount; 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++ = ','; } diff --git a/libs/unimrcp/libs/mrcp/message/src/mrcp_header_accessor.c b/libs/unimrcp/libs/mrcp/message/src/mrcp_header_accessor.c index e7219006dc..5c293ec11c 100644 --- a/libs/unimrcp/libs/mrcp/message/src/mrcp_header_accessor.c +++ b/libs/unimrcp/libs/mrcp/message/src/mrcp_header_accessor.c @@ -79,6 +79,10 @@ MRCP_DECLARE(apt_bool_t) mrcp_header_generate(mrcp_header_accessor_t *accessor, 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) { @@ -90,6 +94,10 @@ MRCP_DECLARE(void) mrcp_header_property_add(mrcp_header_accessor_t *accessor, ap 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) { @@ -102,6 +110,10 @@ MRCP_DECLARE(void) mrcp_header_name_property_add(mrcp_header_accessor_t *accesso 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) { @@ -113,6 +125,10 @@ MRCP_DECLARE(void) mrcp_header_property_remove(mrcp_header_accessor_t *accessor, 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; @@ -161,12 +177,14 @@ MRCP_DECLARE(apt_bool_t) mrcp_header_inherit(mrcp_header_accessor_t *accessor, c for(i=0, j=0; ivtable->field_count && j < parent->counter; i++) { if((parent->properties[i] & MRCP_HEADER_FIELD_NAME) == MRCP_HEADER_FIELD_NAME) { j++; - if((parent->properties[i] & MRCP_HEADER_FIELD_VALUE) == MRCP_HEADER_FIELD_VALUE) { - accessor->vtable->duplicate_field(accessor,parent,i,pool); - mrcp_header_property_add(accessor,i); - } - else { - mrcp_header_name_property_add(accessor,i); + 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); + } } } } diff --git a/libs/unimrcp/libs/mrcp/message/src/mrcp_message.c b/libs/unimrcp/libs/mrcp/message/src/mrcp_message.c index dbe8627f5d..96b52784f5 100644 --- a/libs/unimrcp/libs/mrcp/message/src/mrcp_message.c +++ b/libs/unimrcp/libs/mrcp/message/src/mrcp_message.c @@ -16,426 +16,17 @@ #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 */ @@ -599,6 +190,96 @@ MRCP_DECLARE(apt_bool_t) mrcp_body_generate(mrcp_message_t *message, apt_text_st return TRUE; } +/** Initialize MRCP message */ +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; +} + +/** 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) @@ -608,34 +289,14 @@ MRCP_DECLARE(mrcp_message_t*) mrcp_message_create(apr_pool_t *pool) return message; } -/** Initialize MRCP message */ -MRCP_DECLARE(void) mrcp_message_init(mrcp_message_t *message, apr_pool_t *pool) -{ - mrcp_start_line_init(&message->start_line); - mrcp_channel_id_init(&message->channel_id); - mrcp_message_header_init(&message->header); - apt_string_reset(&message->body); - message->pool = pool; -} - -/** Initialize response/event message by request message */ -MRCP_DECLARE(void) mrcp_message_init_by_request(mrcp_message_t *message, const mrcp_message_t *request_message) -{ - message->channel_id = request_message->channel_id; - message->start_line.request_id = request_message->start_line.request_id; - message->start_line.version = request_message->start_line.version; - message->start_line.method_id = request_message->start_line.method_id; - message->header.generic_header_accessor.vtable = request_message->header.generic_header_accessor.vtable; - message->header.resource_header_accessor.vtable = request_message->header.resource_header_accessor.vtable; -} - /** Create MRCP request message */ -MRCP_DECLARE(mrcp_message_t*) mrcp_request_create(mrcp_resource_id resource_id, mrcp_method_id method_id, apr_pool_t *pool) +MRCP_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; } @@ -643,12 +304,16 @@ MRCP_DECLARE(mrcp_message_t*) mrcp_request_create(mrcp_resource_id resource_id, 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; } @@ -656,11 +321,14 @@ MRCP_DECLARE(mrcp_message_t*) mrcp_response_create(const mrcp_message_t *request 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; } diff --git a/libs/unimrcp/libs/mrcp/message/src/mrcp_start_line.c b/libs/unimrcp/libs/mrcp/message/src/mrcp_start_line.c new file mode 100644 index 0000000000..0e7e7a9fe8 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/message/src/mrcp_start_line.c @@ -0,0 +1,444 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "mrcp_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; +} diff --git a/libs/unimrcp/libs/mrcp/mrcp.vcproj b/libs/unimrcp/libs/mrcp/mrcp.vcproj index 514be9360b..9963bbc266 100644 --- a/libs/unimrcp/libs/mrcp/mrcp.vcproj +++ b/libs/unimrcp/libs/mrcp/mrcp.vcproj @@ -159,6 +159,10 @@ RelativePath=".\message\include\mrcp_message.h" > + + + + + + @@ -223,10 +235,6 @@ Name="include" Filter="h;hpp;hxx;hm;inl;inc;xsd" > - - @@ -236,7 +244,11 @@ > + + - - - - - - @@ -273,11 +273,11 @@ > - - diff --git a/libs/unimrcp/libs/mrcp/resources/include/mrcp_recog_header.h b/libs/unimrcp/libs/mrcp/resources/include/mrcp_recog_header.h index 1669147e50..4b2192b399 100644 --- a/libs/unimrcp/libs/mrcp/resources/include/mrcp_recog_header.h +++ b/libs/unimrcp/libs/mrcp/resources/include/mrcp_recog_header.h @@ -206,7 +206,7 @@ struct mrcp_recog_header_t { /** 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); diff --git a/libs/unimrcp/libs/mrcp/resources/include/mrcp_recorder_header.h b/libs/unimrcp/libs/mrcp/resources/include/mrcp_recorder_header.h new file mode 100644 index 0000000000..af1772337a --- /dev/null +++ b/libs/unimrcp/libs/mrcp/resources/include/mrcp_recorder_header.h @@ -0,0 +1,136 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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__*/ diff --git a/libs/unimrcp/libs/mrcp/resources/include/mrcp_recorder_resource.h b/libs/unimrcp/libs/mrcp/resources/include/mrcp_recorder_resource.h new file mode 100644 index 0000000000..b9afa34999 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/resources/include/mrcp_recorder_resource.h @@ -0,0 +1,53 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __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__*/ diff --git a/libs/unimrcp/libs/mrcp/resources/include/mrcp_synth_header.h b/libs/unimrcp/libs/mrcp/resources/include/mrcp_synth_header.h index f0bf60c9f6..359af6bd59 100644 --- a/libs/unimrcp/libs/mrcp/resources/include/mrcp_synth_header.h +++ b/libs/unimrcp/libs/mrcp/resources/include/mrcp_synth_header.h @@ -291,7 +291,7 @@ struct mrcp_synth_header_t { }; /** 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); diff --git a/libs/unimrcp/libs/mrcp/resources/src/mrcp_default_factory.c b/libs/unimrcp/libs/mrcp/resources/src/mrcp_default_factory.c deleted file mode 100644 index d34ba99c13..0000000000 --- a/libs/unimrcp/libs/mrcp/resources/src/mrcp_default_factory.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2008 Arsen Chaloyan - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mrcp_default_factory.h" -#include "mrcp_synth_resource.h" -#include "mrcp_recog_resource.h" -#include "apt_log.h" - -/** String table of MRCPv2 resources (mrcp_resource_types_e) */ -static const apt_str_table_item_t mrcp_resource_string_table[] = { - {{"speechsynth",11},6}, - {{"speechrecog",11},6} -}; - -/** Create default MRCP resource factory */ -MRCP_DECLARE(mrcp_resource_factory_t*) mrcp_default_factory_create(apr_pool_t *pool) -{ - mrcp_resource_t *resource; - mrcp_resource_factory_t *resource_factory; - /* create resource factory instance */ - apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create MRCP Resource Factory [%d]",MRCP_RESOURCE_TYPE_COUNT); - resource_factory = mrcp_resource_factory_create(MRCP_RESOURCE_TYPE_COUNT,pool); - if(!resource_factory) { - return NULL; - } - - /* set resource string table */ - mrcp_resource_string_table_set(resource_factory,mrcp_resource_string_table); - - /* create and register resources */ - resource = mrcp_synth_resource_create(pool); - if(resource) { - apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Register Synthesizer Resource"); - mrcp_resource_register(resource_factory,resource,MRCP_SYNTHESIZER_RESOURCE); - } - - resource = mrcp_recog_resource_create(pool); - if(resource) { - apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Register Recognizer Resource"); - mrcp_resource_register(resource_factory,resource,MRCP_RECOGNIZER_RESOURCE); - } - - return resource_factory; -} diff --git a/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_client_state_machine.c b/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_client_state_machine.c deleted file mode 100644 index 358f5ee64e..0000000000 --- a/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_client_state_machine.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2008 Arsen Chaloyan - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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; -} diff --git a/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_header.c b/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_header.c index 1d1a997014..4a4076faf3 100644 --- a/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_header.c +++ b/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_header.c @@ -638,7 +638,7 @@ static const mrcp_header_vtable_t v2_vtable = { 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; diff --git a/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_resource.c b/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_resource.c index 819b8b09a6..bf9dc56f3a 100644 --- a/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_resource.c +++ b/libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_resource.c @@ -16,9 +16,7 @@ #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[] = { @@ -71,73 +69,15 @@ static APR_INLINE const apt_str_table_item_t* recog_event_string_table_get(mrcp_ 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); + mrcp_resource_t *resource = mrcp_resource_create(pool); - resource->resourcify_message_by_id = recog_message_resourcify_by_id; - resource->resourcify_message_by_name = recog_message_resourcify_by_name; - - resource->create_client_state_machine = mrcp_recog_client_state_machine_create; - resource->create_server_state_machine = mrcp_recog_server_state_machine_create; + 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; } diff --git a/libs/unimrcp/libs/mrcp/resources/src/mrcp_recorder_header.c b/libs/unimrcp/libs/mrcp/resources/src/mrcp_recorder_header.c new file mode 100644 index 0000000000..1b51da81eb --- /dev/null +++ b/libs/unimrcp/libs/mrcp/resources/src/mrcp_recorder_header.c @@ -0,0 +1,299 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "mrcp_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); +} diff --git a/libs/unimrcp/libs/mrcp/resources/src/mrcp_recorder_resource.c b/libs/unimrcp/libs/mrcp/resources/src/mrcp_recorder_resource.c new file mode 100644 index 0000000000..edb047c5a9 --- /dev/null +++ b/libs/unimrcp/libs/mrcp/resources/src/mrcp_recorder_resource.c @@ -0,0 +1,57 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mrcp_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; +} diff --git a/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_client_state_machine.c b/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_client_state_machine.c deleted file mode 100644 index b34378d176..0000000000 --- a/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_client_state_machine.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2008 Arsen Chaloyan - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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; -} diff --git a/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_header.c b/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_header.c index 0a84904da3..3a8c804d4d 100644 --- a/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_header.c +++ b/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_header.c @@ -548,7 +548,7 @@ static const mrcp_header_vtable_t vtable = { 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; } diff --git a/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_resource.c b/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_resource.c index 9018f49002..702af0a6e5 100644 --- a/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_resource.c +++ b/libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_resource.c @@ -16,9 +16,7 @@ #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[] = { @@ -39,75 +37,25 @@ static const apt_str_table_item_t synth_event_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); + mrcp_resource_t *resource = mrcp_resource_create(pool); - resource->resourcify_message_by_id = synth_message_resourcify_by_id; - resource->resourcify_message_by_name = synth_message_resourcify_by_name; - - resource->create_client_state_machine = mrcp_synth_client_state_machine_create; - resource->create_server_state_machine = mrcp_synth_server_state_machine_create; + 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; } diff --git a/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_connection.h b/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_connection.h index bcef530f87..82d44ea865 100644 --- a/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_connection.h +++ b/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_connection.h @@ -77,7 +77,7 @@ struct mrcp_connection_t { }; /** 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); @@ -91,6 +91,8 @@ mrcp_control_channel_t* mrcp_connection_channel_find(mrcp_connection_t *connecti /** 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 diff --git a/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_connection_types.h b/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_connection_types.h index 5374451216..c7a02c0419 100644 --- a/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_connection_types.h +++ b/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_connection_types.h @@ -53,6 +53,8 @@ struct mrcp_connection_event_vtable_t { 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 */ diff --git a/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_control_descriptor.h b/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_control_descriptor.h index a9323ae759..b5dbc44c5f 100644 --- a/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_control_descriptor.h +++ b/libs/unimrcp/libs/mrcpv2-transport/include/mrcp_control_descriptor.h @@ -22,11 +22,16 @@ * @brief MRCPv2 Control Descriptor */ +#include #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, @@ -85,25 +90,15 @@ struct mrcp_control_descriptor_t { 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); @@ -111,6 +106,12 @@ MRCP_DECLARE(mrcp_control_descriptor_t*) mrcp_control_offer_create(apr_pool_t *p /** 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); diff --git a/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_client_connection.c b/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_client_connection.c index 89f636eefa..a94765f042 100644 --- a/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_client_connection.c +++ b/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_client_connection.c @@ -96,6 +96,7 @@ MRCP_DECLARE(mrcp_connection_agent_t*) mrcp_client_connection_agent_create( 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); @@ -310,7 +311,8 @@ static mrcp_connection_t* mrcp_client_agent_connection_find(mrcp_connection_agen 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; } } @@ -514,7 +516,7 @@ static apt_bool_t mrcp_client_agent_messsage_receive(mrcp_connection_agent_t *ag 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 */ @@ -588,6 +590,9 @@ static apt_bool_t mrcp_client_agent_task_run(apt_task_t *task) 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) { diff --git a/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_connection.c b/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_connection.c index 079671a491..d174cb78d0 100644 --- a/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_connection.c +++ b/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_connection.c @@ -78,3 +78,21 @@ apt_bool_t mrcp_connection_channel_remove(mrcp_connection_t *connection, mrcp_co 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; +} diff --git a/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_control_descriptor.c b/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_control_descriptor.c index 2e9a0e43c2..de0de51aa9 100644 --- a/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_control_descriptor.c +++ b/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_control_descriptor.c @@ -85,14 +85,30 @@ MRCP_DECLARE(mrcp_connection_type_e) mrcp_connection_type_find(const apt_str_t * 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; @@ -101,12 +117,29 @@ MRCP_DECLARE(mrcp_control_descriptor_t*) mrcp_control_offer_create(apr_pool_t *p /** 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; inelts; i++) { + if(APR_ARRAY_IDX(cmid_arr,i,apr_size_t) == cmid) { + return TRUE; + } + } + return FALSE; +} diff --git a/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_server_connection.c b/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_server_connection.c index 1655697c89..ba500a20bd 100644 --- a/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_server_connection.c +++ b/libs/unimrcp/libs/mrcpv2-transport/src/mrcp_server_connection.c @@ -114,6 +114,7 @@ MRCP_DECLARE(mrcp_connection_agent_t*) mrcp_server_connection_agent_create( 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); @@ -610,12 +611,14 @@ static apt_bool_t mrcp_server_message_handler(void *obj, mrcp_message_t *message } 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; @@ -713,6 +716,9 @@ static apt_bool_t mrcp_server_agent_task_run(apt_task_t *task) 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) { diff --git a/libs/unimrcp/libs/uni-rtsp/include/rtsp_header.h b/libs/unimrcp/libs/uni-rtsp/include/rtsp_header.h index 68b5ec2bbe..2a71c6ed2d 100644 --- a/libs/unimrcp/libs/uni-rtsp/include/rtsp_header.h +++ b/libs/unimrcp/libs/uni-rtsp/include/rtsp_header.h @@ -61,6 +61,7 @@ typedef enum{ 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 @@ -135,6 +136,8 @@ struct rtsp_transport_t { 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 */ @@ -181,6 +184,7 @@ static APR_INLINE void rtsp_transport_init(rtsp_transport_t *transport) 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 */ diff --git a/libs/unimrcp/libs/uni-rtsp/src/rtsp_client.c b/libs/unimrcp/libs/uni-rtsp/src/rtsp_client.c index 825e1f359d..8b78005ff6 100644 --- a/libs/unimrcp/libs/uni-rtsp/src/rtsp_client.c +++ b/libs/unimrcp/libs/uni-rtsp/src/rtsp_client.c @@ -582,10 +582,12 @@ static apt_bool_t rtsp_client_session_response_process(rtsp_client_t *client, rt 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); + } } } diff --git a/libs/unimrcp/libs/uni-rtsp/src/rtsp_header.c b/libs/unimrcp/libs/uni-rtsp/src/rtsp_header.c index 9d6e12c532..b76f2ec383 100644 --- a/libs/unimrcp/libs/uni-rtsp/src/rtsp_header.c +++ b/libs/unimrcp/libs/uni-rtsp/src/rtsp_header.c @@ -53,11 +53,12 @@ static const apt_str_table_item_t rtsp_profile_string_table[] = { /** 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 */ @@ -96,15 +97,15 @@ static apt_bool_t rtsp_port_range_generate(rtsp_transport_attrib_e attrib, const 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; } @@ -132,10 +133,10 @@ static apt_bool_t rtsp_transport_attrib_parse(rtsp_transport_t *transport, const 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; @@ -143,6 +144,9 @@ static apt_bool_t rtsp_transport_attrib_parse(rtsp_transport_t *transport, const 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; } @@ -180,7 +184,7 @@ static apt_bool_t rtsp_transport_protocol_parse(rtsp_transport_t *transport, con 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); @@ -256,6 +260,17 @@ static apt_bool_t rtsp_transport_generate(rtsp_transport_t *transport, apt_text_ 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; } diff --git a/libs/unimrcp/libs/uni-rtsp/src/rtsp_message.c b/libs/unimrcp/libs/uni-rtsp/src/rtsp_message.c index 2b44e700a7..2bb2ee60d0 100644 --- a/libs/unimrcp/libs/uni-rtsp/src/rtsp_message.c +++ b/libs/unimrcp/libs/uni-rtsp/src/rtsp_message.c @@ -46,13 +46,13 @@ RTSP_DECLARE(rtsp_message_t*) rtsp_response_create(const rtsp_message_t *request { 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) { @@ -60,6 +60,15 @@ RTSP_DECLARE(rtsp_message_t*) rtsp_response_create(const rtsp_message_t *request 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; } diff --git a/libs/unimrcp/libs/uni-rtsp/src/rtsp_server.c b/libs/unimrcp/libs/uni-rtsp/src/rtsp_server.c index d568e9741c..ad7d251c40 100644 --- a/libs/unimrcp/libs/uni-rtsp/src/rtsp_server.c +++ b/libs/unimrcp/libs/uni-rtsp/src/rtsp_server.c @@ -70,7 +70,6 @@ struct rtsp_server_session_t { /** Session identifier */ apt_str_t id; - apt_str_t url; /** Last cseq sent */ apr_size_t last_cseq; @@ -80,6 +79,9 @@ struct rtsp_server_session_t { /** request queue */ apt_obj_list_t *request_queue; + /** Resource table */ + apr_hash_t *resource_table; + /** In-progress termination request */ apt_bool_t terminating; }; @@ -261,9 +263,9 @@ static rtsp_server_session_t* rtsp_server_session_create(rtsp_server_t *server) 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) { @@ -335,8 +337,14 @@ static apt_bool_t rtsp_server_session_terminate_request(rtsp_server_t *server, r 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) { @@ -349,7 +357,7 @@ static apt_bool_t rtsp_server_session_message_handle(rtsp_server_t *server, rtsp 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; @@ -357,7 +365,6 @@ static rtsp_server_session_t* rtsp_server_session_setup_process(rtsp_server_t *s /* 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); } @@ -405,13 +412,21 @@ static apt_bool_t rtsp_server_session_request_process(rtsp_server_t *server, rts 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); @@ -427,34 +442,62 @@ static apt_bool_t rtsp_server_session_request_process(rtsp_server_t *server, rts /* 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; } + if(!session->active_request) { + /* unexpected response */ + return FALSE; + } + + 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) { + /* 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; + } + } + } + session->last_cseq = message->header.cseq; rtsp_server_message_send(server,session->connection->base,message); - if(session->active_request) { - rtsp_message_t *request = session->active_request; - if(request->start_line.common.request_line.method_id == RTSP_METHOD_SETUP) { - if(message->start_line.common.status_line.status_code != RTSP_STATUS_CODE_OK) { - rtsp_server_session_terminate_request(server,session); - } - } - else if(request->start_line.common.request_line.method_id == RTSP_METHOD_DESCRIBE) { - rtsp_server_session_terminate_request(server,session); - } + 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); diff --git a/libs/unimrcp/modules/mrcp-sofiasip/src/mrcp_sdp.c b/libs/unimrcp/modules/mrcp-sofiasip/src/mrcp_sdp.c index d5e36525fe..56244af8c8 100644 --- a/libs/unimrcp/modules/mrcp-sofiasip/src/mrcp_sdp.c +++ b/libs/unimrcp/modules/mrcp-sofiasip/src/mrcp_sdp.c @@ -56,14 +56,14 @@ MRCP_DECLARE(apr_size_t) sdp_string_generate_by_mrcp_descriptor(char *buffer, ap count = mrcp_session_media_count_get(descriptor); for(i=0; ibase.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); @@ -106,7 +106,7 @@ MRCP_DECLARE(mrcp_session_descriptor_t*) mrcp_descriptor_generate_by_sdp_session { 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; } @@ -114,14 +114,13 @@ MRCP_DECLARE(mrcp_session_descriptor_t*) mrcp_descriptor_generate_by_sdp_session { 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; @@ -147,31 +146,38 @@ static apr_size_t sdp_rtp_media_generate(char *buffer, apr_size_t size, const mr } 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; inelts; 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; inelts; 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); @@ -184,6 +190,7 @@ static apr_size_t sdp_rtp_media_generate(char *buffer, apr_size_t size, const mr /** 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; @@ -197,24 +204,21 @@ static apr_size_t sdp_control_media_generate(char *buffer, apr_size_t size, cons "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 */ @@ -223,29 +227,32 @@ static apr_size_t sdp_control_media_generate(char *buffer, apr_size_t size, cons "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; icmid_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; } @@ -285,31 +292,31 @@ static apt_bool_t mpf_rtp_media_generate(mpf_rtp_media_descriptor_t *rtp_media, 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; } @@ -348,7 +355,7 @@ static apt_bool_t mrcp_control_media_generate(mrcp_control_descriptor_t *control 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; @@ -385,9 +392,11 @@ MRCP_DECLARE(apr_size_t) sdp_resource_discovery_string_generate(const char *ip, "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); diff --git a/libs/unimrcp/modules/mrcp-sofiasip/src/mrcp_sofiasip_client_agent.c b/libs/unimrcp/modules/mrcp-sofiasip/src/mrcp_sofiasip_client_agent.c index d89aa9be3d..aa7a5fb753 100644 --- a/libs/unimrcp/modules/mrcp-sofiasip/src/mrcp_sofiasip_client_agent.c +++ b/libs/unimrcp/modules/mrcp-sofiasip/src/mrcp_sofiasip_client_agent.c @@ -521,8 +521,11 @@ static void mrcp_sofia_event_callback( 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; diff --git a/libs/unimrcp/modules/mrcp-sofiasip/src/mrcp_sofiasip_server_agent.c b/libs/unimrcp/modules/mrcp-sofiasip/src/mrcp_sofiasip_server_agent.c index cb8e112a4a..c5f81a22b5 100644 --- a/libs/unimrcp/modules/mrcp-sofiasip/src/mrcp_sofiasip_server_agent.c +++ b/libs/unimrcp/modules/mrcp-sofiasip/src/mrcp_sofiasip_server_agent.c @@ -276,7 +276,8 @@ static apt_bool_t mrcp_sofia_on_session_answer(mrcp_session_t *session, mrcp_ses 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; @@ -403,6 +404,7 @@ static void mrcp_sofia_on_resource_discover(mrcp_sofia_agent_t *sofia_agent, 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()); } @@ -427,10 +429,13 @@ static void mrcp_sofia_event_callback( nua_event_t nua_event, 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; } } diff --git a/libs/unimrcp/modules/mrcp-unirtsp/include/mrcp_unirtsp_sdp.h b/libs/unimrcp/modules/mrcp-unirtsp/include/mrcp_unirtsp_sdp.h index 59fbf64e86..58d35347d0 100644 --- a/libs/unimrcp/modules/mrcp-unirtsp/include/mrcp_unirtsp_sdp.h +++ b/libs/unimrcp/modules/mrcp-unirtsp/include/mrcp_unirtsp_sdp.h @@ -61,7 +61,7 @@ MRCP_DECLARE(rtsp_message_t*) rtsp_resource_discovery_request_generate( 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, diff --git a/libs/unimrcp/modules/mrcp-unirtsp/src/mrcp_unirtsp_sdp.c b/libs/unimrcp/modules/mrcp-unirtsp/src/mrcp_unirtsp_sdp.c index f1ea89185a..e35055fdc9 100644 --- a/libs/unimrcp/modules/mrcp-unirtsp/src/mrcp_unirtsp_sdp.c +++ b/libs/unimrcp/modules/mrcp-unirtsp/src/mrcp_unirtsp_sdp.c @@ -36,26 +36,33 @@ static apr_size_t sdp_rtp_media_generate(char *buffer, apr_size_t size, const mr } 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; inelts; 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; inelts; 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", @@ -98,31 +105,31 @@ static apt_bool_t mpf_rtp_media_generate(mpf_rtp_media_descriptor_t *rtp_media, 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; } @@ -145,7 +152,7 @@ static mrcp_session_descriptor_t* mrcp_descriptor_generate_by_sdp_session(mrcp_s { 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; } @@ -153,7 +160,7 @@ static mrcp_session_descriptor_t* mrcp_descriptor_generate_by_sdp_session(mrcp_s { 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; } @@ -207,11 +214,11 @@ MRCP_DECLARE(mrcp_session_descriptor_t*) mrcp_descriptor_generate_by_rtsp_reques 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; } } @@ -321,16 +328,16 @@ MRCP_DECLARE(rtsp_message_t*) rtsp_request_generate_by_mrcp_descriptor(const mrc count = mrcp_session_media_count_get(descriptor); for(i=0; ibase.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); @@ -403,17 +410,19 @@ MRCP_DECLARE(rtsp_message_t*) rtsp_response_generate_by_mrcp_descriptor(const rt count = mrcp_session_media_count_get(descriptor); for(i=0; ibase.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); @@ -453,7 +462,7 @@ MRCP_DECLARE(rtsp_message_t*) rtsp_resource_discovery_request_generate( 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, @@ -525,18 +534,15 @@ MRCP_DECLARE(rtsp_message_t*) rtsp_resource_discovery_response_generate( "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; diff --git a/libs/unimrcp/modules/mrcp-unirtsp/src/mrcp_unirtsp_server_agent.c b/libs/unimrcp/modules/mrcp-unirtsp/src/mrcp_unirtsp_server_agent.c index 12de96a741..a29a68c127 100644 --- a/libs/unimrcp/modules/mrcp-unirtsp/src/mrcp_unirtsp_server_agent.c +++ b/libs/unimrcp/modules/mrcp-unirtsp/src/mrcp_unirtsp_server_agent.c @@ -355,6 +355,9 @@ static apt_bool_t mrcp_unirtsp_on_session_control(mrcp_session_t *mrcp_session, 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; } diff --git a/libs/unimrcp/packages/inno-setup/setup.iss b/libs/unimrcp/packages/inno-setup/setup.iss index a14c5001d3..98677b46c9 100644 --- a/libs/unimrcp/packages/inno-setup/setup.iss +++ b/libs/unimrcp/packages/inno-setup/setup.iss @@ -1,4 +1,4 @@ -#define uni_version "0.6.0" +#define uni_version "0.8.0" AppName=UniMRCP AppVerName=UniMRCP-{#= uni_version} diff --git a/libs/unimrcp/platforms/Makefile.am b/libs/unimrcp/platforms/Makefile.am index d6ce93d131..7d0fb6dfd5 100644 --- a/libs/unimrcp/platforms/Makefile.am +++ b/libs/unimrcp/platforms/Makefile.am @@ -1,4 +1,6 @@ MAINTAINERCLEANFILES = Makefile.in SUBDIRS = libunimrcp-server unimrcp-server \ - libunimrcp-client unimrcp-client + libunimrcp-client unimrcp-client \ + libasr-client asr-client \ + umc diff --git a/libs/unimrcp/platforms/asr-client/Makefile.am b/libs/unimrcp/platforms/asr-client/Makefile.am new file mode 100644 index 0000000000..02f6a0a6a9 --- /dev/null +++ b/libs/unimrcp/platforms/asr-client/Makefile.am @@ -0,0 +1,24 @@ +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 diff --git a/libs/unimrcp/platforms/asr-client/asrclient.vcproj b/libs/unimrcp/platforms/asr-client/asrclient.vcproj new file mode 100644 index 0000000000..cee6a32f22 --- /dev/null +++ b/libs/unimrcp/platforms/asr-client/asrclient.vcproj @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/platforms/asr-client/src/main.c b/libs/unimrcp/platforms/asr-client/src/main.c new file mode 100644 index 0000000000..0861bf4200 --- /dev/null +++ b/libs/unimrcp/platforms/asr-client/src/main.c @@ -0,0 +1,298 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#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; +} diff --git a/libs/unimrcp/platforms/libasr-client/Makefile.am b/libs/unimrcp/platforms/libasr-client/Makefile.am new file mode 100644 index 0000000000..468846b3c1 --- /dev/null +++ b/libs/unimrcp/platforms/libasr-client/Makefile.am @@ -0,0 +1,24 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -Iinclude \ + -I$(top_srcdir)/platforms/libunimrcp-client/include \ + -I$(top_srcdir)/libs/mrcp-client/include \ + -I$(top_srcdir)/libs/mrcp-signaling/include \ + -I$(top_srcdir)/libs/mrcpv2-transport/include \ + -I$(top_srcdir)/libs/mrcp/include \ + -I$(top_srcdir)/libs/mrcp/message/include \ + -I$(top_srcdir)/libs/mrcp/control/include \ + -I$(top_srcdir)/libs/mrcp/resources/include \ + -I$(top_srcdir)/libs/mpf/include \ + -I$(top_srcdir)/libs/apr-toolkit/include \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) + +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) diff --git a/libs/unimrcp/platforms/libasr-client/include/asr_engine.h b/libs/unimrcp/platforms/libasr-client/include/asr_engine.h new file mode 100644 index 0000000000..6a9e62fbe5 --- /dev/null +++ b/libs/unimrcp/platforms/libasr-client/include/asr_engine.h @@ -0,0 +1,102 @@ +/* + * 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__*/ diff --git a/libs/unimrcp/platforms/libasr-client/libasrclient.vcproj b/libs/unimrcp/platforms/libasr-client/libasrclient.vcproj new file mode 100644 index 0000000000..98fee3b0e6 --- /dev/null +++ b/libs/unimrcp/platforms/libasr-client/libasrclient.vcproj @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/platforms/libasr-client/src/asr_engine.c b/libs/unimrcp/platforms/libasr-client/src/asr_engine.c new file mode 100644 index 0000000000..491faaf310 --- /dev/null +++ b/libs/unimrcp/platforms/libasr-client/src/asr_engine.c @@ -0,0 +1,586 @@ +/* + * 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 + +/* APR includes */ +#include +#include + +/* 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); +} diff --git a/libs/unimrcp/platforms/libunimrcp-client/Makefile.am b/libs/unimrcp/platforms/libunimrcp-client/Makefile.am index a21f667de3..f1cbd3fe57 100644 --- a/libs/unimrcp/platforms/libunimrcp-client/Makefile.am +++ b/libs/unimrcp/platforms/libunimrcp-client/Makefile.am @@ -29,6 +29,6 @@ libunimrcpclient_la_LIBADD = $(top_builddir)/modules/mrcp-sofiasip/libmrcpsofia $(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) diff --git a/libs/unimrcp/platforms/libunimrcp-client/src/unimrcp_client.c b/libs/unimrcp/platforms/libunimrcp-client/src/unimrcp_client.c index e3f945a5dd..d159582886 100644 --- a/libs/unimrcp/platforms/libunimrcp-client/src/unimrcp_client.c +++ b/libs/unimrcp/platforms/libunimrcp-client/src/unimrcp_client.c @@ -18,7 +18,7 @@ #include #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" @@ -52,8 +52,6 @@ MRCP_DECLARE(mrcp_client_t*) unimrcp_client_create(apt_dir_layout_t *dir_layout) { apr_pool_t *pool; apr_xml_doc *doc; - mrcp_resource_factory_t *resource_factory; - mpf_codec_manager_t *codec_manager; mrcp_client_t *client; if(!dir_layout) { @@ -71,16 +69,6 @@ MRCP_DECLARE(mrcp_client_t*) unimrcp_client_create(apt_dir_layout_t *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); @@ -118,7 +106,7 @@ static apr_xml_doc* unimrcp_client_config_parse(const char *dir_path, apr_pool_t 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); @@ -419,6 +407,18 @@ static mpf_termination_factory_t* unimrcp_client_rtp_factory_load(mrcp_client_t 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); } @@ -436,22 +436,33 @@ static mpf_termination_factory_t* unimrcp_client_rtp_factory_load(mrcp_client_t 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); } @@ -480,6 +491,57 @@ static apt_bool_t unimrcp_client_media_engines_load(mrcp_client_t *client, const 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) { @@ -582,7 +644,10 @@ static apt_bool_t unimrcp_client_config_load(mrcp_client_t *client, const apr_xm 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) { diff --git a/libs/unimrcp/platforms/libunimrcp-server/Makefile.am b/libs/unimrcp/platforms/libunimrcp-server/Makefile.am index a67510ee49..b20402b015 100644 --- a/libs/unimrcp/platforms/libunimrcp-server/Makefile.am +++ b/libs/unimrcp/platforms/libunimrcp-server/Makefile.am @@ -31,6 +31,6 @@ libunimrcpserver_la_LIBADD = $(top_builddir)/modules/mrcp-sofiasip/libmrcpsofia $(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) diff --git a/libs/unimrcp/platforms/libunimrcp-server/src/unimrcp_server.c b/libs/unimrcp/platforms/libunimrcp-server/src/unimrcp_server.c index 46d3ef4059..bf51ef16b0 100644 --- a/libs/unimrcp/platforms/libunimrcp-server/src/unimrcp_server.c +++ b/libs/unimrcp/platforms/libunimrcp-server/src/unimrcp_server.c @@ -18,7 +18,7 @@ #include #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" @@ -57,8 +57,6 @@ MRCP_DECLARE(mrcp_server_t*) unimrcp_server_start(apt_dir_layout_t *dir_layout) { apr_pool_t *pool; apr_xml_doc *doc; - mrcp_resource_factory_t *resource_factory; - mpf_codec_manager_t *codec_manager; mrcp_server_t *server; if(!dir_layout) { @@ -76,16 +74,6 @@ MRCP_DECLARE(mrcp_server_t*) unimrcp_server_start(apt_dir_layout_t *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); @@ -134,7 +122,7 @@ static apr_xml_doc* unimrcp_server_config_parse(const char *dir_path, apr_pool_t 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); @@ -452,6 +440,18 @@ static mpf_termination_factory_t* unimrcp_server_rtp_factory_load(mrcp_server_t 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); } @@ -469,22 +469,33 @@ static mpf_termination_factory_t* unimrcp_server_rtp_factory_load(mrcp_server_t 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); } @@ -513,18 +524,70 @@ static apt_bool_t unimrcp_server_media_engines_load(mrcp_server_t *server, const 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; @@ -535,6 +598,9 @@ static apt_bool_t unimrcp_server_plugin_load(mrcp_server_t *server, const char * 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); } @@ -557,14 +623,31 @@ static apt_bool_t unimrcp_server_plugin_load(mrcp_server_t *server, const char * 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); @@ -687,7 +770,10 @@ static apt_bool_t unimrcp_server_config_load(mrcp_server_t *server, const char * 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) { diff --git a/libs/unimrcp/platforms/umc/Makefile.am b/libs/unimrcp/platforms/umc/Makefile.am new file mode 100644 index 0000000000..4071490368 --- /dev/null +++ b/libs/unimrcp/platforms/umc/Makefile.am @@ -0,0 +1,34 @@ +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 diff --git a/libs/unimrcp/platforms/umc/include/dtmfscenario.h b/libs/unimrcp/platforms/umc/include/dtmfscenario.h new file mode 100644 index 0000000000..63aab623c4 --- /dev/null +++ b/libs/unimrcp/platforms/umc/include/dtmfscenario.h @@ -0,0 +1,75 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __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__*/ diff --git a/libs/unimrcp/platforms/umc/include/dtmfsession.h b/libs/unimrcp/platforms/umc/include/dtmfsession.h new file mode 100644 index 0000000000..6dcc70e937 --- /dev/null +++ b/libs/unimrcp/platforms/umc/include/dtmfsession.h @@ -0,0 +1,69 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __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__*/ diff --git a/libs/unimrcp/platforms/umc/include/recogscenario.h b/libs/unimrcp/platforms/umc/include/recogscenario.h new file mode 100644 index 0000000000..1be4d739a9 --- /dev/null +++ b/libs/unimrcp/platforms/umc/include/recogscenario.h @@ -0,0 +1,88 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __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__*/ diff --git a/libs/unimrcp/platforms/umc/include/recogsession.h b/libs/unimrcp/platforms/umc/include/recogsession.h new file mode 100644 index 0000000000..a31b8644fc --- /dev/null +++ b/libs/unimrcp/platforms/umc/include/recogsession.h @@ -0,0 +1,73 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __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__*/ diff --git a/libs/unimrcp/platforms/umc/include/recorderscenario.h b/libs/unimrcp/platforms/umc/include/recorderscenario.h new file mode 100644 index 0000000000..9648fd3f2b --- /dev/null +++ b/libs/unimrcp/platforms/umc/include/recorderscenario.h @@ -0,0 +1,66 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __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__*/ diff --git a/libs/unimrcp/platforms/umc/include/recordersession.h b/libs/unimrcp/platforms/umc/include/recordersession.h new file mode 100644 index 0000000000..338592ae5b --- /dev/null +++ b/libs/unimrcp/platforms/umc/include/recordersession.h @@ -0,0 +1,69 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __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__*/ diff --git a/libs/unimrcp/platforms/umc/include/synthscenario.h b/libs/unimrcp/platforms/umc/include/synthscenario.h new file mode 100644 index 0000000000..fd5aa9557e --- /dev/null +++ b/libs/unimrcp/platforms/umc/include/synthscenario.h @@ -0,0 +1,74 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#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__*/ diff --git a/libs/unimrcp/platforms/umc/include/synthsession.h b/libs/unimrcp/platforms/umc/include/synthsession.h new file mode 100644 index 0000000000..3b45d50454 --- /dev/null +++ b/libs/unimrcp/platforms/umc/include/synthsession.h @@ -0,0 +1,67 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __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__*/ diff --git a/libs/unimrcp/platforms/umc/include/umcconsole.h b/libs/unimrcp/platforms/umc/include/umcconsole.h new file mode 100644 index 0000000000..c8bd7c6d64 --- /dev/null +++ b/libs/unimrcp/platforms/umc/include/umcconsole.h @@ -0,0 +1,58 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __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__*/ diff --git a/libs/unimrcp/platforms/umc/include/umcframework.h b/libs/unimrcp/platforms/umc/include/umcframework.h new file mode 100644 index 0000000000..36f8082846 --- /dev/null +++ b/libs/unimrcp/platforms/umc/include/umcframework.h @@ -0,0 +1,95 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include +#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__*/ diff --git a/libs/unimrcp/platforms/umc/include/umcscenario.h b/libs/unimrcp/platforms/umc/include/umcscenario.h new file mode 100644 index 0000000000..712eb666cc --- /dev/null +++ b/libs/unimrcp/platforms/umc/include/umcscenario.h @@ -0,0 +1,120 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#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__*/ diff --git a/libs/unimrcp/platforms/umc/include/umcsession.h b/libs/unimrcp/platforms/umc/include/umcsession.h new file mode 100644 index 0000000000..2c2f729aec --- /dev/null +++ b/libs/unimrcp/platforms/umc/include/umcsession.h @@ -0,0 +1,121 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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__*/ diff --git a/libs/unimrcp/platforms/umc/src/dtmfscenario.cpp b/libs/unimrcp/platforms/umc/src/dtmfscenario.cpp new file mode 100644 index 0000000000..ca644b382c --- /dev/null +++ b/libs/unimrcp/platforms/umc/src/dtmfscenario.cpp @@ -0,0 +1,77 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "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); +} diff --git a/libs/unimrcp/platforms/umc/src/dtmfsession.cpp b/libs/unimrcp/platforms/umc/src/dtmfsession.cpp new file mode 100644 index 0000000000..38c85cf5ae --- /dev/null +++ b/libs/unimrcp/platforms/umc/src/dtmfsession.cpp @@ -0,0 +1,326 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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; +} diff --git a/libs/unimrcp/platforms/umc/src/main.cpp b/libs/unimrcp/platforms/umc/src/main.cpp new file mode 100644 index 0000000000..089dee3cd5 --- /dev/null +++ b/libs/unimrcp/platforms/umc/src/main.cpp @@ -0,0 +1,24 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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; +} diff --git a/libs/unimrcp/platforms/umc/src/recogscenario.cpp b/libs/unimrcp/platforms/umc/src/recogscenario.cpp new file mode 100644 index 0000000000..c7cfbcbc26 --- /dev/null +++ b/libs/unimrcp/platforms/umc/src/recogscenario.cpp @@ -0,0 +1,113 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#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); +} diff --git a/libs/unimrcp/platforms/umc/src/recogsession.cpp b/libs/unimrcp/platforms/umc/src/recogsession.cpp new file mode 100644 index 0000000000..b519429a7a --- /dev/null +++ b/libs/unimrcp/platforms/umc/src/recogsession.cpp @@ -0,0 +1,450 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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; +} diff --git a/libs/unimrcp/platforms/umc/src/recorderscenario.cpp b/libs/unimrcp/platforms/umc/src/recorderscenario.cpp new file mode 100644 index 0000000000..398fc57c56 --- /dev/null +++ b/libs/unimrcp/platforms/umc/src/recorderscenario.cpp @@ -0,0 +1,70 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "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); +} diff --git a/libs/unimrcp/platforms/umc/src/recordersession.cpp b/libs/unimrcp/platforms/umc/src/recordersession.cpp new file mode 100644 index 0000000000..9e0030c22a --- /dev/null +++ b/libs/unimrcp/platforms/umc/src/recordersession.cpp @@ -0,0 +1,298 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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; +} diff --git a/libs/unimrcp/platforms/umc/src/synthscenario.cpp b/libs/unimrcp/platforms/umc/src/synthscenario.cpp new file mode 100644 index 0000000000..37c9d66f30 --- /dev/null +++ b/libs/unimrcp/platforms/umc/src/synthscenario.cpp @@ -0,0 +1,80 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#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); +} diff --git a/libs/unimrcp/platforms/umc/src/synthsession.cpp b/libs/unimrcp/platforms/umc/src/synthsession.cpp new file mode 100644 index 0000000000..c5a2fad124 --- /dev/null +++ b/libs/unimrcp/platforms/umc/src/synthsession.cpp @@ -0,0 +1,261 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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"); +} diff --git a/libs/unimrcp/platforms/umc/src/umcconsole.cpp b/libs/unimrcp/platforms/umc/src/umcconsole.cpp new file mode 100644 index 0000000000..0881e40beb --- /dev/null +++ b/libs/unimrcp/platforms/umc/src/umcconsole.cpp @@ -0,0 +1,280 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "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; +} diff --git a/libs/unimrcp/platforms/umc/src/umcframework.cpp b/libs/unimrcp/platforms/umc/src/umcframework.cpp new file mode 100644 index 0000000000..aa9b9ed820 --- /dev/null +++ b/libs/unimrcp/platforms/umc/src/umcframework.cpp @@ -0,0 +1,605 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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; +} diff --git a/libs/unimrcp/platforms/umc/src/umcscenario.cpp b/libs/unimrcp/platforms/umc/src/umcscenario.cpp new file mode 100644 index 0000000000..e93c53a1b6 --- /dev/null +++ b/libs/unimrcp/platforms/umc/src/umcscenario.cpp @@ -0,0 +1,213 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#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; iattrib_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); +} diff --git a/libs/unimrcp/platforms/umc/src/umcsession.cpp b/libs/unimrcp/platforms/umc/src/umcsession.cpp new file mode 100644 index 0000000000..d19be46466 --- /dev/null +++ b/libs/unimrcp/platforms/umc/src/umcsession.cpp @@ -0,0 +1,226 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "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; +} diff --git a/libs/unimrcp/platforms/umc/umc.vcproj b/libs/unimrcp/platforms/umc/umc.vcproj new file mode 100644 index 0000000000..6c6bbac567 --- /dev/null +++ b/libs/unimrcp/platforms/umc/umc.vcproj @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/platforms/unimrcp-client/src/demo_bypass_application.c b/libs/unimrcp/platforms/unimrcp-client/src/demo_bypass_application.c index 639b411c12..6a9b660bce 100644 --- a/libs/unimrcp/platforms/unimrcp-client/src/demo_bypass_application.c +++ b/libs/unimrcp/platforms/unimrcp-client/src/demo_bypass_application.c @@ -31,7 +31,6 @@ #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" @@ -80,9 +79,10 @@ demo_application_t* demo_bypass_application_create(apr_pool_t *pool) 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 */ @@ -163,10 +163,10 @@ static apt_bool_t demo_application_on_channel_add(mrcp_application_t *applicatio 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); } } } diff --git a/libs/unimrcp/platforms/unimrcp-client/src/demo_recog_application.c b/libs/unimrcp/platforms/unimrcp-client/src/demo_recog_application.c index 2f4815af97..3235b2ad86 100644 --- a/libs/unimrcp/platforms/unimrcp-client/src/demo_recog_application.c +++ b/libs/unimrcp/platforms/unimrcp-client/src/demo_recog_application.c @@ -32,15 +32,12 @@ #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 */ @@ -77,7 +74,7 @@ static const mrcp_app_message_dispatcher_t recog_application_dispatcher = { /** 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); @@ -108,27 +105,36 @@ static mrcp_channel_t* recog_application_channel_create(mrcp_session_t *session) { mrcp_channel_t *channel; mpf_termination_t *termination; - mpf_codec_descriptor_t *codec_descriptor = NULL; + 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( @@ -235,13 +241,17 @@ static apt_bool_t recog_application_on_define_grammar(mrcp_application_t *applic 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) { @@ -311,7 +321,7 @@ static apt_bool_t recog_app_stream_destroy(mpf_audio_stream_t *stream) } /** 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; } diff --git a/libs/unimrcp/platforms/unimrcp-client/src/demo_synth_application.c b/libs/unimrcp/platforms/unimrcp-client/src/demo_synth_application.c index 9aba1adc8c..4ad6a7d0d5 100644 --- a/libs/unimrcp/platforms/unimrcp-client/src/demo_synth_application.c +++ b/libs/unimrcp/platforms/unimrcp-client/src/demo_synth_application.c @@ -31,7 +31,6 @@ #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" @@ -68,7 +67,7 @@ static const mrcp_app_message_dispatcher_t synth_application_dispatcher = { /** 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); @@ -99,25 +98,34 @@ static mrcp_channel_t* synth_application_channel_create(mrcp_session_t *session) { mrcp_channel_t *channel; mpf_termination_t *termination; - mpf_codec_descriptor_t *codec_descriptor = NULL; + 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( @@ -187,6 +195,7 @@ static apt_bool_t synth_application_on_session_terminate(mrcp_application_t *app 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); @@ -197,8 +206,12 @@ static apt_bool_t synth_application_on_channel_add(mrcp_application_t *applicati } 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"); } @@ -266,7 +279,7 @@ static apt_bool_t synth_app_stream_destroy(mpf_audio_stream_t *stream) } /** 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; } diff --git a/libs/unimrcp/platforms/unimrcp-client/src/demo_util.c b/libs/unimrcp/platforms/unimrcp-client/src/demo_util.c index 75812a125f..db873f288e 100644 --- a/libs/unimrcp/platforms/unimrcp-client/src/demo_util.c +++ b/libs/unimrcp/platforms/unimrcp-client/src/demo_util.c @@ -16,7 +16,6 @@ #include "demo_util.h" /* common includes */ -#include "mrcp_session.h" #include "mrcp_message.h" #include "mrcp_generic_header.h" /* synthesizer includes */ @@ -183,10 +182,10 @@ mpf_rtp_termination_descriptor_t* demo_rtp_descriptor_create(apr_pool_t *pool) /* 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); diff --git a/libs/unimrcp/plugins/Makefile.am b/libs/unimrcp/plugins/Makefile.am index 9e370a0bbc..9bc7eb75c8 100644 --- a/libs/unimrcp/plugins/Makefile.am +++ b/libs/unimrcp/plugins/Makefile.am @@ -10,6 +10,10 @@ if DEMORECOG_PLUGIN SUBDIRS += demo-recog endif +if RECORDER_PLUGIN +SUBDIRS += mrcp-recorder +endif + if CEPSTRAL_PLUGIN SUBDIRS += mrcp-cepstral endif diff --git a/libs/unimrcp/plugins/demo-recog/src/demo_recog_engine.c b/libs/unimrcp/plugins/demo-recog/src/demo_recog_engine.c index 72a86a5c3e..5c1e66f38b 100644 --- a/libs/unimrcp/plugins/demo-recog/src/demo_recog_engine.c +++ b/libs/unimrcp/plugins/demo-recog/src/demo_recog_engine.c @@ -15,20 +15,19 @@ */ /* - * 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" @@ -40,10 +39,10 @@ typedef struct demo_recog_channel_t demo_recog_channel_t; typedef struct demo_recog_msg_t demo_recog_msg_t; /** Declaration of recognizer engine methods */ -static apt_bool_t demo_recog_engine_destroy(mrcp_resource_engine_t *engine); -static apt_bool_t demo_recog_engine_open(mrcp_resource_engine_t *engine); -static apt_bool_t demo_recog_engine_close(mrcp_resource_engine_t *engine); -static mrcp_engine_channel_t* demo_recog_engine_channel_create(mrcp_resource_engine_t *engine, apr_pool_t *pool); +static 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, @@ -68,7 +67,7 @@ static const struct mrcp_engine_channel_method_vtable_t channel_vtable = { /** 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); @@ -122,11 +121,14 @@ struct demo_recog_msg_t { 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; @@ -145,16 +147,16 @@ MRCP_PLUGIN_DECLARE(mrcp_resource_engine_t*) mrcp_plugin_create(apr_pool_t *pool 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) { @@ -166,7 +168,7 @@ static apt_bool_t demo_recog_engine_destroy(mrcp_resource_engine_t *engine) } /** 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) { @@ -177,7 +179,7 @@ static apt_bool_t demo_recog_engine_open(mrcp_resource_engine_t *engine) } /** 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) { @@ -187,8 +189,11 @@ static apt_bool_t demo_recog_engine_close(mrcp_resource_engine_t *engine) 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; @@ -196,26 +201,35 @@ static mrcp_engine_channel_t* demo_recog_engine_channel_create(mrcp_resource_eng recog_channel->stop_response = NULL; recog_channel->detector = mpf_activity_detector_create(pool); recog_channel->audio_out = NULL; - /* create engine channel base */ - recog_channel->channel = mrcp_engine_sink_channel_create( - engine, /* resource engine */ - &channel_vtable, /* virtual methods table of engine channel */ - &audio_stream_vtable, /* virtual methods table of audio stream */ + + 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 */ - NULL, /* 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 */ + + /* create engine channel base */ + recog_channel->channel = mrcp_engine_channel_create( + engine, /* engine */ + &channel_vtable, /* virtual methods table of engine channel */ + recog_channel, /* object to associate */ + 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; } @@ -255,13 +269,16 @@ static apt_bool_t demo_recog_channel_recognize(mrcp_engine_channel_t *channel, m 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"); @@ -333,7 +350,7 @@ static apt_bool_t demo_recog_stream_destroy(mpf_audio_stream_t *stream) } /** 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; } @@ -459,6 +476,18 @@ static apt_bool_t demo_recog_stream_write(mpf_audio_stream_t *stream, const mpf_ 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); } @@ -495,9 +524,17 @@ static apt_bool_t demo_recog_msg_process(apt_task_t *task, apt_task_msg_t *msg) 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; diff --git a/libs/unimrcp/plugins/demo-synth/src/demo_synth_engine.c b/libs/unimrcp/plugins/demo-synth/src/demo_synth_engine.c index c23129c390..fe41714fb7 100644 --- a/libs/unimrcp/plugins/demo-synth/src/demo_synth_engine.c +++ b/libs/unimrcp/plugins/demo-synth/src/demo_synth_engine.c @@ -15,20 +15,19 @@ */ /* - * 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" @@ -39,10 +38,10 @@ typedef struct demo_synth_channel_t demo_synth_channel_t; typedef struct demo_synth_msg_t demo_synth_msg_t; /** Declaration of synthesizer engine methods */ -static apt_bool_t demo_synth_engine_destroy(mrcp_resource_engine_t *engine); -static apt_bool_t demo_synth_engine_open(mrcp_resource_engine_t *engine); -static apt_bool_t demo_synth_engine_close(mrcp_resource_engine_t *engine); -static mrcp_engine_channel_t* demo_synth_engine_channel_create(mrcp_resource_engine_t *engine, apr_pool_t *pool); +static 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, @@ -67,7 +66,7 @@ static const struct mrcp_engine_channel_method_vtable_t channel_vtable = { /** 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); @@ -119,15 +118,17 @@ struct demo_synth_msg_t { }; -#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)); @@ -148,16 +149,16 @@ MRCP_PLUGIN_DECLARE(mrcp_resource_engine_t*) mrcp_plugin_create(apr_pool_t *pool 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) { @@ -169,7 +170,7 @@ static apt_bool_t demo_synth_engine_destroy(mrcp_resource_engine_t *engine) } /** 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) { @@ -180,7 +181,7 @@ static apt_bool_t demo_synth_engine_open(mrcp_resource_engine_t *engine) } /** 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) { @@ -191,8 +192,11 @@ static apt_bool_t demo_synth_engine_close(mrcp_resource_engine_t *engine) } /** 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; @@ -201,14 +205,28 @@ static mrcp_engine_channel_t* demo_synth_engine_channel_create(mrcp_resource_eng synth_channel->time_to_complete = 0; synth_channel->paused = FALSE; synth_channel->audio_file = NULL; - /* create engine channel base */ - synth_channel->channel = mrcp_engine_source_channel_create( - engine, /* resource engine */ - &channel_vtable, /* virtual methods table of engine channel */ - &audio_stream_vtable, /* virtual methods table of audio stream */ + + 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 */ - NULL, /* 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 */ + + /* 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 */ + return synth_channel->channel; } @@ -244,7 +262,10 @@ static apt_bool_t demo_synth_channel_speak(mrcp_engine_channel_t *channel, mrcp_ 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"); @@ -394,7 +415,7 @@ static apt_bool_t demo_synth_stream_destroy(mpf_audio_stream_t *stream) } /** 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; } diff --git a/libs/unimrcp/plugins/mrcp-cepstral/src/mrcp_swift.c b/libs/unimrcp/plugins/mrcp-cepstral/src/mrcp_swift.c index d057650369..037939b8dd 100644 --- a/libs/unimrcp/plugins/mrcp-cepstral/src/mrcp_swift.c +++ b/libs/unimrcp/plugins/mrcp-cepstral/src/mrcp_swift.c @@ -15,29 +15,29 @@ */ /* - * 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 #include -#include "mrcp_resource_engine.h" -#include "mrcp_synth_resource.h" -#include "mrcp_synth_header.h" -#include "mrcp_generic_header.h" -#include "mrcp_message.h" +#include "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, @@ -62,7 +62,7 @@ static const struct mrcp_engine_channel_method_vtable_t channel_vtable = { /** 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); @@ -76,6 +76,18 @@ static const mpf_audio_stream_vtable_t audio_stream_vtable = { 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 { @@ -118,123 +130,120 @@ static const int swift_prosody_rate_table[PROSODY_RATE_COUNT] = { 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); + 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 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; + /* 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; - /* create engine channel base */ - channel = mrcp_engine_source_channel_create( - engine, /* resource engine */ - &channel_vtable, /* virtual methods table of engine channel */ - &audio_stream_vtable, /* virtual methods table of audio stream */ + + 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 */ - 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(!channel) { - swift_port_close(port); - synth_channel->port = NULL; - return NULL; + /* create engine channel base */ + 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(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; } @@ -242,13 +251,7 @@ static mrcp_engine_channel_t* mrcp_swift_engine_channel_create(mrcp_resource_eng /** 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; } @@ -256,13 +259,46 @@ static apt_bool_t mrcp_swift_channel_destroy(mrcp_engine_channel_t *channel) static apt_bool_t mrcp_swift_channel_open(mrcp_engine_channel_t *channel) { /* 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); } @@ -379,7 +415,7 @@ static apt_bool_t synth_stream_destroy(mpf_audio_stream_t *stream) } /** 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; } @@ -525,8 +561,10 @@ static apt_bool_t mrcp_swift_channel_voice_set(mrcp_swift_channel_t *synth_chann } 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; @@ -536,21 +574,29 @@ static apt_bool_t mrcp_swift_channel_voice_set(mrcp_swift_channel_t *synth_chann } 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) { @@ -571,7 +617,8 @@ apt_bool_t swift_prosody_volume_get(const mrcp_prosody_volume_t *prosody_volume, 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) { @@ -643,24 +690,45 @@ static apt_bool_t mrcp_swift_channel_params_set(mrcp_swift_channel_t *synth_chan 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:"); @@ -671,16 +739,21 @@ static void mrcp_swift_voices_show(swift_engine *engine) 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 */ diff --git a/libs/unimrcp/plugins/mrcp-flite/include/flite_voices.h b/libs/unimrcp/plugins/mrcp-flite/include/flite_voices.h index ab896f1040..eb5a438368 100644 --- a/libs/unimrcp/plugins/mrcp-flite/include/flite_voices.h +++ b/libs/unimrcp/plugins/mrcp-flite/include/flite_voices.h @@ -29,11 +29,15 @@ 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 diff --git a/libs/unimrcp/plugins/mrcp-flite/src/mrcp_flite.c b/libs/unimrcp/plugins/mrcp-flite/src/mrcp_flite.c index 4d9c4ad6ed..3e70eddb2f 100644 --- a/libs/unimrcp/plugins/mrcp-flite/src/mrcp_flite.c +++ b/libs/unimrcp/plugins/mrcp-flite/src/mrcp_flite.c @@ -15,21 +15,20 @@ */ /* - * 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" @@ -39,10 +38,10 @@ typedef struct flite_synth_engine_t flite_synth_engine_t; 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, @@ -119,36 +118,39 @@ typedef struct flite_speak_msg_t flite_speak_msg_t; /* 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"); @@ -162,7 +164,7 @@ static apt_bool_t flite_synth_engine_open(mrcp_resource_engine_t *engine) } /** 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"); @@ -200,10 +202,12 @@ static apt_bool_t flite_synth_task_create(flite_synth_channel_t *synth_channel) } /** 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"); @@ -223,29 +227,32 @@ static mrcp_engine_channel_t* flite_synth_engine_channel_create(mrcp_resource_en 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 + 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 */ - &channel_vtable, /* virtual methods table of engine channel */ - &audio_stream_vtable, /* virtual methods table of audio stream */ - synth_channel, /* object to associate */ - codec_descriptor, /* codec descriptor might be NULL by default */ - pool); /* pool to allocate memory from */ - - if(!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->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; @@ -423,8 +430,11 @@ static apt_bool_t flite_speak(apt_task_t *task, apt_task_msg_t *msg) 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; @@ -498,18 +508,18 @@ static APR_INLINE flite_synth_channel_t* flite_synth_channel_get(apt_task_t *tas 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 */ diff --git a/libs/unimrcp/plugins/mrcp-pocketsphinx/Makefile.am b/libs/unimrcp/plugins/mrcp-pocketsphinx/Makefile.am index a38f3a56b7..4cdfb7cf3c 100644 --- a/libs/unimrcp/plugins/mrcp-pocketsphinx/Makefile.am +++ b/libs/unimrcp/plugins/mrcp-pocketsphinx/Makefile.am @@ -21,9 +21,16 @@ mrcppocketsphinx_la_LDFLAGS = -module $(PLUGIN_LT_VERSION) 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 diff --git a/libs/unimrcp/plugins/mrcp-pocketsphinx/include/pocketsphinx_properties.h b/libs/unimrcp/plugins/mrcp-pocketsphinx/include/pocketsphinx_properties.h index e37e55b940..a07c54c6e8 100644 --- a/libs/unimrcp/plugins/mrcp-pocketsphinx/include/pocketsphinx_properties.h +++ b/libs/unimrcp/plugins/mrcp-pocketsphinx/include/pocketsphinx_properties.h @@ -26,34 +26,47 @@ 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, diff --git a/libs/unimrcp/plugins/mrcp-pocketsphinx/src/mrcp_pocketsphinx.c b/libs/unimrcp/plugins/mrcp-pocketsphinx/src/mrcp_pocketsphinx.c index 14e241d5b5..afe451cc88 100644 --- a/libs/unimrcp/plugins/mrcp-pocketsphinx/src/mrcp_pocketsphinx.c +++ b/libs/unimrcp/plugins/mrcp-pocketsphinx/src/mrcp_pocketsphinx.c @@ -15,13 +15,16 @@ */ /* - * 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 @@ -29,11 +32,7 @@ #include #include #include -#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" @@ -46,10 +45,10 @@ typedef struct pocketsphinx_engine_t pocketsphinx_engine_t; 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, @@ -87,8 +86,8 @@ static const mpf_audio_stream_vtable_t audio_stream_vtable = { /** 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; }; @@ -102,7 +101,7 @@ struct pocketsphinx_recognizer_t { 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; @@ -141,60 +140,68 @@ struct pocketsphinx_recognizer_t { 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; @@ -215,27 +222,30 @@ static mrcp_engine_channel_t* pocketsphinx_engine_recognizer_create(mrcp_resourc 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); @@ -290,6 +300,7 @@ static apt_bool_t pocketsphinx_recognizer_close(mrcp_engine_channel_t *channel) /* 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; @@ -303,6 +314,7 @@ static apt_bool_t pocketsphinx_recognizer_request_process(mrcp_engine_channel_t /* 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; @@ -311,9 +323,10 @@ static apt_bool_t pocketsphinx_recognizer_request_process(mrcp_engine_channel_t /** 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"; } @@ -375,7 +388,6 @@ static apt_bool_t pocketsphinx_result_build(pocketsphinx_recognizer_t *recognize recognizer->grammar_id, recognizer->grammar_id, 99, - recognizer->last_result, recognizer->last_result); if(body->buf) { mrcp_generic_header_t *generic_header; @@ -409,16 +421,75 @@ static apt_bool_t pocketsphinx_grammars_clear(pocketsphinx_recognizer_t *recogni 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) { @@ -427,7 +498,7 @@ static apt_bool_t pocketsphinx_define_grammar(pocketsphinx_recognizer_t *recogni 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; @@ -439,78 +510,31 @@ static apt_bool_t pocketsphinx_define_grammar(pocketsphinx_recognizer_t *recogni 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; + + 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; } - - 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; - 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; } } @@ -522,13 +546,49 @@ static apt_bool_t pocketsphinx_define_grammar(pocketsphinx_recognizer_t *recogni /** 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; @@ -557,8 +617,9 @@ static apt_bool_t pocketsphinx_recognize(pocketsphinx_recognizer_t *recognizer, /* 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); @@ -730,11 +791,13 @@ static void* APR_THREAD_FUNC pocketsphinx_recognizer_run(apr_thread_t *thread, v 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 */ @@ -746,6 +809,7 @@ static void* APR_THREAD_FUNC pocketsphinx_recognizer_run(apr_thread_t *thread, v /* 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); @@ -814,6 +878,7 @@ static apt_bool_t pocketsphinx_end_of_input(pocketsphinx_recognizer_t *recognize /* 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; diff --git a/libs/unimrcp/plugins/mrcp-pocketsphinx/src/pocketsphinx_properties.c b/libs/unimrcp/plugins/mrcp-pocketsphinx/src/pocketsphinx_properties.c index 2b8fe94e1c..ecd5053995 100644 --- a/libs/unimrcp/plugins/mrcp-pocketsphinx/src/pocketsphinx_properties.c +++ b/libs/unimrcp/plugins/mrcp-pocketsphinx/src/pocketsphinx_properties.c @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include #include "pocketsphinx_properties.h" #include "apt_log.h" diff --git a/libs/unimrcp/plugins/mrcp-recorder/Makefile.am b/libs/unimrcp/plugins/mrcp-recorder/Makefile.am new file mode 100644 index 0000000000..bdb22748ab --- /dev/null +++ b/libs/unimrcp/plugins/mrcp-recorder/Makefile.am @@ -0,0 +1,16 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -Iinclude \ + -I$(top_srcdir)/libs/mrcp-engine/include \ + -I$(top_srcdir)/libs/mrcp/include \ + -I$(top_srcdir)/libs/mrcp/message/include \ + -I$(top_srcdir)/libs/mrcp/control/include \ + -I$(top_srcdir)/libs/mrcp/resources/include \ + -I$(top_srcdir)/libs/mpf/include \ + -I$(top_srcdir)/libs/apr-toolkit/include \ + $(UNIMRCP_APR_INCLUDES) $(UNIMRCP_APU_INCLUDES) + +plugin_LTLIBRARIES = mrcprecorder.la + +mrcprecorder_la_SOURCES = src/mrcp_recorder_engine.c +mrcprecorder_la_LDFLAGS = -module $(PLUGIN_LT_VERSION) diff --git a/libs/unimrcp/plugins/mrcp-recorder/mrcprecorder.vcproj b/libs/unimrcp/plugins/mrcp-recorder/mrcprecorder.vcproj new file mode 100644 index 0000000000..da80adc3bd --- /dev/null +++ b/libs/unimrcp/plugins/mrcp-recorder/mrcprecorder.vcproj @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/unimrcp/plugins/mrcp-recorder/src/mrcp_recorder_engine.c b/libs/unimrcp/plugins/mrcp-recorder/src/mrcp_recorder_engine.c new file mode 100644 index 0000000000..7a928dc8e6 --- /dev/null +++ b/libs/unimrcp/plugins/mrcp-recorder/src/mrcp_recorder_engine.c @@ -0,0 +1,463 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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,";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; +} diff --git a/libs/unimrcp/tests/mpftest/src/mpf_suite.c b/libs/unimrcp/tests/mpftest/src/mpf_suite.c index 4075ebb546..446c1ab577 100644 --- a/libs/unimrcp/tests/mpftest/src/mpf_suite.c +++ b/libs/unimrcp/tests/mpftest/src/mpf_suite.c @@ -17,8 +17,6 @@ #include #include "apt_test_suite.h" #include "mpf_engine.h" -#include "mpf_user.h" -#include "mpf_termination.h" #include "mpf_rtp_termination_factory.h" #include "mpf_file_termination_factory.h" #include "mpf_audio_file_descriptor.h" @@ -51,8 +49,8 @@ struct mpf_suite_engine_t { /** The main task of the test engine, which sends messages to MPF engine and * processes responses and events sent back from MPF engine */ apt_consumer_task_t *consumer_task; - /** MPF engine task */ - apt_task_t *engine_task; + /** MPF engine */ + mpf_engine_t *engine; /** RTP termination factory */ mpf_termination_factory_t *rtp_termination_factory; /** File termination factory */ @@ -68,7 +66,7 @@ static apt_bool_t mpf_test_run(apt_test_suite_t *suite, int argc, const char * c 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); @@ -97,7 +95,7 @@ static apt_bool_t mpf_test_run(apt_test_suite_t *suite, int argc, const char * c 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; @@ -106,7 +104,7 @@ static apt_bool_t mpf_test_run(apt_test_suite_t *suite, int argc, const char * c 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"); @@ -126,12 +124,12 @@ static apt_bool_t mpf_test_run(apt_test_suite_t *suite, int argc, const char * c 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); @@ -163,8 +161,8 @@ static void mpf_suite_on_start_complete(apt_task_t *task) mpf_suite_session_t *session; apt_task_t *consumer_task; mpf_suite_engine_t *suite_engine; - apt_task_msg_t *msg; - mpf_message_t *mpf_message; + mpf_task_msg_t *task_msg = NULL; + void *descriptor; apr_pool_t *pool = NULL; consumer_task = apt_task_object_get(task); @@ -180,22 +178,17 @@ static void mpf_suite_on_start_complete(apt_task_t *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) { @@ -206,21 +199,20 @@ static void mpf_suite_on_start_complete(apt_task_t *task) } 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 */ @@ -229,147 +221,118 @@ static void mpf_suite_on_terminate_complete(apt_task_t *task) apt_log(APT_LOG_MARK,APT_PRIO_INFO,"On MPF Suite Terminate"); } -/** Process task messages */ -static apt_bool_t mpf_suite_msg_process(apt_task_t *task, apt_task_msg_t *msg) +/** 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); - } - } - } - } - 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"); + 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->termination1) { - apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Subtract Termination [1]"); - msg = apt_task_msg_get(task); - msg->type = TASK_MSG_USER; - request = (mpf_message_t*) msg->data; - - request->message_type = MPF_MESSAGE_TYPE_REQUEST; - request->command_id = MPF_COMMAND_SUBTRACT; - request->context = session->context; - request->termination = session->termination1; - - consumer_task = apt_task_object_get(task); - suite_engine = apt_task_object_get(consumer_task); - apt_task_msg_signal(suite_engine->engine_task,msg); - } - if(session->termination2) { - apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Subtract Termination [2]"); - msg = apt_task_msg_get(task); - msg->type = TASK_MSG_USER; - request = (mpf_message_t*) msg->data; - - request->message_type = MPF_MESSAGE_TYPE_REQUEST; - request->command_id = MPF_COMMAND_SUBTRACT; - request->context = session->context; - request->termination = session->termination2; - - consumer_task = apt_task_object_get(task); - suite_engine = apt_task_object_get(consumer_task); - apt_task_msg_signal(suite_engine->engine_task,msg); + 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->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 == 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) { + 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; icount; 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; } @@ -380,9 +343,9 @@ static mpf_rtp_stream_descriptor_t* mpf_rtp_local_descriptor_create(mpf_suite_se 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; } @@ -395,9 +358,9 @@ static mpf_rtp_stream_descriptor_t* mpf_rtp_remote_descriptor_create(mpf_suite_s 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); diff --git a/libs/unimrcp/tests/mrcptest/mrcptest.vcproj b/libs/unimrcp/tests/mrcptest/mrcptest.vcproj index 69168a11b9..1af3fc579d 100644 --- a/libs/unimrcp/tests/mrcptest/mrcptest.vcproj +++ b/libs/unimrcp/tests/mrcptest/mrcptest.vcproj @@ -1,7 +1,7 @@ #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" @@ -173,7 +174,15 @@ static apt_bool_t test_dir_process(apt_test_suite_t *suite, mrcp_resource_factor 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; diff --git a/libs/unimrcp/tests/mrcptest/src/set_get_suite.c b/libs/unimrcp/tests/mrcptest/src/set_get_suite.c index 830cba3fde..19a72f42c5 100644 --- a/libs/unimrcp/tests/mrcptest/src/set_get_suite.c +++ b/libs/unimrcp/tests/mrcptest/src/set_get_suite.c @@ -17,7 +17,8 @@ #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 */ @@ -38,9 +39,12 @@ 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; @@ -156,9 +160,12 @@ static mrcp_message_t* speak_event_create(mrcp_resource_factory_t *factory, cons 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; @@ -289,7 +296,15 @@ static apt_bool_t get_params_test_run(apt_test_suite_t *suite, mrcp_resource_fac 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; diff --git a/libs/unimrcp/tests/strtablegen/src/main.c b/libs/unimrcp/tests/strtablegen/src/main.c index e547b9b1f1..971243ade2 100644 --- a/libs/unimrcp/tests/strtablegen/src/main.c +++ b/libs/unimrcp/tests/strtablegen/src/main.c @@ -15,6 +15,7 @@ */ #include +#include #include "apt_pool.h" #include "apt_string_table.h" #include "apt_text_stream.h" diff --git a/libs/unimrcp/unimrcp.sln b/libs/unimrcp/unimrcp.sln index 421ed16929..84eb159db6 100644 --- a/libs/unimrcp/unimrcp.sln +++ b/libs/unimrcp/unimrcp.sln @@ -27,27 +27,27 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mrcp", "libs\mrcp\mrcp.vcpr 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}" @@ -57,9 +57,9 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mrcpclient", "libs\mrcp-cli 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}" @@ -74,8 +74,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mrcpv2transport", "libs\mrc 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}" @@ -127,8 +127,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rtsptest", "tests\rtsptest\ 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}" @@ -150,6 +150,26 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mrcpflite", "plugins\mrcp-f 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 @@ -256,6 +276,22 @@ Global {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 @@ -276,11 +312,15 @@ Global {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}