From 89ca44ed3e5a6c58604983e642cebe8f0fd117be Mon Sep 17 00:00:00 2001 From: Michael Jerris Date: Fri, 20 Nov 2009 22:00:51 +0000 Subject: [PATCH] merge upstream changes to svn r2096 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 84cd8df5775fa8349c05f6ed81de752931d8ac31 Author: achaloyan Date: Fri Nov 20 06:34:01 2009 +0000 Provided a bit cleaner solution to adjust scheduler rate (if needed). git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1296 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 2a83967881043cc98f097eb7e3680fc0424811c7 Author: achaloyan Date: Thu Nov 19 19:00:47 2009 +0000 Introduced an option to run mpf scheduler n (rate) times faster that real-time. By default everything remains as is. This option aims to workaround a particular issue. Do not use this option, unless you know what you're doing. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1295 f001bc3a-424a-0410-80a0-a715b8f413a8 commit adb09a70e3c5c8b4df1284419d8d38613079cacf Author: achaloyan Date: Thu Nov 19 18:16:06 2009 +0000 Added an engine for recorder resource in default config, which I forgot to do before git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1294 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 7f450dd3a0f38ea22ec26a700ab494256f669657 Author: achaloyan Date: Wed Nov 18 18:16:03 2009 +0000 Implemented RTCP receiving procedure, made other RTCP related enhancements git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1293 f001bc3a-424a-0410-80a0-a715b8f413a8 commit c2a5dc114f639eebff0e8a2583105d51311fd743 Author: achaloyan Date: Wed Nov 18 17:59:52 2009 +0000 Fixed timers (nodes of the list) to be properly sorted by scheduled to elapse time git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1292 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 47d6df92a8c76567a80667f1a556f930cf9718d4 Author: achaloyan Date: Wed Nov 18 17:50:07 2009 +0000 Set one-8kHz.pcm instead of old one.pcm as default input for asrclient application git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1291 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 1ac5e6c2677bdf0da009c0bd729ea9ce3c265b18 Author: achaloyan Date: Tue Nov 17 20:11:32 2009 +0000 Loading RTCP related settings from config files (unimrcpclient.xml and unimrcpserver.xml respectively) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1290 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 9587d418d1e528d7b590280aef25dc89441d55cf Author: achaloyan Date: Tue Nov 17 20:09:28 2009 +0000 Added policies for RTCP BYE transmission and also added optional reason string generation git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1289 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 707c89631ea5f03bb0944fa14acde98abc942cc0 Author: achaloyan Date: Tue Nov 17 13:18:36 2009 +0000 Added more checking not to crash on wild pointers passed from user space git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1288 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 8b447a59c3939a1ca8bb78da7ca18dcea2680e92 Author: achaloyan Date: Mon Nov 16 16:12:19 2009 +0000 Properly tracking the state of RTP/RTCP session to send compound RTCP BYE message when needed. Using local and remote sockaddr in traces. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1287 f001bc3a-424a-0410-80a0-a715b8f413a8 commit ec172c46622e9b9d332caef9989358453669a3c8 Author: achaloyan Date: Sat Nov 14 21:24:08 2009 +0000 Filled fields of RTCP RR report git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1286 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 8dc2333148cfda12658a1b6d0eaf4521a15173bf Author: achaloyan Date: Sat Nov 14 18:39:05 2009 +0000 MPF timer related fixes and enhancements git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1285 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 3e63923d6d1e5962205e4abba5e6e5b8a364bb68 Author: achaloyan Date: Sat Nov 14 16:29:54 2009 +0000 Added debug trace to indicate waiting for new messages state git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1284 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 9a7937cb5cf2ed669bc193ded4cd389282b42230 Author: achaloyan Date: Fri Nov 13 20:37:39 2009 +0000 Sending first RTCP reports git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1283 f001bc3a-424a-0410-80a0-a715b8f413a8 commit ebe5ccbdb032edfb8a45cbdd29cc4271b72b1b8f Author: achaloyan Date: Fri Nov 13 20:36:23 2009 +0000 Added an utility function to get current NTP time git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1282 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 4421f92ed549842252616802ce46e2bc3716652c Author: achaloyan Date: Fri Nov 13 18:17:02 2009 +0000 Fixed RTCP packet fields and alignments git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1281 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 28ec22508d517720a52c5a98a5cdb252df604363 Author: achaloyan Date: Thu Nov 12 20:17:05 2009 +0000 Set RTCP transmission timer if RTCP is enabled git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1280 f001bc3a-424a-0410-80a0-a715b8f413a8 commit a7d91738c0d024600050ebc3955642f955eb6aeb Author: achaloyan Date: Thu Nov 12 18:56:33 2009 +0000 Made timer manager available for media terminations git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1279 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 4940bee339c41206af57ff2b623f8211a4c11c07 Author: achaloyan Date: Thu Nov 12 18:50:32 2009 +0000 Do not destroy audio streams while destroying media context, which still contains terminations. Instead just subtract terminations. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1278 f001bc3a-424a-0410-80a0-a715b8f413a8 commit a24d8e814e4fe8d587b87c7a0103e3ecc8cabb6b Author: achaloyan Date: Thu Nov 12 18:41:00 2009 +0000 Added timer manager (missing from prev commit) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1277 f001bc3a-424a-0410-80a0-a715b8f413a8 commit bc132cbae6e761630960797234ffc76bfcd76897 Author: achaloyan Date: Thu Nov 12 18:38:44 2009 +0000 Reworked timer/clock related entities. mpf_scheduler provides clock for media processing and timers mpf_engine uses media processing clock as it was before mpf_timer_manager uses timer clock to provide timers management routine (set/kill/elapse) for mpf layer mpf_timers can be used only from the context of mpf_scheduler thread (context, streams, callbacks) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1276 f001bc3a-424a-0410-80a0-a715b8f413a8 commit f7765d18eeabb7f3d61c51f78c47f1a2667405fc Author: achaloyan Date: Thu Nov 12 14:59:13 2009 +0000 Removed array of multimedia timers, only one of them is actually used. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1275 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 5997acf00aebc18c78294e574d3614fcbc794ac4 Author: achaloyan Date: Thu Nov 12 14:56:34 2009 +0000 Basically implemented RTCP session scheduler git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1274 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 9ab5014493a35e1f8f8ba34031e8a884c06afc4c Author: achaloyan Date: Wed Nov 11 21:19:04 2009 +0000 Added new methods to mpf_termination interface such as add() and subtract(). These methods are called when termination is added and subtracted from mpf_context git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1273 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 6b7a388da519acbbc6ecec4ead0e381c63346402 Author: achaloyan Date: Wed Nov 11 19:18:59 2009 +0000 Separated mpf_termination and mpf_termination_factory in order to hide mpf_termination in mpf layer and not to expose it to user space. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1272 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 2148e6a7bfd42f9c9b662a2a107349476d2a4fa1 Author: achaloyan Date: Wed Nov 11 18:11:20 2009 +0000 Implemented creation of socket pair (RTP/RTCP). RTCP socket is created next to RTP. It's reasonable to create RTCP socket even in case RTCP isn't used to avoid sending ICMP packets to remote peer. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1271 f001bc3a-424a-0410-80a0-a715b8f413a8 commit c48e8e1f286ba5e433c7a961587ced8039cde3fd Author: achaloyan Date: Wed Nov 11 05:10:03 2009 +0000 Fixed compilation git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1270 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 99e186b3d4e7e74da4cdbfebf141b13e611d7d1c Author: achaloyan Date: Tue Nov 10 20:30:16 2009 +0000 Added RTCP related params to rtp_config (yet to be loaded from config file) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1269 f001bc3a-424a-0410-80a0-a715b8f413a8 commit f43ef8c5c9a8d168b38784b5a2f95df8aa9af932 Author: achaloyan Date: Tue Nov 10 20:21:52 2009 +0000 Added skeleton for RTCP session scheduler git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1268 f001bc3a-424a-0410-80a0-a715b8f413a8 commit c7359c5e3fe1f577a18e9dd9321865697da064cc Author: achaloyan Date: Tue Nov 10 18:39:55 2009 +0000 Added RTCP packet git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1267 f001bc3a-424a-0410-80a0-a715b8f413a8 commit d013ea92461e21803803707d171c63b8f163dee8 Author: achaloyan Date: Tue Nov 10 18:32:40 2009 +0000 Fixed minor typos in comments git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1266 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 1b1f313769f1375eb3eeea8c1378463a226182fa Author: achaloyan Date: Mon Nov 9 21:56:33 2009 +0000 Set jitter and ssrc of rr_stat. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1265 f001bc3a-424a-0410-80a0-a715b8f413a8 commit a09883271b921d5ff649b66af65b91f0aab8aeaf Author: achaloyan Date: Mon Nov 9 21:29:21 2009 +0000 Calculated not only number of packets sent, but also octets (payload bytes) sent. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1264 f001bc3a-424a-0410-80a0-a715b8f413a8 commit de157f9cf11eae33485e820cf6d32aee0a4ce739 Author: achaloyan Date: Mon Nov 9 20:52:38 2009 +0000 Fixed typo in file name (rtpc -> rtcp) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1263 f001bc3a-424a-0410-80a0-a715b8f413a8 commit c4363f25417e045fa56ef05ee5a1ef27c847a02d Author: achaloyan Date: Mon Nov 9 20:48:41 2009 +0000 Added RTCP header and stats used in SR and RR reports git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1262 f001bc3a-424a-0410-80a0-a715b8f413a8 commit f8a10bb46185567c68fd9785cd351f3931519d2e Author: achaloyan Date: Mon Nov 9 17:23:26 2009 +0000 Reset frame type and marker before read_frame() in decoder git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1261 f001bc3a-424a-0410-80a0-a715b8f413a8 commit a8ac67a6ee566b0b3535a4cb4ea98ca18021c077 Author: achaloyan Date: Mon Nov 9 16:03:45 2009 +0000 Fixed simultaneous transmission of named events and regular audio stream git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1260 f001bc3a-424a-0410-80a0-a715b8f413a8 commit b8fb271257862c817eae069dc65093eebdcb9ff6 Author: tomas.valenta@speechtech.cz Date: Mon Nov 9 00:18:41 2009 +0000 DTMF generator now fully supports out-of-band digits, including long-lasting events. DTMF detector now fully supports both in-band and out-of-band digits. To do: Simultaneous in-band and out-of-band transfer (at generator side) and events' ptime support (currently CODEC_FRAME_TIME_BASE=10ms used). git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1259 f001bc3a-424a-0410-80a0-a715b8f413a8 commit df38c3338ddb51eedb0640a3e14c435258876115 Author: achaloyan Date: Sun Nov 8 20:31:18 2009 +0000 Defined RTP_TRACE and JB_TRACE as null_trace by default git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1258 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 1b94caa54c41fb3c4cafe7f752888d96f9fbcd79 Author: achaloyan Date: Sun Nov 8 20:19:43 2009 +0000 Added JB_TRACE define git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1257 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 555e2c6a9340f9cf8640abd69a37a26f3a2d17ab Author: achaloyan Date: Sun Nov 8 19:39:24 2009 +0000 Added RTP_TRACE define git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1256 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 40bc2080211e33eefc25b183e124a6c694043c90 Author: achaloyan Date: Sun Nov 8 13:37:41 2009 +0000 Do not restart rx routine if failure threshold fired on processing of named events. Usually these are minor errors, which can be safely ignored. Unfortunately there are still many broken implementations out in the field. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1255 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 9b7bd4b4937a1546052c26ba957f5075b6d8c9b1 Author: achaloyan Date: Sat Nov 7 21:40:30 2009 +0000 Although host/net order of named events duration was effectively handled in mpf generator, this field as well as others should in host order for user level, including dtmf generator and detector entities. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1254 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 3a48e36216d03459e258e4f78d314a7e12d4c4cd Author: achaloyan Date: Sat Nov 7 21:29:17 2009 +0000 Added traces to show how to trigger start and end of named events. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1253 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 7e88afd669e77a45a175f88e09b8d68069d189bf Author: achaloyan Date: Sat Nov 7 21:17:40 2009 +0000 Fixed processing of named events in jitter buffer git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1252 f001bc3a-424a-0410-80a0-a715b8f413a8 commit f547a0454c79e387536da276ef9dbde24ff09210 Author: achaloyan Date: Sat Nov 7 19:10:12 2009 +0000 Reset, copy frame->marker where required git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1251 f001bc3a-424a-0410-80a0-a715b8f413a8 commit a3aa75dd7aa0128c19cdaa10d87eed0f5f7d116c Author: achaloyan Date: Sat Nov 7 18:36:01 2009 +0000 Set event descriptor based on capabilities and offer/answer git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1250 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 7ee88a44a398ef9a7a9847277bd96ea7a4db5f16 Author: achaloyan Date: Fri Nov 6 20:55:57 2009 +0000 Writing named events to jitter buffer (detection of new events, tracking of event updates, ...) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1249 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 0a0099e1b7f90b288dabfcd694e8dea0f5b5b609 Author: achaloyan Date: Fri Nov 6 18:21:42 2009 +0000 Added sample DTMF recognizer scenario/session Update your umcscenarios.xml file and use 'run dtmf' from umc console git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1248 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 40bfd9392b819708312effc60833fe5bf09f0418 Author: achaloyan Date: Fri Nov 6 05:42:27 2009 +0000 Added missing -lm to resolve undefined reference to 'sin' and 'cos' used from mpf_detector and mpf_generator git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1247 f001bc3a-424a-0410-80a0-a715b8f413a8 commit f6b4d75156532f6e6e8441021c3d59f4c2658634 Author: achaloyan Date: Thu Nov 5 20:57:54 2009 +0000 Fixed compilation under gcc (redefinition of typedef struct) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1246 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 7b72a080f717628ba7a7197eef79be1adcb8ad6a Author: achaloyan Date: Thu Nov 5 18:58:42 2009 +0000 Fixed host/net order routine for named events. User level is in host order. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1245 f001bc3a-424a-0410-80a0-a715b8f413a8 commit ba65dbed0e994dce789de726921d84e5c8af19bf Author: tomas.valenta@speechtech.cz Date: Thu Nov 5 18:58:24 2009 +0000 Added DTMF generator and detector into MPF. Currently just in-band (audio) digits only are fully supported. The generated tone length is currently limited to 0xFFFF RTP time units (approx 8 s@8 kHz); will be enhanced. Out-of-band generator might work, but is not complete. Out-of-band detector is not implemented yet. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1244 f001bc3a-424a-0410-80a0-a715b8f413a8 commit e84cf0107550fe0b5df025b365bff537a3c43461 Author: achaloyan Date: Thu Nov 5 16:10:46 2009 +0000 Set resource id if resource indeed created git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1243 f001bc3a-424a-0410-80a0-a715b8f413a8 commit f074ded9633a148305f8182ea353e583fa549e43 Author: achaloyan Date: Thu Nov 5 14:15:08 2009 +0000 Set named events in jitter buffer git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1242 f001bc3a-424a-0410-80a0-a715b8f413a8 commit a86daebb9db0b1ccd5b1400fa65b2d95e674a8c6 Author: achaloyan Date: Wed Nov 4 22:02:33 2009 +0000 Properly set timestamp on event packets. All the markers should be set from user level to make it work correctly. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1241 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 52a71c40fa19ee2210923cf1d3893b4da53054fb Author: achaloyan Date: Wed Nov 4 21:58:24 2009 +0000 Added new marker which indicates start of new segment in case of long-lasting events in order to properly set timestamp on event packets git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1240 f001bc3a-424a-0410-80a0-a715b8f413a8 commit a7bf0a924f924798032d65cf0c2484bab86ad6ff Author: achaloyan Date: Wed Nov 4 19:31:41 2009 +0000 Added frame marker which indicates either start or end of event, more markers might be added in the future git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1239 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 433e5c94a0e3ee1bc39bd113bc8e0af2832df950 Author: achaloyan Date: Wed Nov 4 19:09:28 2009 +0000 Supported simultaneous transmission of events and audio data, if/when needed. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1238 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 5b9acde2754550eab6cfae7f189dfcf762166a3f Author: achaloyan Date: Tue Nov 3 19:20:06 2009 +0000 Set Max-time and Final-silence header fields in umc record session git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1237 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 25b0c6ebc7faadd3d8d15bae603d428189ee0df1 Author: achaloyan Date: Tue Nov 3 19:17:20 2009 +0000 Supported a few more header fields such as Max-time, Final-silence also set Record-URI header filed in response to STOP request or RECORD-COMPLETE event git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1236 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 56d14eebe0d57d7a4eeec68e7e74075ddabd19d2 Author: achaloyan Date: Tue Nov 3 17:17:36 2009 +0000 Split complete_timeout into two parts: speech (activity detection) timeout and silence (inactivity detection) timeout git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1235 f001bc3a-424a-0410-80a0-a715b8f413a8 commit f62b8529a3e174be452dca631ca94f27011cba05 Author: achaloyan Date: Mon Nov 2 21:09:48 2009 +0000 Added recorder scenario/session to umc application framework. To launch basic recorder session, update your umscenarios.xml file and use "run rec" command git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1234 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 3d1707440b7ce386a35802f724549212238e5126 Author: achaloyan Date: Mon Nov 2 21:02:33 2009 +0000 Integrated recorder plugin into GNU build git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1233 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 2e0071e66003ff4d73dc8e44a83000884f79af40 Author: achaloyan Date: Mon Nov 2 20:53:08 2009 +0000 Added mrcprecorder to the solution git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1232 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 965bf199a93ac7b79d7e94de8a25a5c55fa18b8d Author: achaloyan Date: Mon Nov 2 20:44:19 2009 +0000 Initially added implementation of recorder engine (plugin) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1231 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 5e3e1dbca93ecc37593a4c8a091a4b039a97450d Author: achaloyan Date: Mon Nov 2 17:48:46 2009 +0000 Added state machine for recorder resource git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1230 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 0ad71ee97050e33da4f39803de1132dcb7d95e48 Author: achaloyan Date: Tue Oct 27 19:06:01 2009 +0000 Fixed core dump with large in-line grammars (Issue-52, Thanks Asackheim) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1229 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 089756fca925b818ade79e1b009612aed8af3f36 Author: achaloyan Date: Tue Oct 27 19:00:38 2009 +0000 Added a few more checkings git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1228 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 60e20ca963baded1e1e9e8135030d0fd6085cf83 Author: achaloyan Date: Tue Oct 27 14:49:23 2009 +0000 Added missing #include to compile under gcc git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1227 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 7a1a5eeff14b80a795fd03f164bb03d40e586d2b Author: achaloyan Date: Tue Oct 27 14:48:09 2009 +0000 Fixed function prototypes to compile without warnings with -Wstrict-prototypes gcc option As John stated, int foo() is not the same as int foo(void). This is true for pure C, meantime in C++ int foo() and int foo(void) are just the same prototypes. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1226 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 744d6dc495b072f175f38ccbee4b415b88c95239 Author: achaloyan Date: Tue Oct 27 14:35:15 2009 +0000 Properly processed nua's shutdown (Thanks John) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1225 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 8000fb6d6e7503b87c8046c9bb65c56b61137f08 Author: achaloyan Date: Tue Oct 27 14:27:02 2009 +0000 Initialized codec manager a bit later with media engine. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1224 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 036cc125672f2a05cc9847436f967778d3dd8831 Author: achaloyan Date: Mon Oct 26 17:44:48 2009 +0000 Loading MRCP resources from config. Resources MUST be loaded first, update your unimrcpclient.xml and unimrcpserver.xml files according git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1223 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 0788e08c0a17bd2974cf75d6a0459ee4d6e947f6 Author: achaloyan Date: Mon Oct 26 17:12:57 2009 +0000 Do not initialize profile if no resource factory registered git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1222 f001bc3a-424a-0410-80a0-a715b8f413a8 commit e8cc3b89b8de07a4f7a480560616b6912059a082 Author: achaloyan Date: Mon Oct 26 14:04:31 2009 +0000 Fixed @brief description git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1221 f001bc3a-424a-0410-80a0-a715b8f413a8 commit aed93bacf17e27f2507209a22382d700dc9d17a9 Author: achaloyan Date: Sun Oct 25 19:26:22 2009 +0000 Separated MRCP start-line related routine git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1220 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 3c71e09c365a2e252a3ce64bebffa8cfafe6081c Author: achaloyan Date: Sun Oct 25 19:06:54 2009 +0000 Stored mrcp_resource instead of mrcp_resource_id in mrcp_message. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1219 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 90822e0d43710cdef2caf8f3761a56c333a6f9fa Author: achaloyan Date: Sat Oct 24 15:28:44 2009 +0000 Initialized resource for channel at earlier stage to remove no more required resource_id channel member git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1218 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 1b42089957ee75a0da7f78fca442c94b1a88fde9 Author: achaloyan Date: Sat Oct 24 14:39:47 2009 +0000 Moved resource loader to "control" folder to leave "resources" folder exclusively for resources. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1217 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 76940502830e53c99ed4f7e92e12382a973bef61 Author: achaloyan Date: Sat Oct 24 14:29:17 2009 +0000 Arranged resource, resource factory and resource loader entities in a bit more clever way. Both resource name and identifier are properties of resource. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1216 f001bc3a-424a-0410-80a0-a715b8f413a8 commit b97b51d0300668cb821b62124ab1a724b29cdbb4 Author: achaloyan Date: Fri Oct 23 18:20:10 2009 +0000 Replaced mrcp_default_factory with mrcp_resource_loader Resource loader soon will be able to load resources based on configuration. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1215 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 6e8ca2e820b6282b46169935272eb10229a7ef71 Author: achaloyan Date: Fri Oct 23 15:46:08 2009 +0000 Applied temporarily workaround to send a named event as soon as it's received regardless what the actual packetization time is (Thanks Randy) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1214 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 7fc090163d5b5eda6e12e302b4bde2729ac4cc05 Author: achaloyan Date: Fri Oct 23 13:24:53 2009 +0000 Initialized recorder resource on start-up by default (should be configurable) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1213 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 9f66366c68461fbe2c7edfe3cd15fdf213c76f79 Author: achaloyan Date: Thu Oct 22 18:46:28 2009 +0000 Added methods and events for MRCP recorder resource git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1212 f001bc3a-424a-0410-80a0-a715b8f413a8 commit ce024776e6f03ec578cfae108cf53aab9ec1b086 Author: achaloyan Date: Thu Oct 22 18:07:16 2009 +0000 Hopefully finally fixed RTSP/MRCPv1 session management related issues, when multiple resources are offered using consecutive RTSP SETUP requests containing no SDP. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1211 f001bc3a-424a-0410-80a0-a715b8f413a8 commit a72740ffe0169e87fe7a33e8403ccc61a7e5ad38 Author: achaloyan Date: Thu Oct 22 14:53:06 2009 +0000 Added a few accessors to mrcp_application interface in order to completely hide session interface from the application context. Session pointer SHOULD be used as an opaque pointer from now on. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1210 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 78c223cc46fedc0a8f5a76492aea7f76049cf23f Author: achaloyan Date: Wed Oct 21 20:41:49 2009 +0000 Initially added MRCP recorder resource header git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1209 f001bc3a-424a-0410-80a0-a715b8f413a8 commit ea6b4a05b836b072cad4f2b17c312a31a516350e Author: achaloyan Date: Wed Oct 21 16:19:59 2009 +0000 Enhanced apt_task to be able to raise late start-complete event when task is not just started, but also ready to process messages. Some tasks are implicitly ready to process messages. The others still initialize some additional data. Thus, they should explicitly indicate ready state. As a result on_ready event will be raised to application context when it was intended to be raised. (Issue-49) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1208 f001bc3a-424a-0410-80a0-a715b8f413a8 commit fecf6decb056e0b7232622abbf0240b230f4332f Author: achaloyan Date: Tue Oct 20 20:19:01 2009 +0000 Better representation of abstract MRCP resource git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1207 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 813442c8b6b3d2fc202e58ea9189e2e62e4232d2 Author: achaloyan Date: Tue Oct 20 17:22:56 2009 +0000 Updated project files according git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1206 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 1bc8a5b581b78a5c31f5e2ea594e87725be9b513 Author: achaloyan Date: Tue Oct 20 17:13:57 2009 +0000 Moved files as well git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1205 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 3e0bd386532d66ec7d63030910b4fa62012eeb6d Author: achaloyan Date: Tue Oct 20 17:08:40 2009 +0000 Moved server side state machine creation from shared between client and server stacks mrcp_resource to mrcp_engine git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1204 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 07ce822cefd70b8696393ba1681179926168cb87 Author: achaloyan Date: Tue Oct 20 16:30:08 2009 +0000 Renamed server_state_machine to state_machine. No functional changes. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1203 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 738e0b5a319f3e3c5968ffc4bc1ea649e63031c8 Author: achaloyan Date: Tue Oct 20 16:24:07 2009 +0000 Removed client_state_machine, which was actually not implemented and unused. Renamed server_state_machine to state_machine. No functional changes. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1202 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 0f576df4b9a39f6e4ffaec76056e0d45e59dbaa6 Author: achaloyan Date: Mon Oct 19 18:46:57 2009 +0000 Fixed compilation under gcc git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1201 f001bc3a-424a-0410-80a0-a715b8f413a8 commit a1ef30ee7037f61ba8454e3447b675ebeb8a0d39 Author: achaloyan Date: Mon Oct 19 18:18:26 2009 +0000 Added compile time define to support "too long" MRCP request identifiers. MRCPv2 specifies request-id as 32bit unsigned integer, while MRCPv1 doesn't limit this value. Some MRCPv1 clients use too long request-id. To support them #define TOO_LONG_MRCP_REQUEST_ID (Issue-48) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1200 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 8e560595461249c0924523f43a0a02a2f3106afe Author: achaloyan Date: Sun Oct 18 17:54:05 2009 +0000 Increased unimrcp version number git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1198 f001bc3a-424a-0410-80a0-a715b8f413a8 commit adbef71d8a6017c654fc3bc970120da81ea1d8e0 Author: achaloyan Date: Sun Oct 18 17:52:55 2009 +0000 Increased plugin version number git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1197 f001bc3a-424a-0410-80a0-a715b8f413a8 commit d19a7550d924d9482996641e7d7d617c9082c72e Author: achaloyan Date: Sun Oct 18 17:52:10 2009 +0000 Clarified a bit plugin implementation rules git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1196 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 0456d9b69f89b82b4487acb91574380eaa88b282 Author: achaloyan Date: Sun Oct 18 15:51:33 2009 +0000 Fixed doxygen warnings git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1195 f001bc3a-424a-0410-80a0-a715b8f413a8 commit ba29c377eda8bdc3212c49c3f217d50ba98da2d8 Author: achaloyan Date: Sun Oct 18 10:34:45 2009 +0000 Updated the INSTALL to strengthen requirement for the dependency package. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1188 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 598434417a61d48072dad7f5e52dd7d1aabcd802 Author: achaloyan Date: Sat Oct 17 17:39:54 2009 +0000 Added Chaitanya and Vali to the contributors list git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1185 f001bc3a-424a-0410-80a0-a715b8f413a8 commit f3de0b40955ed4386e21a480e158f1557a957bdc Author: achaloyan Date: Fri Oct 16 15:04:25 2009 +0000 Fixed header inheritance routine in order to not override fields which exist in original header (Issue-47) Broken since r846 git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1184 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 0a9d6f344d456f283d2ab9dfd112961908fcce39 Author: achaloyan Date: Thu Oct 15 15:43:17 2009 +0000 Do not accept any messages after final TEARDOWN (session is being terminated) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1183 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 0e04171d7f00a02901b8f793288e0cdd9778e0f2 Author: achaloyan Date: Thu Oct 15 15:09:04 2009 +0000 Return JB_DISCARD_TOO_EARLY if buffer is full git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1182 f001bc3a-424a-0410-80a0-a715b8f413a8 commit cd0cd0c0f89988a841db4947c79bc584591db2fd Author: achaloyan Date: Wed Oct 14 15:56:47 2009 +0000 Spelled 'discovery' git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1181 f001bc3a-424a-0410-80a0-a715b8f413a8 commit c8ee0ead88dd52dd08d4eeab8048ad8263a55a88 Author: achaloyan Date: Tue Oct 13 18:37:43 2009 +0000 Fixed RTPS server stack processing in case there are more than one resource in the scope of the same RTSP session git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1180 f001bc3a-424a-0410-80a0-a715b8f413a8 commit d325c4d5fa1a7b96bb47111147dde1804890d4c4 Author: achaloyan Date: Tue Oct 13 18:34:03 2009 +0000 Set resource name for RTPS ANNOUNCE events git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1179 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 6d418aa8bc5e21cd32394fecb92fc949befc56cd Author: achaloyan Date: Tue Oct 13 06:09:36 2009 +0000 Checked the return value of MrcpSessionCreate() git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1178 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 7a6396943262fe550f49f8040217d4f26d39c70f Author: achaloyan Date: Mon Oct 12 18:37:17 2009 +0000 Added convenient headers, which should be included from synth and recog plugins mrcp_synth_engine.h mrcp_recog_engine.h git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1177 f001bc3a-424a-0410-80a0-a715b8f413a8 commit d16295aee7c3518a0523de15ced333918f6feb08 Author: achaloyan Date: Mon Oct 12 18:14:10 2009 +0000 Made changes in plugins proposed earlier (resource_engine -> engine) Nothing has to be done. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1176 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 27fdce12cb98e49bd61a58c93b113740d2aae1c2 Author: achaloyan Date: Mon Oct 12 15:20:34 2009 +0000 added svn props git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1175 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 354b415531d0605292d8e861590ef536803a849c Author: achaloyan Date: Mon Oct 12 13:21:40 2009 +0000 Added support for grammars supplied inline within RECOGNIZE request in PocketSphinx plugin git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1174 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 9b3889df631dfa896a84d05c3eed876d87a43f9f Author: achaloyan Date: Mon Oct 12 13:19:35 2009 +0000 Enhanced recog session/scenario to be able to supply inline grammar within RECOGNIZE request, instead of preceding DEFINE-GRAMMAR one. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1173 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 5be3d2ae0ca3ec58888bd76d46315e22671f87b3 Author: achaloyan Date: Sun Oct 11 17:19:25 2009 +0000 Added new attribute for recog scenario to optionally specify audio source instead of default hard coded one.pcm. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1172 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 1c4e5829cf2657df49c7a98e6732c1c980fef981 Author: achaloyan Date: Sun Oct 11 15:31:38 2009 +0000 Added a couple of new umc console commands to show running sessions and available scenarios. > show sessions > show scenarios It was already possible to run new session and kill it > run synth [1] > kill 1 git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1171 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 4c59fe99e7d30359f416a1544c0ee84fc6784ee2 Author: achaloyan Date: Sun Oct 11 08:23:48 2009 +0000 Fixed another potential crash of client stack (leftover from previous fix). Pending session termination request should not be canceled even if termination event is received. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1170 f001bc3a-424a-0410-80a0-a715b8f413a8 commit b966e80ea53795344c4e5c1472d3173ff71765d6 Author: achaloyan Date: Sat Oct 10 19:17:53 2009 +0000 Install *.jsgf and *.txt as well git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1169 f001bc3a-424a-0410-80a0-a715b8f413a8 commit fb12c7c19144fced8fa684f60bd3308e7099afb7 Author: achaloyan Date: Sat Oct 10 19:06:24 2009 +0000 Added sample JSGF grammar and plain text contents git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1168 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 5c13e452e75c506436c6327e600f11b460e61ec3 Author: achaloyan Date: Sat Oct 10 18:57:29 2009 +0000 Added umcscenarios.xml git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1167 f001bc3a-424a-0410-80a0-a715b8f413a8 commit fe2d1fa156764eb0f6001b5a6d0f21cc3f99df31 Author: achaloyan Date: Sat Oct 10 18:16:16 2009 +0000 Added missing stdlib.h include git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1166 f001bc3a-424a-0410-80a0-a715b8f413a8 commit c511584c906303db3eb5607dc94aae91c1fc307f Author: achaloyan Date: Sat Oct 10 18:12:00 2009 +0000 Loading the attributes of predefined scenarios such as content-type, content-location, capabilities from config file. Predefined scenarios (Synthesizer, Recognizer) are also a bit enhanced, they are more customizable now... git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1165 f001bc3a-424a-0410-80a0-a715b8f413a8 commit b2160f787f7695bc5128137309793ba5c4913da1 Author: achaloyan Date: Sat Oct 10 14:18:11 2009 +0000 Fixed return value, which should be int for bitmasks git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1164 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 536460af46efc3389db9e40e584fa0331d278730 Author: achaloyan Date: Wed Oct 7 13:14:47 2009 +0000 Do not just return, but close file descriptor if config file is not a valid XML git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1163 f001bc3a-424a-0410-80a0-a715b8f413a8 commit f2f7452654a5449fc99ea5c28435fcdff651fef2 Author: achaloyan Date: Mon Oct 5 17:56:42 2009 +0000 Added umc project into the solution git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1162 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 727c402ca49c98bad25e408296cf268256f3a5d1 Author: achaloyan Date: Mon Oct 5 17:52:07 2009 +0000 Added umc application into GNU build git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1161 f001bc3a-424a-0410-80a0-a715b8f413a8 commit baa849da05ab3f52612ae17ea91b89ba5def954e Author: achaloyan Date: Sun Oct 4 20:58:45 2009 +0000 Added another application (umc) built on top of libunimrcpclient. This application will eventually replace demo unimrcpclient ... git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1160 f001bc3a-424a-0410-80a0-a715b8f413a8 commit e962a729e315cf54051d5b2eb5abbe0c6d72d4bc Author: achaloyan Date: Fri Oct 2 18:34:42 2009 +0000 Fixed plugin_version_hdr in configure.ac (Thanks, John) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1159 f001bc3a-424a-0410-80a0-a715b8f413a8 commit cd46e9c59102734fa2c64097857b86a94403f6e0 Author: achaloyan Date: Fri Oct 2 15:24:23 2009 +0000 Fixed APR_ARRAY_IDX related issue in apt_pair (Thanks, Anthony) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1158 f001bc3a-424a-0410-80a0-a715b8f413a8 commit a7cc93a58d189ec805f55360c5cc7d6578566050 Author: achaloyan Date: Fri Oct 2 07:45:19 2009 +0000 Implicitly modify initial offer (RTSP/MRCPv1), if it contains disabled media stream. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1157 f001bc3a-424a-0410-80a0-a715b8f413a8 commit cc135ce9547c5caf09238b552d53085b4443b8bc Author: achaloyan Date: Thu Oct 1 04:06:18 2009 +0000 Added a user level function to associate (de-associate) external object with the session git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1156 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 5364deb02ce21e10f3a8a0f13ab78473022c5248 Author: achaloyan Date: Wed Sep 30 18:24:33 2009 +0000 Fixed crash of client stack caused by race condition on session termination. Most probably this fixes the issue reported on the discussion group recently and Issue-33. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1155 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 8c6b3def701b3e4b04b7cec344d6c83e6b147733 Author: achaloyan Date: Tue Sep 29 15:54:02 2009 +0000 Enhanced "prepare" utility project to install the whole apr-iconv stuff including iconv/*.so, if apr-iconv library exists. (Issue-45) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1154 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 1a26706b602aa5aef4eb77e60656600e0efab22c Author: achaloyan Date: Mon Sep 28 18:38:47 2009 +0000 Fixed C++ compile error in codec_descriptor.h (Issue-44, Thanks Vali) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1153 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 980551739dae22c9219d2987958d44ba65072cf1 Author: achaloyan Date: Mon Sep 28 18:32:22 2009 +0000 Added media path traces for mixer and multiplier. Fixed initialization of source and sink streams in them. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1152 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 4d0d245f3ad93e62b64d549df4a3edfa00607eba Author: achaloyan Date: Mon Sep 28 10:01:23 2009 +0000 Added an example on how to specify 16Kz codecs in config git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1151 f001bc3a-424a-0410-80a0-a715b8f413a8 commit edd7d550cd0c69b7e71863fe82f825a80d75348a Author: achaloyan Date: Mon Sep 28 04:51:02 2009 +0000 Added to project file git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1150 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 56866f06009823a8a682535d12a516295e627be9 Author: achaloyan Date: Sun Sep 27 18:41:54 2009 +0000 Using MRCP_PLUGIN_VERSION_DECLARE macro for existing plugins. Version info is a property of the plugin but not the engine as it was before. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1149 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 0f33b7d3a61ee9bb2815b54e0a6f7473acd42f2d Author: achaloyan Date: Sun Sep 27 18:37:08 2009 +0000 Separated user interface (what MRCP server uses) and implementation (what plugins implement) of MRCP engines. There are a few changes plugin implementors must be aware of - Plugin version number must be declared in each plugin, preferably using MRCP_PLUGIN_VERSION_DECLARE macro - MRCP engine is renamed from mrcp_resource_engine_t to mrcp_engine_t (legacy typedef remains) - Instead of mrcp_resource_engine.h consider using mrcp_engine_impl.h (legacy header remains) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1148 f001bc3a-424a-0410-80a0-a715b8f413a8 commit b10833cf2bc46e7c306fdbf27493d95b86136258 Author: achaloyan Date: Sun Sep 27 09:55:54 2009 +0000 Added missing #include stdlib.h (GNU compilation) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1147 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 1601da5d00e1d04ae284578937a6d7f07df75829 Author: achaloyan Date: Sun Sep 27 09:53:04 2009 +0000 Moved, separated functionality related to loading of plugins into corresponding header and source files git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1146 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 860768fabdd91b8c50ccdbbe882455bf120d59bf Author: achaloyan Date: Sun Sep 27 09:01:27 2009 +0000 Moved, separated functionality related to factory of MRCP engines into corresponding header and source files git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1145 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 065cb1e9a449a6692f3cc023dee14f60c8612800 Author: achaloyan Date: Sat Sep 26 07:28:47 2009 +0000 Finally applied changes to the Cepstral plugin either. Scan available voices and indicate stream capabilities upon channel creation and use decided codec descriptor on channel open Better comply with engine create/destroy open/close routine git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1144 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 7d61bdeba92adcfed677619b7c92a203a8c4d94a Author: achaloyan Date: Fri Sep 25 16:10:18 2009 +0000 Finally applied changes to the plugins. Indicate stream capabilities upon channel creation and use decided codec descriptor on channel open git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1143 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 64aaee0d070a5589aa3ec162586acd78d023b343 Author: achaloyan Date: Fri Sep 25 15:24:01 2009 +0000 Modified processing of offer. 1. Receive offer 2. Create control channels and media terminations indicating their capabilities 3. Build media topology and media path (all the descriptors are initialized at this stage) 4. Only then open control channels 5. Send answer git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1142 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 5a35d4c20ce3f67c8cd2a6ed1440817c6d5a1f1f Author: achaloyan Date: Fri Sep 25 14:31:39 2009 +0000 Added is_open member to mrcp_engine_channel_t git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1141 f001bc3a-424a-0410-80a0-a715b8f413a8 commit ff764c523b9a60bb6de243e928d1a399f2ed7e6a Author: achaloyan Date: Fri Sep 25 14:10:01 2009 +0000 Building RTP termination capabilities according to associated media termination(s) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1140 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 6b85a37e65c2600e317440d539968a794b2212f5 Author: achaloyan Date: Thu Sep 24 21:06:54 2009 +0000 Updated demo speech file naming convention (demo-8kHz and demo-16kHz) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1139 f001bc3a-424a-0410-80a0-a715b8f413a8 commit da1d688fac7022152ea5962fa36420a9f6e049a6 Author: achaloyan Date: Thu Sep 24 20:50:27 2009 +0000 Added/renamed 8 kHz and 16 kHz speech to be used from demos git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1138 f001bc3a-424a-0410-80a0-a715b8f413a8 commit c54d354c1de2a273b2e5c4eeb83c75d893a4cbe5 Author: achaloyan Date: Thu Sep 24 19:55:12 2009 +0000 Added new function to the client API to create media termination (audio stream) based on specified capabilities. mrcp_application_audio_termination_create() Marked the following functions as deprecated (they still remain functional though) mrcp_application_source_termination_create() mrcp_application_sink_termination_create() Upgraded demo synth and recog applications according git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1137 f001bc3a-424a-0410-80a0-a715b8f413a8 commit a8de40f600fe129b6367af435c47e80a3f1d52d6 Author: achaloyan Date: Thu Sep 24 19:10:02 2009 +0000 Added new function for the plugins to create media termination (audio stream) based on specified capabilities. mrcp_engine_audio_termination_create() Marked the following functions as deprecated (they still remain functional though) mrcp_engine_source_channel_create() mrcp_engine_sink_channel_create() Upgraded Flite plugin according git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1136 f001bc3a-424a-0410-80a0-a715b8f413a8 commit a2f6fbc628717b4f927eed5fbda64dd47027bc9b Author: achaloyan Date: Wed Sep 23 16:59:32 2009 +0000 Dropped any further requests from application if session is being terminated. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1135 f001bc3a-424a-0410-80a0-a715b8f413a8 commit d27e77a39c48baa110bb96524e170ff2a6419bbc Author: achaloyan Date: Wed Sep 23 15:15:50 2009 +0000 Modified includes to #include what is actually needed git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1134 f001bc3a-424a-0410-80a0-a715b8f413a8 commit ede4c291d16d5fe8783e3300b2eda71137fb095a Author: achaloyan Date: Wed Sep 23 14:51:44 2009 +0000 Reviewed mpf_codec_t and mpf_stream_t entities and relationship between them. Codec as a manipulator (encode,decode,dissect) is not a property of stream. Stream holds capabilities and active codec descriptor(s). Actual codecs (PCMU, PCMA, L16) are registered at startup and passed to encoder and decoder objects when/where needed. Linear PCM (LPCM, but not L16) is just an internal descriptor, it's not a codec. Everything should work as is basis. The only user level interface change is in stream_open() method, which is optional and probably is not actually used. See demos for more info. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1133 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 502ed670a75f8a94c017fbd3525bbeb928d72ac9 Author: achaloyan Date: Tue Sep 22 16:55:40 2009 +0000 Considered own capabilities while building local descriptor (codec list), added more utility functions git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1132 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 9cc077274f2333a8c1c2ed01d28d396c0ba95992 Author: achaloyan Date: Mon Sep 21 15:38:56 2009 +0000 Further enhanced stream, codec capabilities and descriptors. Finally those capabilities will participate in offer/answer, a few steps remain. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1131 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 9ddb49bd1a1dc51c4510abee8f1a092a6bce5dab Author: achaloyan Date: Sat Sep 19 11:53:16 2009 +0000 Using APR_ARRAY_IDX and APR_ARRAY_PUSH convenient macros to operate on APR arrays. These macros are more safe and intuitive. Unfortunately they have been added only in APR-1.3. If you still use earlier APR-1.2.x versions, consider to backport those macros, please. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1130 f001bc3a-424a-0410-80a0-a715b8f413a8 commit c38a54eb3b65b45e8d5265144864b6419dc4824d Author: achaloyan Date: Thu Sep 17 16:57:49 2009 +0000 Renamed mpf_stream_mode_e to mpf_stream_direction_e (send,receive or duplex), moved mpf_stream_capabilities_t to mpf_stream_descriptor.h Nothing has to be done, unless you explicitly construct and use mpf_stream_mode_e in your apps. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1129 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 28ca2617a6f85face3e7eb7a2fbe3d300b9199c6 Author: achaloyan Date: Thu Sep 17 16:44:53 2009 +0000 Fixed last commit git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1128 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 450154c862b567f8b24b30e0662ad16eda6f9855 Author: achaloyan Date: Thu Sep 17 16:25:25 2009 +0000 Minor fix in format of vcproj version 8,00 -> 8.00 git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1127 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 1cf803d3696d62523f6084c4ac88a528c0bd9eaa Author: achaloyan Date: Wed Sep 16 20:59:41 2009 +0000 Destroy resource engines on server destroy git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1126 f001bc3a-424a-0410-80a0-a715b8f413a8 commit b08782eb2d6a4a87a0a70dc7d276957dab906223 Author: achaloyan Date: Wed Sep 16 20:40:17 2009 +0000 Added is_open state to resource engines (Issue-42) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1125 f001bc3a-424a-0410-80a0-a715b8f413a8 commit df037667c94449091144f36347bbb4c6dbdc1168 Author: achaloyan Date: Wed Sep 16 16:15:36 2009 +0000 Unregister plugins at later stage from mrcp_server_destroy(). It makes no difference for now, but from conceptual view point, it should be possible to create server once, start/stop it multiple times if needed and finally destroy it once. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1124 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 1336d1b985329b32fca0c8127f3c3bd4785f9412 Author: achaloyan Date: Wed Sep 16 13:10:50 2009 +0000 Reset existing associations and topology (if any), while processing answer from server (re-Invites). git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1123 f001bc3a-424a-0410-80a0-a715b8f413a8 commit d83f12e3262f156bb10bb55ab5bf9407afb110b2 Author: achaloyan Date: Mon Sep 14 19:12:42 2009 +0000 Fixed spelling: transmit -> transmitter, receive -> receiver git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1122 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 67894e96a9c8b5db7652c8a2a34b5eecd88aa1d7 Author: achaloyan Date: Mon Sep 14 17:11:03 2009 +0000 Added media path traces Examples: Source->[PCMU/8000/1]->Decoder->[LPCM/8000/1]->Bridge->[LPCM/8000/1]->Sink Source->[LPCM/8000/1]->Bridge->[LPCM/8000/1]->Encoder->[PCMU/8000/1]->Sink git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1121 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 35cd98bce0d462c10dfbc8beb5369e12f3de55a3 Author: achaloyan Date: Sat Sep 12 17:58:23 2009 +0000 Merged mpf_media_descriptor with mpf_rtp_media_descriptor (no need in separate base descriptor) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1120 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 689df477413bef09cda1fc2f0ba6c14f6378e088 Author: achaloyan Date: Sat Sep 12 17:05:31 2009 +0000 Using apr types (short -> apr_int16_t) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1119 f001bc3a-424a-0410-80a0-a715b8f413a8 commit c578f6ea274229afee64a4aa6faf0b0610049153 Author: achaloyan Date: Sat Sep 12 16:50:10 2009 +0000 Fixed compilation warnings under gcc git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1118 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 8499d8cbc4cdadd07b60d72a5811e21b48da6f0f Author: achaloyan Date: Sat Sep 12 16:16:08 2009 +0000 Added MPF object derived new entities such as mixer (n-sources, 1-sink) and multiplier (1-source, n-sinks) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1117 f001bc3a-424a-0410-80a0-a715b8f413a8 commit a957416aa7e536c5616d9c58725b82689bce58a0 Author: achaloyan Date: Thu Sep 10 18:47:32 2009 +0000 Added new line at the end of the file to fix compilation warning git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1116 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 3fd96c529abd99b1a4b398481e72541448c48783 Author: achaloyan Date: Thu Sep 10 16:27:14 2009 +0000 Encapsulated creation of encoder, decoder and resampler in mpf_bridge. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1115 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 4b169b670e95adec16741c2876d18f90a72fd95d Author: achaloyan Date: Thu Sep 10 16:05:25 2009 +0000 Added skeleton for MPF resampler. Eventually external resampler should be integrated there. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1114 f001bc3a-424a-0410-80a0-a715b8f413a8 commit aa8507f6c0982ae45cdba3f44fd52fcf7ad9e372 Author: achaloyan Date: Wed Sep 9 20:39:25 2009 +0000 Removed unused function git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1113 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 2e533dbc7a24c24e03a0e2810d78b3eaad13efb6 Author: achaloyan Date: Wed Sep 9 20:32:46 2009 +0000 Supported multiple "cmid" attributes per each control m-line. (Section 4.3) Client API still allows to define only one to one association between control channels and their corresponding media terminations. Meantime server should be flexible enough even now and support any offered association among control channels and media terminations. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1112 f001bc3a-424a-0410-80a0-a715b8f413a8 commit e8c77db43f8c6a39f912e2b60e594c753b2b3182 Author: achaloyan Date: Wed Sep 9 18:45:41 2009 +0000 Fixed issue-41. Port number must be matched too, while trying to find an existing MRCPv2 connection (Thanks asackheim) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1111 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 3594fd3d2f5a3ba5ae91a885fb09b3d9322bbffd Author: achaloyan Date: Wed Sep 9 16:57:49 2009 +0000 Fixed race condition issue in pocketsphinx plugin (Thanks Alban) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1110 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 202b654d75f5625bfdfd00adc61bb0fab000ab42 Author: achaloyan Date: Tue Sep 8 15:51:58 2009 +0000 Added define for TCP discard port used in offer/answer git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1109 f001bc3a-424a-0410-80a0-a715b8f413a8 commit a0d61fd4ea2b0e8c1899b1420fc24c1f2515afa7 Author: achaloyan Date: Tue Sep 8 13:56:51 2009 +0000 Reworked MPF context, which allows to provide custom associations among the terminations involved in the context and build topology based on association matrix. Client and server stacks have been modified according. Everything should work as is basis git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1108 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 54470cc5cdb35169409fe145c69046b3ad5a0c27 Author: achaloyan Date: Tue Sep 8 13:50:01 2009 +0000 Check if session->connection is a valid pointer first and only then use it git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1107 f001bc3a-424a-0410-80a0-a715b8f413a8 commit d33217a9227ea3ecb1b3e2e360b148ece5b6be0d Author: achaloyan Date: Thu Sep 3 19:23:10 2009 +0000 Added more MPF commands to operate (add/modify/subtract) on terminations, add/remove associations between terminations and apply/destroy topology. More to come... git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1106 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 19984cd1e5229910d9392588cba587895b05b50f Author: achaloyan Date: Thu Sep 3 17:01:10 2009 +0000 Using apr_ring to store factory of media contexts. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1105 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 93c65ccde10d8eaa2eeec1b453c820af93b28ff2 Author: achaloyan Date: Wed Sep 2 18:43:31 2009 +0000 Moved MPF context related public interface from mpf_user.h to mpf_engine.h git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1104 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 99d7100026ba6867daf5486c29c514b0deaa2c75 Author: achaloyan Date: Wed Sep 2 14:59:15 2009 +0000 Removed unused function git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1103 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 6f21c53584b73e79ced6dbc695178a02611cce9d Author: achaloyan Date: Wed Sep 2 13:20:14 2009 +0000 Added ability to send more than one MPF messages at once. Grouping of MPF messages allows to reduce thread context switches, as a result, improves the performance. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1102 f001bc3a-424a-0410-80a0-a715b8f413a8 commit b2a72d24996acf67b29b8aabfed3850da2b27ef7 Author: achaloyan Date: Mon Aug 31 17:14:05 2009 +0000 Updated Makefile git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1101 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 18647d833d4df8991bee0b1a8255c7359502e8c7 Author: achaloyan Date: Mon Aug 31 17:04:09 2009 +0000 Added stream capabilities in order to set not only single codec descriptor, but capabilities of the stream (no API changes ... yet) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1100 f001bc3a-424a-0410-80a0-a715b8f413a8 commit ac6fe620c6d0f45ad5f7f43e04db124f2e0db69b Author: achaloyan Date: Fri Aug 28 13:10:39 2009 +0000 Added/moved negotiated rx/tx event descriptor to mpf_stream base next to rx/tx codecs git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1099 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 481f01cefa540b3903aa6635b833bb6e894a300d Author: achaloyan Date: Fri Aug 28 13:08:46 2009 +0000 Name of the attribute should be bits_per_sample git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1098 f001bc3a-424a-0410-80a0-a715b8f413a8 commit e22100eeb324677d214c07a3ef91504fc6c106de Author: achaloyan Date: Mon Aug 24 18:00:43 2009 +0000 Added Makefile targets for pocketsphinx plugin to install dictionary and model. Do nothing if installed, but not source directory of pocketsphinx is used at ./configure time. In this case, dictionary and model have to be copied manually. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1097 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 3c680289d19481ae8e62009057ef7d639fd14a00 Author: achaloyan Date: Mon Aug 24 17:41:29 2009 +0000 Fixed compilation warnings in pocketsphinx plugin, while compiling with enabled mainatainer mode git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1096 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 5d6c7e26cc6a98f59da72676aae9fd1d0d17bb9b Author: achaloyan Date: Mon Aug 24 15:15:44 2009 +0000 Fixed prototype of on_start and on_terminate handlers in Flite plugin git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1095 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 1d3044c8137ec39b3b7dcf65f8ec5e10718c7dd3 Author: achaloyan Date: Tue Aug 18 16:41:06 2009 +0000 Fixed trailing space in MRCPv2 response-line (interoperability with Optimsys, Thanks Roger) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1094 f001bc3a-424a-0410-80a0-a715b8f413a8 commit e835d5e8e51d19932a2e3e3f808ec987e2901de4 Author: achaloyan Date: Tue Aug 18 13:26:50 2009 +0000 Fixed possible access violation in APT logger (Issue-40, Thanks Vali) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1093 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 2e35e28c31d4a79025d105b00c73a9d66019d144 Author: achaloyan Date: Mon Aug 17 18:31:48 2009 +0000 Added L16 and telephone-event to yet hard coded capabilities of the server included into the response to resource discovery request git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1092 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 1fa878e171b528293528566f244c5142c051ac3a Author: achaloyan Date: Mon Aug 17 18:17:07 2009 +0000 Fixed the response message sent to RTSP DESCRIBE request. It shouldn't contain RTSP Transport header, while RTSP Transport can be present only in RTSP SETUP request/response messages. This hopefully fixes interoperability with GVP (Issue-39, Thanks Vali). git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1091 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 4e3d64c778fb3cc6a0a072d898f43b3c37e80eba Author: achaloyan Date: Sun Aug 16 06:49:54 2009 +0000 Applied patch, which implements utility functions to transform DTMF characters and event identifiers (Thanks Vali) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1090 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 041580394f2ecdfe920cdb1e3d3d16ebceaecdbd Author: achaloyan Date: Sun Aug 16 06:39:45 2009 +0000 Applied patch, which basically implements RFC4733/RFC2833 sender procedure, yet to be enhanced, see Issue-31 (Thanks Chaitanya) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1089 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 947b49f9ff8e353d6bb5985f00db14942db971db Author: achaloyan Date: Sun Aug 16 06:28:22 2009 +0000 Set negotiated payload type of named event for RTP transmit and receive git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1088 f001bc3a-424a-0410-80a0-a715b8f413a8 commit dc2687129335089a513127405e968b749197e002 Author: achaloyan Date: Sat Aug 15 17:47:13 2009 +0000 Added "telephone-event/101/8000" to default client and server config git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1087 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 178ba987d2f89ae5b9d6684be46814014fb3df2e Author: achaloyan Date: Sat Aug 15 17:36:24 2009 +0000 Added generation of a=fmtp SDP media level attribute, which is optional for generic codecs, but is required for telephone-event (a=fmtp:100 0-15) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1086 f001bc3a-424a-0410-80a0-a715b8f413a8 commit bfcc86e8e93fca16543e723a54132b74f3082216 Author: achaloyan Date: Sat Aug 15 16:38:46 2009 +0000 Set SOATAG_AUDIO_AUX("telephone-event") in nua_respond to enable auxiliary codecs in response. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1085 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 1287b89a349dce746b9f11f927e31b3e52b4bd9a Author: achaloyan Date: Sat Aug 15 16:33:37 2009 +0000 Integrated named events into offer/answer, made codec descriptors related minor enhancements git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1084 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 98e9b1971e3b28fa3865ff77c3865c3e6d76d305 Author: achaloyan Date: Sat Aug 15 12:26:23 2009 +0000 Moved matching of codec capabilities into separate function git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1083 f001bc3a-424a-0410-80a0-a715b8f413a8 commit e6f975d55fdd89d415548bcd63ba7756dcc93b8a Author: achaloyan Date: Thu Aug 13 18:32:21 2009 +0000 Added codec descriptor of named event into codec manager to load from config (done) and further to participate in offer/answer (yet to be done) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1082 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 6418912f7b96acce0e14feb474d106b8ac834cb8 Author: achaloyan Date: Thu Aug 13 16:37:04 2009 +0000 Moved named event definition into mpf_named_event.h, more stuff should be added there. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1081 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 9cc4da5d75f1fca118a5b962ee9e8c5d20c7a616 Author: achaloyan Date: Tue Aug 11 18:21:02 2009 +0000 Switched libasrclient into dll in order not to expose internal stuff to application context git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1080 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 85480c95b03f727dbd11618fb42ae3ac907eddc0 Author: achaloyan Date: Mon Aug 10 07:59:54 2009 +0000 Added plugin/engine config. The structure consists of - parameters which are common for all engines such as engine name and max channel count - table of transparent name/value string parameters, which are engine dependent git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1079 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 5dfe8442ded69b773e83cb9c43b9197213ee5654 Author: achaloyan Date: Mon Aug 10 05:40:10 2009 +0000 Initialized codec manager with 4 instead of 3 codecs to avoid later reallocations, as we actually have 4 codecs now git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1078 f001bc3a-424a-0410-80a0-a715b8f413a8 commit cb9d6d1406d4e5eb418c8a6de04eae004998a2f9 Author: achaloyan Date: Mon Aug 10 05:36:41 2009 +0000 Fixed typo in comments (defualt -> default) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1077 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 00971c82e72adde2221fdbc204571a7c7a781e5a Author: achaloyan Date: Wed Aug 5 07:57:20 2009 +0000 Added ability to get MRCP version of the channel from plugin context in order to be able to construct MRCP version dependent responses and events when/if needed. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1076 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 328ebb30c8eb5124326bf5a43f4fa588877bea9d Author: achaloyan Date: Wed Aug 5 05:45:31 2009 +0000 Set RTSP transport "mode" attribute in the outgoing RTSP response if it's specified in the received RTSP request git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1075 f001bc3a-424a-0410-80a0-a715b8f413a8 commit c538367f46ef48cf157af53431ace47c6ac2be88 Author: achaloyan Date: Wed Aug 5 05:42:32 2009 +0000 Added another RTSP transport attribute (mode=) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1074 f001bc3a-424a-0410-80a0-a715b8f413a8 commit a8a7ac5d7dc2845e1294d726fc8d651b9b749472 Author: achaloyan Date: Mon Aug 3 19:05:17 2009 +0000 Added ability to limit max number of engine channels in use. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1073 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 43bdbd881e763c33e1cb6fff9ac88c91fe038d07 Author: achaloyan Date: Sat Aug 1 14:44:31 2009 +0000 Added helper function to get session MRCP version git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1072 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 7d53c0d31b12122a2ea4a200f3390ef0c78b392e Author: achaloyan Date: Sat Aug 1 12:54:34 2009 +0000 Added enumeration of RTP payload types (RFC3551) project uses git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1071 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 666f5657bf8bcd2f149119d07c725ff9599b2759 Author: achaloyan Date: Thu Jul 30 13:39:11 2009 +0000 Added svn props git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1070 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 996b0d2489b377818290f7527080cbbfb8229bdc Author: achaloyan Date: Thu Jul 30 13:36:44 2009 +0000 Updated GNU build git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1069 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 53d8fe5f0fd4379c2ea626c861f9a031c9d88349 Author: achaloyan Date: Thu Jul 30 13:21:52 2009 +0000 Separated libasrclient library from asrclient application to server as a reusable basic ASR block on top of UniMRCP client stack. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1068 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 8e6b678bd662127240546f95e8bf8c3a68033090 Author: achaloyan Date: Tue Jul 28 10:36:40 2009 +0000 Added Michael Jerris (OS X build, FreeSWITCH build integration) and Carlos Pina Soares (RTP port management and several essential issue reports) to the project contributors git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1067 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 25f0b60b56d7ed3ab11d1779615f2c2ba90dd686 Author: achaloyan Date: Fri Jul 24 16:33:17 2009 +0000 Included asrclient into GNU build git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1066 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 316c3331b67a3504461bbf597ed3ff95c44bd053 Author: achaloyan Date: Fri Jul 24 16:23:34 2009 +0000 Introduced an alternate demo ASR client application usage: run [grammar_file] [audio_input_file] [profile_name] examples: run run grammar.xml one.pcm run grammar.xml one.pcm MRCPv1-Default git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1065 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 04a3b14edb3e68743df4a4b22ca5445a54c79f9a Author: achaloyan Date: Wed Jul 22 19:18:21 2009 +0000 Increased unimrcp version number git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1063 f001bc3a-424a-0410-80a0-a715b8f413a8 commit d4b7ddc381ff0d2fb6831be473c84cd6c1da78ef Author: achaloyan Date: Wed Jul 22 18:39:58 2009 +0000 Fixed doxygen warnings git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1062 f001bc3a-424a-0410-80a0-a715b8f413a8 commit da304aa5485b2252a2826355c870108c52eac4bd Author: achaloyan Date: Wed Jul 22 18:30:08 2009 +0000 Fixed doxygen warnings git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1061 f001bc3a-424a-0410-80a0-a715b8f413a8 commit e6db110edb0a199298db5a0bdd08134543df40d7 Author: achaloyan Date: Wed Jul 22 18:23:34 2009 +0000 Fixed doxygen warnings git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1060 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 5b29de76a653423a02ec9329aec56429959d40c1 Author: achaloyan Date: Wed Jul 22 17:52:11 2009 +0000 Updated INSTALL instructions git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1059 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 8882ecf7b8dea6959d096edecb371c213294f25d Author: achaloyan Date: Wed Jul 22 17:13:29 2009 +0000 APR-Iconv is not included in dependency pack. Thus, first check if apr-iconv.dll exists and only then try to copy it. git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1057 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 1997ed691507b5e0fdf58c3b22ce04c7a9c4fead Author: achaloyan Date: Wed Jul 22 16:17:57 2009 +0000 From now on use thread safe APR pools. Consider using the dependency package from http://code.google.com/p/unimrcp/downloads/list or manually apply patches over the libraries you use from http://www.unimrcp.org/dependencies git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1056 f001bc3a-424a-0410-80a0-a715b8f413a8 commit 5cbdaa7304b6146ff002191b7142885a18c8cf0e Author: achaloyan Date: Fri Jul 17 13:48:27 2009 +0000 Raised unexpected MRCPv2 disconnect event from transport layer to client and server stacks (Issue-36) git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1055 f001bc3a-424a-0410-80a0-a715b8f413a8 git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@15580 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- libs/unimrcp/AUTHORS | 12 +- libs/unimrcp/INSTALL | 41 +- libs/unimrcp/Makefile.am | 2 +- libs/unimrcp/build/tools/prepare.vcproj | 4 +- libs/unimrcp/build/uni_version.h | 2 +- .../build/vsprops/unimrcpserver.vsprops | 2 +- libs/unimrcp/conf/umcscenarios.xml | 96 ++ libs/unimrcp/conf/unimrcpclient.xml | 23 +- libs/unimrcp/conf/unimrcpserver.xml | 24 +- libs/unimrcp/configure.ac | 17 +- libs/unimrcp/data/demo-16kHz.pcm | Bin 0 -> 115200 bytes libs/unimrcp/data/{demo.pcm => demo-8kHz.pcm} | Bin libs/unimrcp/data/grammar.jsgf | 3 + libs/unimrcp/data/one-16kHz.pcm | Bin 0 -> 64508 bytes libs/unimrcp/data/{one.pcm => one-8kHz.pcm} | Bin libs/unimrcp/data/speak.txt | 1 + .../apr-toolkit/include/apt_cyclic_queue.h | 1 + .../libs/apr-toolkit/include/apt_log.h | 6 +- .../libs/apr-toolkit/include/apt_net.h | 8 + .../libs/apr-toolkit/include/apt_nlsml_doc.h | 8 +- .../libs/apr-toolkit/include/apt_pool.h | 2 +- .../libs/apr-toolkit/include/apt_task.h | 17 +- .../libs/apr-toolkit/include/apt_test_suite.h | 2 +- .../libs/apr-toolkit/src/apt_consumer_task.c | 2 + libs/unimrcp/libs/apr-toolkit/src/apt_log.c | 2 +- libs/unimrcp/libs/apr-toolkit/src/apt_net.c | 17 + .../apr-toolkit/src/apt_net_client_task.c | 4 + .../apr-toolkit/src/apt_net_server_task.c | 4 + libs/unimrcp/libs/apr-toolkit/src/apt_pair.c | 6 +- libs/unimrcp/libs/apr-toolkit/src/apt_pool.c | 3 +- libs/unimrcp/libs/apr-toolkit/src/apt_task.c | 25 +- libs/unimrcp/libs/mpf/Makefile.am | 29 +- .../libs/mpf/include/mpf_activity_detector.h | 9 +- .../mpf/include/mpf_audio_file_descriptor.h | 23 +- libs/unimrcp/libs/mpf/include/mpf_bridge.h | 15 +- libs/unimrcp/libs/mpf/include/mpf_codec.h | 14 +- .../libs/mpf/include/mpf_codec_descriptor.h | 157 ++- .../libs/mpf/include/mpf_codec_manager.h | 3 - libs/unimrcp/libs/mpf/include/mpf_context.h | 95 +- libs/unimrcp/libs/mpf/include/mpf_decoder.h | 3 +- .../libs/mpf/include/mpf_dtmf_detector.h | 121 ++ .../libs/mpf/include/mpf_dtmf_generator.h | 131 ++ libs/unimrcp/libs/mpf/include/mpf_encoder.h | 3 +- libs/unimrcp/libs/mpf/include/mpf_engine.h | 85 +- .../include/mpf_file_termination_factory.h | 2 +- libs/unimrcp/libs/mpf/include/mpf_frame.h | 39 +- .../libs/mpf/include/mpf_jitter_buffer.h | 6 +- .../libs/mpf/include/mpf_media_descriptor.h | 66 - libs/unimrcp/libs/mpf/include/mpf_message.h | 29 +- libs/unimrcp/libs/mpf/include/mpf_mixer.h | 47 + .../unimrcp/libs/mpf/include/mpf_multiplier.h | 47 + .../libs/mpf/include/mpf_named_event.h | 71 ++ libs/unimrcp/libs/mpf/include/mpf_object.h | 46 +- .../include/{mpf_timer.h => mpf_resampler.h} | 30 +- .../libs/mpf/include/mpf_rtcp_packet.h | 200 +++ .../libs/mpf/include/mpf_rtp_attribs.h | 7 +- libs/unimrcp/libs/mpf/include/mpf_rtp_defs.h | 63 +- .../libs/mpf/include/mpf_rtp_descriptor.h | 81 +- .../unimrcp/libs/mpf/include/mpf_rtp_header.h | 5 +- libs/unimrcp/libs/mpf/include/mpf_rtp_pt.h | 44 + libs/unimrcp/libs/mpf/include/mpf_rtp_stat.h | 82 +- .../unimrcp/libs/mpf/include/mpf_rtp_stream.h | 12 + .../mpf/include/mpf_rtp_termination_factory.h | 3 +- libs/unimrcp/libs/mpf/include/mpf_scheduler.h | 61 + libs/unimrcp/libs/mpf/include/mpf_stream.h | 82 +- .../libs/mpf/include/mpf_stream_descriptor.h | 88 ++ .../libs/mpf/include/mpf_stream_mode.h | 53 - .../libs/mpf/include/mpf_termination.h | 77 +- .../mpf/include/mpf_termination_factory.h | 88 ++ .../libs/mpf/include/mpf_timer_manager.h | 53 + libs/unimrcp/libs/mpf/include/mpf_types.h | 19 +- libs/unimrcp/libs/mpf/include/mpf_user.h | 52 - libs/unimrcp/libs/mpf/mpf.vcproj | 90 +- .../libs/mpf/src/mpf_activity_detector.c | 33 +- .../libs/mpf/src/mpf_audio_file_stream.c | 30 +- libs/unimrcp/libs/mpf/src/mpf_bridge.c | 173 ++- .../libs/mpf/src/mpf_codec_descriptor.c | 249 +++- libs/unimrcp/libs/mpf/src/mpf_codec_g711.c | 33 +- libs/unimrcp/libs/mpf/src/mpf_codec_linear.c | 47 +- libs/unimrcp/libs/mpf/src/mpf_codec_manager.c | 104 +- libs/unimrcp/libs/mpf/src/mpf_context.c | 603 ++++++--- libs/unimrcp/libs/mpf/src/mpf_decoder.c | 56 +- libs/unimrcp/libs/mpf/src/mpf_dtmf_detector.c | 299 +++++ .../unimrcp/libs/mpf/src/mpf_dtmf_generator.c | 347 ++++++ libs/unimrcp/libs/mpf/src/mpf_encoder.c | 53 +- libs/unimrcp/libs/mpf/src/mpf_engine.c | 325 +++-- .../mpf/src/mpf_file_termination_factory.c | 29 +- libs/unimrcp/libs/mpf/src/mpf_frame_buffer.c | 10 +- libs/unimrcp/libs/mpf/src/mpf_jitter_buffer.c | 185 ++- libs/unimrcp/libs/mpf/src/mpf_mixer.c | 205 ++++ libs/unimrcp/libs/mpf/src/mpf_multiplier.c | 182 +++ libs/unimrcp/libs/mpf/src/mpf_named_event.c | 74 ++ libs/unimrcp/libs/mpf/src/mpf_resampler.c | 26 + libs/unimrcp/libs/mpf/src/mpf_rtp_attribs.c | 8 +- libs/unimrcp/libs/mpf/src/mpf_rtp_stream.c | 1074 +++++++++++++---- .../mpf/src/mpf_rtp_termination_factory.c | 36 +- libs/unimrcp/libs/mpf/src/mpf_scheduler.c | 250 ++++ libs/unimrcp/libs/mpf/src/mpf_stream.c | 158 +++ libs/unimrcp/libs/mpf/src/mpf_termination.c | 62 +- .../libs/mpf/src/mpf_termination_factory.c | 66 + libs/unimrcp/libs/mpf/src/mpf_timer.c | 154 --- libs/unimrcp/libs/mpf/src/mpf_timer_manager.c | 188 +++ .../mrcp-client/include/mrcp_application.h | 56 +- .../mrcp-client/include/mrcp_client_session.h | 68 +- .../libs/mrcp-client/src/mrcp_client.c | 202 +++- .../mrcp-client/src/mrcp_client_session.c | 701 ++++++----- libs/unimrcp/libs/mrcp-engine/Makefile.am | 24 +- .../mrcp-engine/include/mrcp_engine_factory.h | 57 + .../mrcp-engine/include/mrcp_engine_iface.h | 93 ++ .../mrcp-engine/include/mrcp_engine_impl.h | 121 ++ .../mrcp-engine/include/mrcp_engine_loader.h | 48 + ...resource_plugin.h => mrcp_engine_plugin.h} | 32 +- .../mrcp-engine/include/mrcp_engine_types.h | 141 +++ .../mrcp-engine/include/mrcp_recog_engine.h | 33 + .../include/mrcp_recog_state_machine.h | 7 +- .../include/mrcp_recorder_engine.h | 33 + .../include/mrcp_recorder_state_machine.h} | 17 +- .../include/mrcp_resource_engine.h | 198 +-- .../include/mrcp_state_machine.h | 0 .../mrcp-engine/include/mrcp_synth_engine.h | 33 + .../include/mrcp_synth_state_machine.h | 7 +- .../libs/mrcp-engine/mrcpengine.vcproj | 76 +- .../mrcp-engine/src/mrcp_engine_factory.c | 147 +++ .../libs/mrcp-engine/src/mrcp_engine_iface.c | 55 + .../libs/mrcp-engine/src/mrcp_engine_impl.c | 241 ++++ .../libs/mrcp-engine/src/mrcp_engine_loader.c | 164 +++ .../src/mrcp_recog_state_machine.c} | 67 +- .../src/mrcp_recorder_state_machine.c | 392 ++++++ .../mrcp-engine/src/mrcp_resource_engine.c | 151 --- .../src/mrcp_synth_state_machine.c} | 73 +- .../libs/mrcp-server/include/mrcp_server.h | 25 +- .../mrcp-server/include/mrcp_server_session.h | 52 +- .../libs/mrcp-server/src/mrcp_server.c | 292 ++--- .../mrcp-server/src/mrcp_server_session.c | 676 +++++++---- .../mrcp-signaling/include/mrcp_session.h | 3 + .../include/mrcp_session_descriptor.h | 21 +- libs/unimrcp/libs/mrcp/Makefile.am | 17 +- .../libs/mrcp/control/include/mrcp_resource.h | 48 +- .../control/include/mrcp_resource_factory.h | 20 +- .../control/include/mrcp_resource_loader.h | 51 + .../mrcp/control/src/mrcp_resource_factory.c | 84 +- .../mrcp/control/src/mrcp_resource_loader.c | 133 ++ .../libs/mrcp/control/src/mrcp_stream.c | 20 +- libs/unimrcp/libs/mrcp/include/mrcp_types.h | 27 +- .../libs/mrcp/message/include/mrcp_message.h | 103 +- .../mrcp/message/include/mrcp_start_line.h | 114 ++ .../mrcp/message/src/mrcp_generic_header.c | 6 +- .../mrcp/message/src/mrcp_header_accessor.c | 30 +- .../libs/mrcp/message/src/mrcp_message.c | 546 ++------- .../libs/mrcp/message/src/mrcp_start_line.c | 444 +++++++ libs/unimrcp/libs/mrcp/mrcp.vcproj | 44 +- .../resources/include/mrcp_recog_header.h | 2 +- .../resources/include/mrcp_recorder_header.h | 136 +++ .../include/mrcp_recorder_resource.h | 53 + .../resources/include/mrcp_synth_header.h | 2 +- .../mrcp/resources/src/mrcp_default_factory.c | 57 - .../src/mrcp_recog_client_state_machine.c | 36 - .../mrcp/resources/src/mrcp_recog_header.c | 2 +- .../mrcp/resources/src/mrcp_recog_resource.c | 72 +- .../mrcp/resources/src/mrcp_recorder_header.c | 299 +++++ .../resources/src/mrcp_recorder_resource.c | 57 + .../src/mrcp_synth_client_state_machine.c | 36 - .../mrcp/resources/src/mrcp_synth_header.c | 2 +- .../mrcp/resources/src/mrcp_synth_resource.c | 72 +- .../include/mrcp_connection.h | 4 +- .../include/mrcp_connection_types.h | 2 + .../include/mrcp_control_descriptor.h | 31 +- .../src/mrcp_client_connection.c | 9 +- .../mrcpv2-transport/src/mrcp_connection.c | 18 + .../src/mrcp_control_descriptor.c | 47 +- .../src/mrcp_server_connection.c | 16 +- .../libs/uni-rtsp/include/rtsp_header.h | 4 + libs/unimrcp/libs/uni-rtsp/src/rtsp_client.c | 10 +- libs/unimrcp/libs/uni-rtsp/src/rtsp_header.c | 41 +- libs/unimrcp/libs/uni-rtsp/src/rtsp_message.c | 13 +- libs/unimrcp/libs/uni-rtsp/src/rtsp_server.c | 89 +- .../modules/mrcp-sofiasip/src/mrcp_sdp.c | 95 +- .../src/mrcp_sofiasip_client_agent.c | 7 +- .../src/mrcp_sofiasip_server_agent.c | 13 +- .../mrcp-unirtsp/include/mrcp_unirtsp_sdp.h | 2 +- .../mrcp-unirtsp/src/mrcp_unirtsp_sdp.c | 82 +- .../src/mrcp_unirtsp_server_agent.c | 3 + libs/unimrcp/packages/inno-setup/setup.iss | 2 +- libs/unimrcp/platforms/Makefile.am | 4 +- libs/unimrcp/platforms/asr-client/Makefile.am | 24 + .../platforms/asr-client/asrclient.vcproj | 166 +++ libs/unimrcp/platforms/asr-client/src/main.c | 298 +++++ .../platforms/libasr-client/Makefile.am | 24 + .../libasr-client/include/asr_engine.h | 102 ++ .../libasr-client/libasrclient.vcproj | 173 +++ .../platforms/libasr-client/src/asr_engine.c | 586 +++++++++ .../platforms/libunimrcp-client/Makefile.am | 2 +- .../libunimrcp-client/src/unimrcp_client.c | 97 +- .../platforms/libunimrcp-server/Makefile.am | 2 +- .../libunimrcp-server/src/unimrcp_server.c | 126 +- libs/unimrcp/platforms/umc/Makefile.am | 34 + .../platforms/umc/include/dtmfscenario.h | 75 ++ .../platforms/umc/include/dtmfsession.h | 69 ++ .../platforms/umc/include/recogscenario.h | 88 ++ .../platforms/umc/include/recogsession.h | 73 ++ .../platforms/umc/include/recorderscenario.h | 66 + .../platforms/umc/include/recordersession.h | 69 ++ .../platforms/umc/include/synthscenario.h | 74 ++ .../platforms/umc/include/synthsession.h | 67 + .../platforms/umc/include/umcconsole.h | 58 + .../platforms/umc/include/umcframework.h | 95 ++ .../platforms/umc/include/umcscenario.h | 120 ++ .../platforms/umc/include/umcsession.h | 121 ++ .../platforms/umc/src/dtmfscenario.cpp | 77 ++ .../unimrcp/platforms/umc/src/dtmfsession.cpp | 326 +++++ libs/unimrcp/platforms/umc/src/main.cpp | 24 + .../platforms/umc/src/recogscenario.cpp | 113 ++ .../platforms/umc/src/recogsession.cpp | 450 +++++++ .../platforms/umc/src/recorderscenario.cpp | 70 ++ .../platforms/umc/src/recordersession.cpp | 298 +++++ .../platforms/umc/src/synthscenario.cpp | 80 ++ .../platforms/umc/src/synthsession.cpp | 261 ++++ libs/unimrcp/platforms/umc/src/umcconsole.cpp | 280 +++++ .../platforms/umc/src/umcframework.cpp | 605 ++++++++++ .../unimrcp/platforms/umc/src/umcscenario.cpp | 213 ++++ libs/unimrcp/platforms/umc/src/umcsession.cpp | 226 ++++ libs/unimrcp/platforms/umc/umc.vcproj | 261 ++++ .../src/demo_bypass_application.c | 14 +- .../src/demo_recog_application.c | 44 +- .../src/demo_synth_application.c | 43 +- .../platforms/unimrcp-client/src/demo_util.c | 9 +- libs/unimrcp/plugins/Makefile.am | 4 + .../demo-recog/src/demo_recog_engine.c | 123 +- .../demo-synth/src/demo_synth_engine.c | 95 +- .../plugins/mrcp-cepstral/src/mrcp_swift.c | 313 +++-- .../plugins/mrcp-flite/include/flite_voices.h | 4 + .../plugins/mrcp-flite/src/mrcp_flite.c | 124 +- .../plugins/mrcp-pocketsphinx/Makefile.am | 13 +- .../include/pocketsphinx_properties.h | 23 +- .../mrcp-pocketsphinx/src/mrcp_pocketsphinx.c | 299 +++-- .../src/pocketsphinx_properties.c | 1 + .../unimrcp/plugins/mrcp-recorder/Makefile.am | 16 + .../plugins/mrcp-recorder/mrcprecorder.vcproj | 165 +++ .../mrcp-recorder/src/mrcp_recorder_engine.c | 463 +++++++ libs/unimrcp/tests/mpftest/src/mpf_suite.c | 267 ++-- libs/unimrcp/tests/mrcptest/mrcptest.vcproj | 2 +- .../tests/mrcptest/src/parse_gen_suite.c | 13 +- .../tests/mrcptest/src/set_get_suite.c | 27 +- libs/unimrcp/tests/strtablegen/src/main.c | 1 + libs/unimrcp/unimrcp.sln | 58 +- 245 files changed, 18406 insertions(+), 4543 deletions(-) create mode 100644 libs/unimrcp/conf/umcscenarios.xml create mode 100644 libs/unimrcp/data/demo-16kHz.pcm rename libs/unimrcp/data/{demo.pcm => demo-8kHz.pcm} (100%) create mode 100644 libs/unimrcp/data/grammar.jsgf create mode 100644 libs/unimrcp/data/one-16kHz.pcm rename libs/unimrcp/data/{one.pcm => one-8kHz.pcm} (100%) create mode 100644 libs/unimrcp/data/speak.txt create mode 100644 libs/unimrcp/libs/mpf/include/mpf_dtmf_detector.h create mode 100644 libs/unimrcp/libs/mpf/include/mpf_dtmf_generator.h delete mode 100644 libs/unimrcp/libs/mpf/include/mpf_media_descriptor.h create mode 100644 libs/unimrcp/libs/mpf/include/mpf_mixer.h create mode 100644 libs/unimrcp/libs/mpf/include/mpf_multiplier.h create mode 100644 libs/unimrcp/libs/mpf/include/mpf_named_event.h rename libs/unimrcp/libs/mpf/include/{mpf_timer.h => mpf_resampler.h} (54%) create mode 100644 libs/unimrcp/libs/mpf/include/mpf_rtcp_packet.h create mode 100644 libs/unimrcp/libs/mpf/include/mpf_rtp_pt.h create mode 100644 libs/unimrcp/libs/mpf/include/mpf_scheduler.h create mode 100644 libs/unimrcp/libs/mpf/include/mpf_stream_descriptor.h delete mode 100644 libs/unimrcp/libs/mpf/include/mpf_stream_mode.h create mode 100644 libs/unimrcp/libs/mpf/include/mpf_termination_factory.h create mode 100644 libs/unimrcp/libs/mpf/include/mpf_timer_manager.h delete mode 100644 libs/unimrcp/libs/mpf/include/mpf_user.h create mode 100644 libs/unimrcp/libs/mpf/src/mpf_dtmf_detector.c create mode 100644 libs/unimrcp/libs/mpf/src/mpf_dtmf_generator.c create mode 100644 libs/unimrcp/libs/mpf/src/mpf_mixer.c create mode 100644 libs/unimrcp/libs/mpf/src/mpf_multiplier.c create mode 100644 libs/unimrcp/libs/mpf/src/mpf_named_event.c create mode 100644 libs/unimrcp/libs/mpf/src/mpf_resampler.c create mode 100644 libs/unimrcp/libs/mpf/src/mpf_scheduler.c create mode 100644 libs/unimrcp/libs/mpf/src/mpf_stream.c create mode 100644 libs/unimrcp/libs/mpf/src/mpf_termination_factory.c delete mode 100644 libs/unimrcp/libs/mpf/src/mpf_timer.c create mode 100644 libs/unimrcp/libs/mpf/src/mpf_timer_manager.c create mode 100644 libs/unimrcp/libs/mrcp-engine/include/mrcp_engine_factory.h create mode 100644 libs/unimrcp/libs/mrcp-engine/include/mrcp_engine_iface.h create mode 100644 libs/unimrcp/libs/mrcp-engine/include/mrcp_engine_impl.h create mode 100644 libs/unimrcp/libs/mrcp-engine/include/mrcp_engine_loader.h rename libs/unimrcp/libs/mrcp-engine/include/{mrcp_resource_plugin.h => mrcp_engine_plugin.h} (76%) create mode 100644 libs/unimrcp/libs/mrcp-engine/include/mrcp_engine_types.h create mode 100644 libs/unimrcp/libs/mrcp-engine/include/mrcp_recog_engine.h rename libs/unimrcp/libs/{mrcp/resources => mrcp-engine}/include/mrcp_recog_state_machine.h (72%) create mode 100644 libs/unimrcp/libs/mrcp-engine/include/mrcp_recorder_engine.h rename libs/unimrcp/libs/{mrcp/resources/include/mrcp_default_factory.h => mrcp-engine/include/mrcp_recorder_state_machine.h} (62%) rename libs/unimrcp/libs/{mrcp/control => mrcp-engine}/include/mrcp_state_machine.h (100%) create mode 100644 libs/unimrcp/libs/mrcp-engine/include/mrcp_synth_engine.h rename libs/unimrcp/libs/{mrcp/resources => mrcp-engine}/include/mrcp_synth_state_machine.h (72%) create mode 100644 libs/unimrcp/libs/mrcp-engine/src/mrcp_engine_factory.c create mode 100644 libs/unimrcp/libs/mrcp-engine/src/mrcp_engine_iface.c create mode 100644 libs/unimrcp/libs/mrcp-engine/src/mrcp_engine_impl.c create mode 100644 libs/unimrcp/libs/mrcp-engine/src/mrcp_engine_loader.c rename libs/unimrcp/libs/{mrcp/resources/src/mrcp_recog_server_state_machine.c => mrcp-engine/src/mrcp_recog_state_machine.c} (92%) create mode 100644 libs/unimrcp/libs/mrcp-engine/src/mrcp_recorder_state_machine.c delete mode 100644 libs/unimrcp/libs/mrcp-engine/src/mrcp_resource_engine.c rename libs/unimrcp/libs/{mrcp/resources/src/mrcp_synth_server_state_machine.c => mrcp-engine/src/mrcp_synth_state_machine.c} (91%) create mode 100644 libs/unimrcp/libs/mrcp/control/include/mrcp_resource_loader.h create mode 100644 libs/unimrcp/libs/mrcp/control/src/mrcp_resource_loader.c create mode 100644 libs/unimrcp/libs/mrcp/message/include/mrcp_start_line.h create mode 100644 libs/unimrcp/libs/mrcp/message/src/mrcp_start_line.c create mode 100644 libs/unimrcp/libs/mrcp/resources/include/mrcp_recorder_header.h create mode 100644 libs/unimrcp/libs/mrcp/resources/include/mrcp_recorder_resource.h delete mode 100644 libs/unimrcp/libs/mrcp/resources/src/mrcp_default_factory.c delete mode 100644 libs/unimrcp/libs/mrcp/resources/src/mrcp_recog_client_state_machine.c create mode 100644 libs/unimrcp/libs/mrcp/resources/src/mrcp_recorder_header.c create mode 100644 libs/unimrcp/libs/mrcp/resources/src/mrcp_recorder_resource.c delete mode 100644 libs/unimrcp/libs/mrcp/resources/src/mrcp_synth_client_state_machine.c create mode 100644 libs/unimrcp/platforms/asr-client/Makefile.am create mode 100644 libs/unimrcp/platforms/asr-client/asrclient.vcproj create mode 100644 libs/unimrcp/platforms/asr-client/src/main.c create mode 100644 libs/unimrcp/platforms/libasr-client/Makefile.am create mode 100644 libs/unimrcp/platforms/libasr-client/include/asr_engine.h create mode 100644 libs/unimrcp/platforms/libasr-client/libasrclient.vcproj create mode 100644 libs/unimrcp/platforms/libasr-client/src/asr_engine.c create mode 100644 libs/unimrcp/platforms/umc/Makefile.am create mode 100644 libs/unimrcp/platforms/umc/include/dtmfscenario.h create mode 100644 libs/unimrcp/platforms/umc/include/dtmfsession.h create mode 100644 libs/unimrcp/platforms/umc/include/recogscenario.h create mode 100644 libs/unimrcp/platforms/umc/include/recogsession.h create mode 100644 libs/unimrcp/platforms/umc/include/recorderscenario.h create mode 100644 libs/unimrcp/platforms/umc/include/recordersession.h create mode 100644 libs/unimrcp/platforms/umc/include/synthscenario.h create mode 100644 libs/unimrcp/platforms/umc/include/synthsession.h create mode 100644 libs/unimrcp/platforms/umc/include/umcconsole.h create mode 100644 libs/unimrcp/platforms/umc/include/umcframework.h create mode 100644 libs/unimrcp/platforms/umc/include/umcscenario.h create mode 100644 libs/unimrcp/platforms/umc/include/umcsession.h create mode 100644 libs/unimrcp/platforms/umc/src/dtmfscenario.cpp create mode 100644 libs/unimrcp/platforms/umc/src/dtmfsession.cpp create mode 100644 libs/unimrcp/platforms/umc/src/main.cpp create mode 100644 libs/unimrcp/platforms/umc/src/recogscenario.cpp create mode 100644 libs/unimrcp/platforms/umc/src/recogsession.cpp create mode 100644 libs/unimrcp/platforms/umc/src/recorderscenario.cpp create mode 100644 libs/unimrcp/platforms/umc/src/recordersession.cpp create mode 100644 libs/unimrcp/platforms/umc/src/synthscenario.cpp create mode 100644 libs/unimrcp/platforms/umc/src/synthsession.cpp create mode 100644 libs/unimrcp/platforms/umc/src/umcconsole.cpp create mode 100644 libs/unimrcp/platforms/umc/src/umcframework.cpp create mode 100644 libs/unimrcp/platforms/umc/src/umcscenario.cpp create mode 100644 libs/unimrcp/platforms/umc/src/umcsession.cpp create mode 100644 libs/unimrcp/platforms/umc/umc.vcproj create mode 100644 libs/unimrcp/plugins/mrcp-recorder/Makefile.am create mode 100644 libs/unimrcp/plugins/mrcp-recorder/mrcprecorder.vcproj create mode 100644 libs/unimrcp/plugins/mrcp-recorder/src/mrcp_recorder_engine.c 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 0000000000000000000000000000000000000000..01ace40952cb432a96473d1531a6c6328f6747a3 GIT binary patch literal 115200 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..44e0b3431a17b81373af5eb6fc6df6ff7528b58a GIT binary patch literal 64508 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

meF} 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 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}