From 5c343dbde70f25f961a28bc119f1e3382438734e Mon Sep 17 00:00:00 2001 From: Jeffrey C. Ollie Date: Dec 28 2007 21:44:29 +0000 Subject: Update to 1.4.16.2 --- diff --git a/.cvsignore b/.cvsignore index a114d2f..e674ae8 100644 --- a/.cvsignore +++ b/.cvsignore @@ -1 +1 @@ -asterisk-1.4.16.1-stripped.tar.gz +asterisk-1.4.16.2-stripped.tar.gz diff --git a/asterisk-1.4.16-alternate-extensions.patch b/asterisk-1.4.16-alternate-extensions.patch deleted file mode 100644 index 523ada8..0000000 --- a/asterisk-1.4.16-alternate-extensions.patch +++ /dev/null @@ -1,49 +0,0 @@ -From bf6bbce50cf7486e1627605c7808c5a74acb3c45 Mon Sep 17 00:00:00 2001 -From: Jeffrey C. Ollie -Date: Thu, 8 Nov 2007 16:32:56 -0600 -Subject: [PATCH] Allow alternate extensions to be specified in users.conf - ---- - pbx/pbx_config.c | 14 ++++++++++++++ - 1 files changed, 14 insertions(+), 0 deletions(-) - -diff --git a/pbx/pbx_config.c b/pbx/pbx_config.c -index 6d0ec2e..969c77a 100644 ---- a/pbx/pbx_config.c -+++ b/pbx/pbx_config.c -@@ -2357,11 +2357,14 @@ static void pbx_load_users(void) - { - struct ast_config *cfg; - char *cat, *chan; -+ char *ext; - const char *zapchan; - const char *hasexten; -+ const char *altexts; - char tmp[256]; - char iface[256]; - char zapcopy[256]; -+ char altcopy[256]; - char *c; - int len; - int hasvoicemail; -@@ -2441,6 +2444,17 @@ static void pbx_load_users(void) - } else { - ast_add_extension2(con, 0, cat, 1, NULL, NULL, "Dial", strdup("${HINT}"), ast_free, registrar); - } -+ altexts = ast_variable_retrieve(cfg, cat, "alternateexts"); -+ if (!ast_strlen_zero(altexts)) { -+ snprintf(tmp, sizeof(tmp), "%s|1", cat); -+ ast_copy_string(altcopy, altexts, sizeof(altcopy)); -+ c = altcopy; -+ ext = strsep(&c, ","); -+ while (ext) { -+ ast_add_extension2(con, 0, ext, 1, NULL, NULL, "Goto", strdup(tmp), ast_free, registrar); -+ ext = strsep(&c, ","); -+ } -+ } - } - } - ast_config_destroy(cfg); --- -1.5.3.6 - diff --git a/asterisk-1.4.16-alternate-voicemail.patch b/asterisk-1.4.16-alternate-voicemail.patch deleted file mode 100644 index 3c7a9ec..0000000 --- a/asterisk-1.4.16-alternate-voicemail.patch +++ /dev/null @@ -1,47 +0,0 @@ -From f8476ffc82e9e320890dd486572621c29e8c621d Mon Sep 17 00:00:00 2001 -From: Jeffrey C. Ollie -Date: Thu, 8 Nov 2007 15:52:36 -0600 -Subject: [PATCH] Modify modules.conf so that different voicemail modules can be loaded. - ---- - configs/modules.conf.sample | 27 +++++++++++++++++++++++++++ - 1 files changed, 27 insertions(+), 0 deletions(-) - -diff --git a/configs/modules.conf.sample b/configs/modules.conf.sample -index 0c92e1d..43c3f48 100644 ---- a/configs/modules.conf.sample -+++ b/configs/modules.conf.sample -@@ -36,3 +36,30 @@ load => res_musiconhold.so - ; - noload => chan_alsa.so - ;noload => chan_oss.so -+ -+; -+; Voicemail storage selection -+; -+; Comment out the "noload" lines for the voicemail -+; storage system that you want. Leave the ones that -+; you don't want uncommented. -+; -+ -+; -+; Voicemail with IMAP storage -+; -+noload => app_directory_imap.so -+noload => app_voicemail_imap.so -+ -+; -+; Voicemail with ODBC storage -+; -+noload => app_directory_odbc.so -+noload => app_voicemail_odbc.so -+ -+; -+; Voicemail with filesystem storage -+; -+;noload => app_directory_plain.so -+;noload => app_voicemail_plain.so -+ --- -1.5.3.6 - diff --git a/asterisk-1.4.16-appconference.patch b/asterisk-1.4.16-appconference.patch deleted file mode 100644 index 16cb3c5..0000000 --- a/asterisk-1.4.16-appconference.patch +++ /dev/null @@ -1,10238 +0,0 @@ -From 5fdceb7064e63067e16e8d27ae084ccb1f9d0f60 Mon Sep 17 00:00:00 2001 -From: Jeffrey C. Ollie -Date: Sun, 18 Nov 2007 22:23:17 -0600 -Subject: [PATCH] Add app_conference rev 911 - ---- - apps/Makefile | 14 + - apps/app_conference.c | 113 ++ - apps/conference/CLI.txt | 96 + - apps/conference/Flags.txt | 31 + - apps/conference/LICENSE | 341 ++++ - apps/conference/README | 125 ++ - apps/conference/README.videoswitch | 87 + - apps/conference/TODO | 4 + - apps/conference/app_conference.h | 244 +++ - apps/conference/cli.c | 1265 ++++++++++++++ - apps/conference/cli.h | 99 ++ - apps/conference/common.h | 63 + - apps/conference/conf_frame.h | 73 + - apps/conference/conference.c | 2923 +++++++++++++++++++++++++++++++ - apps/conference/conference.h | 194 +++ - apps/conference/frame.c | 679 ++++++++ - apps/conference/frame.h | 75 + - apps/conference/member.c | 3332 ++++++++++++++++++++++++++++++++++++ - apps/conference/member.h | 315 ++++ - 19 files changed, 10073 insertions(+), 0 deletions(-) - create mode 100644 apps/app_conference.c - create mode 100644 apps/conference/CLI.txt - create mode 100644 apps/conference/Flags.txt - create mode 100644 apps/conference/LICENSE - create mode 100644 apps/conference/README - create mode 100644 apps/conference/README.videoswitch - create mode 100644 apps/conference/TODO - create mode 100644 apps/conference/app_conference.h - create mode 100644 apps/conference/cli.c - create mode 100644 apps/conference/cli.h - create mode 100644 apps/conference/common.h - create mode 100644 apps/conference/conf_frame.h - create mode 100644 apps/conference/conference.c - create mode 100644 apps/conference/conference.h - create mode 100644 apps/conference/frame.c - create mode 100644 apps/conference/frame.h - create mode 100644 apps/conference/member.c - create mode 100644 apps/conference/member.h - -diff --git a/apps/Makefile b/apps/Makefile -index 4006c9a..cf33505 100644 ---- a/apps/Makefile -+++ b/apps/Makefile -@@ -44,4 +44,18 @@ endif - - all: _all - -+app_conference.o: ASTCFLAGS+=-DSILDET=2 -Iconference -+ -+conference/conference.o: ASTCFLAGS+=-DSILDET=2 -Iconference -+ -+conference/member.o: ASTCFLAGS+=-DSILDET=2 -Iconference -+ -+conference/frame.o: ASTCFLAGS+=-DSILDET=2 -Iconference -+ -+conference/cli.o: ASTCFLAGS+=-DSILDET=2 -Iconference -+ -+app_conference.so: app_conference.o conference/conference.o conference/member.o conference/frame.o conference/cli.o -+ $(ECHO_PREFIX) echo " [LD] $^ -> $@" -+ $(CMD_PREFIX) $(CXX) $(PTHREAD_CFLAGS) $(ASTLDFLAGS) $(SOLINK) -o $@ $^ -lspeex -+ - include $(ASTTOPDIR)/Makefile.moddir_rules -diff --git a/apps/app_conference.c b/apps/app_conference.c -new file mode 100644 -index 0000000..824d5dd ---- /dev/null -+++ b/apps/app_conference.c -@@ -0,0 +1,113 @@ -+/* -+ * app_conference -+ * -+ * A channel independent conference application for Asterisk -+ * -+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH -+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc. -+ * Copyright (C) 2005, 2006 HorizonWimba, Inc. -+ * Copyright (C) 2007 Wimba, Inc. -+ * -+ * Klaus-Peter Junghanns -+ * -+ * Video Conferencing support added by -+ * Neil Stratford -+ * Copyright (C) 2005, 2005 Vipadia Limited -+ * -+ * VAD driven video conferencing, text message support -+ * and miscellaneous enhancements added by -+ * Mihai Balea -+ * -+ * This program may be modified and distributed under the -+ * terms of the GNU General Public License. You should have received -+ * a copy of the GNU General Public License along with this -+ * program; if not, write to the Free Software Foundation, Inc. -+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#include "asterisk.h" -+ -+// SVN revision number, provided by make -+#ifndef REVISION -+#define REVISION "unknown" -+#endif -+ -+static char *revision = REVISION; -+ -+ASTERISK_FILE_VERSION(__FILE__, REVISION) -+ -+#include "app_conference.h" -+#include "common.h" -+ -+/* -+ * a conference has n + 1 threads, where n is the number of -+ * members and 1 is a conference thread which sends audio -+ * back to the members. -+ * -+ * each member thread reads frames from the channel and -+ * add's them to the member's frame queue. -+ * -+ * the conference thread reads frames from each speaking members -+ * queue, mixes them, and then re-queues them for the member thread -+ * to send back to the user. -+ */ -+ -+static char *app = "Conference"; -+static char *synopsis = "Channel Independent Conference"; -+static char *descrip = "Channel Independent Conference Application"; -+ -+static int app_conference_main(struct ast_channel* chan, void* data) -+{ -+ int res ; -+ struct ast_module_user *u ; -+ -+ u = ast_module_user_add(chan); -+ -+ // call member thread function -+ res = member_exec( chan, data ) ; -+ -+ ast_module_user_remove(u); -+ -+ return res ; -+} -+ -+static int unload_module( void ) -+{ -+ ast_log( LOG_NOTICE, "unloading app_conference module\n" ) ; -+ -+ ast_module_user_hangup_all(); -+ -+ unregister_conference_cli() ; -+ -+ return ast_unregister_application( app ) ; -+} -+ -+static int load_module( void ) -+{ -+ ast_log( LOG_NOTICE, "Loading app_conference module, revision=%s\n", revision) ; -+ -+ init_conference() ; -+ -+ register_conference_cli() ; -+ -+ return ast_register_application( app, app_conference_main, synopsis, descrip ) ; -+} -+ -+// increment a timeval by ms milliseconds -+void add_milliseconds(struct timeval* tv, long ms) -+{ -+ // add the microseconds to the microseconds field -+ tv->tv_usec += ( ms * 1000 ) ; -+ -+ // calculate the number of seconds to increment -+ long s = ( tv->tv_usec / 1000000 ) ; -+ -+ // adjust the microsends field -+ if ( s > 0 ) tv->tv_usec -= ( s * 1000000 ) ; -+ -+ // increment the seconds field -+ tv->tv_sec += s ; -+} -+ -+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, -+ "Channel Independent Conference Application"); -diff --git a/apps/conference/CLI.txt b/apps/conference/CLI.txt -new file mode 100644 -index 0000000..48acf5a ---- /dev/null -+++ b/apps/conference/CLI.txt -@@ -0,0 +1,96 @@ -+Current command line used by app_conference -+Please note that app_conference is still work in progress, so this document might be outdated. As always, the source code is the definitive reference (cli.[ch] and conference.[ch]). You can also obtain help/usage information by using Asterisk CLI help system ("help conference") -+ -+A member in a conference can be referred to by its id or by its channel. Id is a positive number assigned automatically when the member joins the conference. Channel is Asterisk channel identifier. To obtain a list of member ids and channels in a conference, do: -+ -+*CLI> conference list -+ -+ -+- conference debug: enable debugging for a conference -+ usage: conference debug [ on | off ] -+ -+- conference end: stops a conference -+ usage: conference end -+ -+- conference kick: kick member from a conference -+ usage: conference kick -+ -+- conference kickchannel: kick channel from a conference -+ usage: conference kickchannel -+ -+- conference list: list members of a conference. If no conference is specified, all conferences are listed -+ usage: conference list {conference_name} -+ -+- conference lock: locks incoming video to a member -+ usage: conference lock -+ -+- conference lockchannel: locks incoming video to a channel -+ usage: conference lockchannel -+ -+- conference mute: mute member in a conference -+ usage: conference mute -+ -+- conference mutechannel: mute channel in a conference -+ usage: conference mutechannel -+ -+- conference play sound: play a sound to a conference member -+ usage: conference play sound [mute] -+ If mute is specified, all other audio is muted while the sound is played back. -+ -+- conference restart: kick all users in all conferences -+ usage: conference restart -+ -+- conference set default: sets default video source -+ usage: conference set default -+ use a negative value for member if you want to clear the default -+ -+- conference set defaultchannel: sets default video source channel -+ usage: conference set defaultchannel -+ -+- conference show stats: show conference stats -+ usage: conference show stats -+ -+- conference text: sends a text message to a member. Depends on the member's channel capabilities. -+ usage: conference text -+ -+- conference textbroadcast: sends a text message to all members in a conference -+ usage: conference textbroadcast -+ -+- conference textchannel: sends a text message to a channel -+ usage: conference textchannel -+ -+- conference unlock: unlocks incoming video -+ usage: conference unlock -+ -+- conference unmute: unmute member in a conference -+ usage: conference unmute -+ -+- conference unmutechannel: unmute channel in a conference -+ usage: conference unmutechannel -+ -+- conference video mute: mutes video from a member -+ usage: conference video mute -+ -+- conference video mutechannel: mutes video from a channel -+ usage: conference video mutechannel -+ -+- conference video unmute: unmutes video from a member -+ usage: conference video unmute -+ -+- conference video unmutechannel: unmutes video from a channel -+ usage: conference video unmutechannel -+ -+- conference viewchannel: switch video for a channel in a conference -+ usage: conference viewchannel -+ -+- conference viewstream: switch video for a member a conference -+ usage: conference viewstream -+ -+- conference drive: drive VAD video switching of destination member using audio from source member -+ usage: conference drive [destination member] -+ If destination member is missing or negative, break existing connection -+ -+- conference drivechannel: drive VAD video switching of destination channel using audio from source channel -+ usage: conference drivechannel [destination channel] -+ If destination channel is missing, break existing connection -+ -diff --git a/apps/conference/Flags.txt b/apps/conference/Flags.txt -new file mode 100644 -index 0000000..dbb9732 ---- /dev/null -+++ b/apps/conference/Flags.txt -@@ -0,0 +1,31 @@ -+Current dialplan flags used by app_conference -+Please note that app_conference is still work in progress, so this document might be outdated. As always, the source code is the definitive reference (member.c in create_member()) -+ -+Mute/no receive options: -+'C' : member starts with video muted -+'c' : member starts unable to receive video -+'L' : member starts with audio muted -+'l' : member starts unable to receive audio -+ -+Speex preprocessing options (right now app_conference does preprocessing only for Zaptel members): -+'V' : enable speex preprocessing Voice Activity Detection -+'D' : enable speex preprocessing De-noise -+'A' : enable speex preprocessing Automatic Gain Control -+'T' : member connects through Zaptel, so speex preprocessing should be enabled -+ -+DTMF options: -+'X' : enable DTMF switch: video can be switched by users using DTMF. Do not use with 'S'. -+'R' : enable DTMF relay: DTMF tones generate a manager event -+If neither 'X' nor 'R' are present, DTMF tones will be forwarded to all members in the conference -+ -+Moderator/video switch options: -+'M' : member is a "moderator". When a moderator quits, all members are kicked and the conference is disabled. -+'S' : member accepts VAD controlled video switching. Do not use with 'X'. -+ -+Miscellaneous: -+'t' : member accepts text based control messages. The messages are described in a separate document -+'N' : Assume that the member starts off with camera disabled. -+ -+Future development (these are not implemented yet): -+'x' : marked member. We plan to change the behavior so that when ALL moderators quit, all members that are marked will get kicked. Other members in the conference are not affected. -+ -diff --git a/apps/conference/LICENSE b/apps/conference/LICENSE -new file mode 100644 -index 0000000..a52b16e ---- /dev/null -+++ b/apps/conference/LICENSE -@@ -0,0 +1,341 @@ -+ -+ GNU GENERAL PUBLIC LICENSE -+ Version 2, June 1991 -+ -+ Copyright (C) 1989, 1991 Free Software Foundation, Inc. -+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ Everyone is permitted to copy and distribute verbatim copies -+ of this license document, but changing it is not allowed. -+ -+ Preamble -+ -+ The licenses for most software are designed to take away your -+freedom to share and change it. By contrast, the GNU General Public -+License is intended to guarantee your freedom to share and change free -+software--to make sure the software is free for all its users. This -+General Public License applies to most of the Free Software -+Foundation's software and to any other program whose authors commit to -+using it. (Some other Free Software Foundation software is covered by -+the GNU Library General Public License instead.) You can apply it to -+your programs, too. -+ -+ When we speak of free software, we are referring to freedom, not -+price. Our General Public Licenses are designed to make sure that you -+have the freedom to distribute copies of free software (and charge for -+this service if you wish), that you receive source code or can get it -+if you want it, that you can change the software or use pieces of it -+in new free programs; and that you know you can do these things. -+ -+ To protect your rights, we need to make restrictions that forbid -+anyone to deny you these rights or to ask you to surrender the rights. -+These restrictions translate to certain responsibilities for you if you -+distribute copies of the software, or if you modify it. -+ -+ For example, if you distribute copies of such a program, whether -+gratis or for a fee, you must give the recipients all the rights that -+you have. You must make sure that they, too, receive or can get the -+source code. And you must show them these terms so they know their -+rights. -+ -+ We protect your rights with two steps: (1) copyright the software, and -+(2) offer you this license which gives you legal permission to copy, -+distribute and/or modify the software. -+ -+ Also, for each author's protection and ours, we want to make certain -+that everyone understands that there is no warranty for this free -+software. If the software is modified by someone else and passed on, we -+want its recipients to know that what they have is not the original, so -+that any problems introduced by others will not reflect on the original -+authors' reputations. -+ -+ Finally, any free program is threatened constantly by software -+patents. We wish to avoid the danger that redistributors of a free -+program will individually obtain patent licenses, in effect making the -+program proprietary. To prevent this, we have made it clear that any -+patent must be licensed for everyone's free use or not licensed at all. -+ -+ The precise terms and conditions for copying, distribution and -+modification follow. -+ -+ GNU GENERAL PUBLIC LICENSE -+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -+ -+ 0. This License applies to any program or other work which contains -+a notice placed by the copyright holder saying it may be distributed -+under the terms of this General Public License. The "Program", below, -+refers to any such program or work, and a "work based on the Program" -+means either the Program or any derivative work under copyright law: -+that is to say, a work containing the Program or a portion of it, -+either verbatim or with modifications and/or translated into another -+language. (Hereinafter, translation is included without limitation in -+the term "modification".) Each licensee is addressed as "you". -+ -+Activities other than copying, distribution and modification are not -+covered by this License; they are outside its scope. The act of -+running the Program is not restricted, and the output from the Program -+is covered only if its contents constitute a work based on the -+Program (independent of having been made by running the Program). -+Whether that is true depends on what the Program does. -+ -+ 1. You may copy and distribute verbatim copies of the Program's -+source code as you receive it, in any medium, provided that you -+conspicuously and appropriately publish on each copy an appropriate -+copyright notice and disclaimer of warranty; keep intact all the -+notices that refer to this License and to the absence of any warranty; -+and give any other recipients of the Program a copy of this License -+along with the Program. -+ -+You may charge a fee for the physical act of transferring a copy, and -+you may at your option offer warranty protection in exchange for a fee. -+ -+ 2. You may modify your copy or copies of the Program or any portion -+of it, thus forming a work based on the Program, and copy and -+distribute such modifications or work under the terms of Section 1 -+above, provided that you also meet all of these conditions: -+ -+ a) You must cause the modified files to carry prominent notices -+ stating that you changed the files and the date of any change. -+ -+ b) You must cause any work that you distribute or publish, that in -+ whole or in part contains or is derived from the Program or any -+ part thereof, to be licensed as a whole at no charge to all third -+ parties under the terms of this License. -+ -+ c) If the modified program normally reads commands interactively -+ when run, you must cause it, when started running for such -+ interactive use in the most ordinary way, to print or display an -+ announcement including an appropriate copyright notice and a -+ notice that there is no warranty (or else, saying that you provide -+ a warranty) and that users may redistribute the program under -+ these conditions, and telling the user how to view a copy of this -+ License. (Exception: if the Program itself is interactive but -+ does not normally print such an announcement, your work based on -+ the Program is not required to print an announcement.) -+ -+These requirements apply to the modified work as a whole. If -+identifiable sections of that work are not derived from the Program, -+and can be reasonably considered independent and separate works in -+themselves, then this License, and its terms, do not apply to those -+sections when you distribute them as separate works. But when you -+distribute the same sections as part of a whole which is a work based -+on the Program, the distribution of the whole must be on the terms of -+this License, whose permissions for other licensees extend to the -+entire whole, and thus to each and every part regardless of who wrote it. -+ -+Thus, it is not the intent of this section to claim rights or contest -+your rights to work written entirely by you; rather, the intent is to -+exercise the right to control the distribution of derivative or -+collective works based on the Program. -+ -+In addition, mere aggregation of another work not based on the Program -+with the Program (or with a work based on the Program) on a volume of -+a storage or distribution medium does not bring the other work under -+the scope of this License. -+ -+ 3. You may copy and distribute the Program (or a work based on it, -+under Section 2) in object code or executable form under the terms of -+Sections 1 and 2 above provided that you also do one of the following: -+ -+ a) Accompany it with the complete corresponding machine-readable -+ source code, which must be distributed under the terms of Sections -+ 1 and 2 above on a medium customarily used for software interchange; or, -+ -+ b) Accompany it with a written offer, valid for at least three -+ years, to give any third party, for a charge no more than your -+ cost of physically performing source distribution, a complete -+ machine-readable copy of the corresponding source code, to be -+ distributed under the terms of Sections 1 and 2 above on a medium -+ customarily used for software interchange; or, -+ -+ c) Accompany it with the information you received as to the offer -+ to distribute corresponding source code. (This alternative is -+ allowed only for noncommercial distribution and only if you -+ received the program in object code or executable form with such -+ an offer, in accord with Subsection b above.) -+ -+The source code for a work means the preferred form of the work for -+making modifications to it. For an executable work, complete source -+code means all the source code for all modules it contains, plus any -+associated interface definition files, plus the scripts used to -+control compilation and installation of the executable. However, as a -+special exception, the source code distributed need not include -+anything that is normally distributed (in either source or binary -+form) with the major components (compiler, kernel, and so on) of the -+operating system on which the executable runs, unless that component -+itself accompanies the executable. -+ -+If distribution of executable or object code is made by offering -+access to copy from a designated place, then offering equivalent -+access to copy the source code from the same place counts as -+distribution of the source code, even though third parties are not -+compelled to copy the source along with the object code. -+ -+ 4. You may not copy, modify, sublicense, or distribute the Program -+except as expressly provided under this License. Any attempt -+otherwise to copy, modify, sublicense or distribute the Program is -+void, and will automatically terminate your rights under this License. -+However, parties who have received copies, or rights, from you under -+this License will not have their licenses terminated so long as such -+parties remain in full compliance. -+ -+ 5. You are not required to accept this License, since you have not -+signed it. However, nothing else grants you permission to modify or -+distribute the Program or its derivative works. These actions are -+prohibited by law if you do not accept this License. Therefore, by -+modifying or distributing the Program (or any work based on the -+Program), you indicate your acceptance of this License to do so, and -+all its terms and conditions for copying, distributing or modifying -+the Program or works based on it. -+ -+ 6. Each time you redistribute the Program (or any work based on the -+Program), the recipient automatically receives a license from the -+original licensor to copy, distribute or modify the Program subject to -+these terms and conditions. You may not impose any further -+restrictions on the recipients' exercise of the rights granted herein. -+You are not responsible for enforcing compliance by third parties to -+this License. -+ -+ 7. If, as a consequence of a court judgment or allegation of patent -+infringement or for any other reason (not limited to patent issues), -+conditions are imposed on you (whether by court order, agreement or -+otherwise) that contradict the conditions of this License, they do not -+excuse you from the conditions of this License. If you cannot -+distribute so as to satisfy simultaneously your obligations under this -+License and any other pertinent obligations, then as a consequence you -+may not distribute the Program at all. For example, if a patent -+license would not permit royalty-free redistribution of the Program by -+all those who receive copies directly or indirectly through you, then -+the only way you could satisfy both it and this License would be to -+refrain entirely from distribution of the Program. -+ -+If any portion of this section is held invalid or unenforceable under -+any particular circumstance, the balance of the section is intended to -+apply and the section as a whole is intended to apply in other -+circumstances. -+ -+It is not the purpose of this section to induce you to infringe any -+patents or other property right claims or to contest validity of any -+such claims; this section has the sole purpose of protecting the -+integrity of the free software distribution system, which is -+implemented by public license practices. Many people have made -+generous contributions to the wide range of software distributed -+through that system in reliance on consistent application of that -+system; it is up to the author/donor to decide if he or she is willing -+to distribute software through any other system and a licensee cannot -+impose that choice. -+ -+This section is intended to make thoroughly clear what is believed to -+be a consequence of the rest of this License. -+ -+ 8. If the distribution and/or use of the Program is restricted in -+certain countries either by patents or by copyrighted interfaces, the -+original copyright holder who places the Program under this License -+may add an explicit geographical distribution limitation excluding -+those countries, so that distribution is permitted only in or among -+countries not thus excluded. In such case, this License incorporates -+the limitation as if written in the body of this License. -+ -+ 9. The Free Software Foundation may publish revised and/or new versions -+of the General Public License from time to time. Such new versions will -+be similar in spirit to the present version, but may differ in detail to -+address new problems or concerns. -+ -+Each version is given a distinguishing version number. If the Program -+specifies a version number of this License which applies to it and "any -+later version", you have the option of following the terms and conditions -+either of that version or of any later version published by the Free -+Software Foundation. If the Program does not specify a version number of -+this License, you may choose any version ever published by the Free Software -+Foundation. -+ -+ 10. If you wish to incorporate parts of the Program into other free -+programs whose distribution conditions are different, write to the author -+to ask for permission. For software which is copyrighted by the Free -+Software Foundation, write to the Free Software Foundation; we sometimes -+make exceptions for this. Our decision will be guided by the two goals -+of preserving the free status of all derivatives of our free software and -+of promoting the sharing and reuse of software generally. -+ -+ NO WARRANTY -+ -+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -+REPAIR OR CORRECTION. -+ -+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -+POSSIBILITY OF SUCH DAMAGES. -+ -+ END OF TERMS AND CONDITIONS -+ -+ How to Apply These Terms to Your New Programs -+ -+ If you develop a new program, and you want it to be of the greatest -+possible use to the public, the best way to achieve this is to make it -+free software which everyone can redistribute and change under these terms. -+ -+ To do so, attach the following notices to the program. It is safest -+to attach them to the start of each source file to most effectively -+convey the exclusion of warranty; and each file should have at least -+the "copyright" line and a pointer to where the full notice is found. -+ -+ -+ Copyright (C) 19yy -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License as published by -+ the Free Software Foundation; either version 2 of the License, or -+ (at your option) any later version. -+ -+ This program is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ GNU General Public License for more details. -+ -+ You should have received a copy of the GNU General Public License -+ along with this program; if not, write to the Free Software -+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ -+ -+Also add information on how to contact you by electronic and paper mail. -+ -+If the program is interactive, make it output a short notice like this -+when it starts in an interactive mode: -+ -+ Gnomovision version 69, Copyright (C) 19yy name of author -+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. -+ This is free software, and you are welcome to redistribute it -+ under certain conditions; type `show c' for details. -+ -+The hypothetical commands `show w' and `show c' should show the appropriate -+parts of the General Public License. Of course, the commands you use may -+be called something other than `show w' and `show c'; they could even be -+mouse-clicks or menu items--whatever suits your program. -+ -+You should also get your employer (if you work as a programmer) or your -+school, if any, to sign a "copyright disclaimer" for the program, if -+necessary. Here is a sample; alter the names: -+ -+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program -+ `Gnomovision' (which makes passes at compilers) written by James Hacker. -+ -+ , 1 April 1989 -+ Ty Coon, President of Vice -+ -+This General Public License does not permit incorporating your program into -+proprietary programs. If your program is a subroutine library, you may -+consider it more useful to permit linking proprietary applications with the -+library. If this is what you want to do, use the GNU Library General -+Public License instead of this License. -diff --git a/apps/conference/README b/apps/conference/README -new file mode 100644 -index 0000000..4c103ce ---- /dev/null -+++ b/apps/conference/README -@@ -0,0 +1,125 @@ -+Introduction -+ -+App_conference is a channel-independent conference application. -+It features efficient audio mixing algorithms as well as video selection -+support based on VAD, DTMF or CLI. -+ -+ -+Design goals -+ -+Appconference has several design goals which are different than Meetme: -+ -+ * It does not require a zap channel for timing. -+ * It is very efficient when used with channels which support DTX (silence -+ detection/discontinuous transmission). -+ -+ * It can do VAD on channels which do not support DTX (although this -+ is more expensive than just mixing them, but less expensive then -+ encoding; therefore it might still be a win). -+ * It presents messages on the Monitor interface for determine which -+ speakers are active. -+ -+Mixing design -+ -+ * Minimize encoding/decoding, minimize mixing. -+ * Minimize generational loss from trancoding. -+ * Usual cases are handled very efficiently: -+ o One speaker: That speaker's frame is sent directly to each -+ participant which uses the same codec. It is trancoded -+ _once_ for each additional codec type used by participants. -+ o Two speakers: Each speaker gets the other speaker's frames. -+ The two speaker's frames are decoded and mixed, and then -+ encoded _once_ for each codec type used by participants. -+ -+Video features -+ -+ * Video passthrough: video from selected member is passed to every -+ member of the conference. -+ * Multiple ways to select video -+ - VAD -+ - DTMF from conference members -+ - CLI -+ * Ability to set default video sources and to lock/unlock video sources. -+ -+ -+License -+ -+Naturally, app_conference is GPL. The SVN repository also includes parts of -+libspeex, which is distributed under a BSD-style license. See LICENSE for more -+details. -+ -+ -+Getting app_conference -+ -+app_conference is available via SVN from its own home on sourceforge: -+ -+ * http://sourceforge.net/projects/appconference -+ -+ -+Compiling app_conference -+ -+ * Checkout sources -+ * Modify Makefile to point to your Asterisk include directory -+ * make -+ * sudo make install -+ -+ -+Using app_conference -+ -+There is no configuration file. Conferences are created on-the-fly. -+ -+Dialplan syntax: Conference(ConferenceName/Flags/Priority[/VADSTART/VADCONTINUE]) -+ -+ * ConferenceName: Whatever you want to name the conference -+ * Flags: please see Flags.txt for a comprehensive list of dialplan flags -+ * Priority: Currently ignored; was to be a "speaking priority" so a -+ higher priority caller could "override" others. -+ * VADSTART: Optional: "probability" to use to detect start of speech. -+ * VADCONTINUE: Optional: "probability" to use to detect continuation -+ of speech. -+ -+ -+CLI Commands -+ -+Please look at CLI.txt for a comprehensive list of CLI commands and parameters. -+ -+ -+Manager Events -+ -+ -+app_conference generates several detailed manager events so that applications -+interfacing with the manager API can monitor conferences: -+ -+ * ConferenceState: sent as members begin/end speaking. -+ Channel: The channel -+ State: "speaking" or "silent" -+ -+ * ConferenceDTMF: sent when conference members send DTMF to the conference -+ Channel: The channel -+ Key: The DTMF key send [0-9*#] -+ -+ * ConferenceSoundComplete: send when the conference has finished playing -+ a sound to a user -+ Channel: The channel -+ Sound: The first 255 bytes of the file requested in conference play -+ sound CLI/Mgr command. -+ -+ -+Benchmarking -+ -+It would be nice to have solid benchmarks to present, but a good size -+machine should be able to handle many callers when either (a) they are -+using DTX, or (b) they are listen-only. It's used often with hundreds of -+simultaneous callers. -+ -+ -+Discussion -+ -+The appconference-devel mailing list is the place to discuss everything related -+to app_conference. The bug tracker on SourceForge gets a little bit of -+attention now and then. -+ -+ -+--- -+ -+app_conference is brought to you by the letter q, and the number e -diff --git a/apps/conference/README.videoswitch b/apps/conference/README.videoswitch -new file mode 100644 -index 0000000..2612194 ---- /dev/null -+++ b/apps/conference/README.videoswitch -@@ -0,0 +1,87 @@ -+VideoSwitch -+----------- -+(c) Vipadia Limited 2005-2006 -+Neil Stratford -+ -+Based on app_conference, see README. -+ -+Including contributions from John Martin -+ -+Example use: -+ -+exten => 2300,1,Videoswitch(test/RX) -+ -+This puts the user into a conference 'test' with the options SRX. -+ -+The options are the same as app_conference, except: -+ -+X - enable the caller to switch video stream using DTMF -+R - relay the DTMF to the management interface -+C - Mute video - no video from this client -+c - No Receive video - send no video to this client -+L - Mute audio - no audio from this client -+l - No Receive audio - send no audio to this client -+M - member is moderator - when they leave everyone else is kicked -+ -+Stream selection options: two integers, first is receive id, second is send id. -+Both are optional. -+ -+0-9 - Set initial receive stream to n -+0-9 - Set this stream id to n (will stop any other video with that id already) -+ -+eg: Videoswitch(test/01) will set our id to 1, and we will receive id 0's video -+ -+CLI commands (which may also be invoked from the manager interface -+using the Command action): -+ -+Most commands have two versions, which can either take a member number (obtained from 'videoswitch list') or a channel identifier (such as SIP/2304-1e82). -+ -+fidwell*CLI> help videoswitch -+ videoswitch debug enable debugging for a videoswitch -+ videoswitch kick kick member from a videoswitch -+ videoswitch list list members of a videoswitch -+ videoswitch mute mute member in a videoswitch -+ videoswitch mutechannel mute channel in a videoswitch -+ videoswitch show stats show videoswitch stats -+ videoswitch unmute unmute member in a videoswitch -+videoswitch unmutechannel unmute channel in a videoswitch -+ videoswitch viewchannel switch channel in a videoswitch -+ videoswitch viewstream switch view in a videoswitch -+ -+fidwell*CLI> help videoswitch debug -+usage: videoswitch debug [ on | off ] -+ enable debugging for a videoswitch -+ -+fidwell*CLI> help videoswitch kick -+usage: videoswitch kick -+ kick member form a videoswitch -+ -+fidwell*CLI> help videoswitch list -+usage: videoswitch list {} -+ list members of a videoswitch or list of videoswitches if no name -+ -+fidwell*CLI> help videoswitch mute -+usage: videoswitch mute -+ mute member in a videoswitch -+ -+fidwell*CLI> help videoswitch unmute -+usage: videoswitch unmute -+ unmute member in a videoswitch -+ -+fidwell*CLI> help videoswitch mutechannel -+usage: videoswitch mute -+ mute channel in a videoswitch -+ -+fidwell*CLI> help videoswitch unmutechannel -+usage: videoswitch unmute -+ unmute channel in a videoswitch -+ -+fidwell*CLI> help videoswitch viewchannel -+usage: videoswitch viewchannel -+ channel will receive video stream -+ -+fidwell*CLI> help videoswitch viewstream -+usage: videoswitch viewstream -+ member will receive video stream -+ -+ -diff --git a/apps/conference/TODO b/apps/conference/TODO -new file mode 100644 -index 0000000..121e83e ---- /dev/null -+++ b/apps/conference/TODO -@@ -0,0 +1,4 @@ -+Things we need to do for the next release -+- Enable speex based VAD for all members instead of just for telephone -+members (configurable) -+- Documentation! -diff --git a/apps/conference/app_conference.h b/apps/conference/app_conference.h -new file mode 100644 -index 0000000..17aca58 ---- /dev/null -+++ b/apps/conference/app_conference.h -@@ -0,0 +1,244 @@ -+ -+// $Id: app_conference.h 839 2007-01-17 22:32:03Z sbalea $ -+ -+/* -+ * app_conference -+ * -+ * A channel independent conference application for Asterisk -+ * -+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH -+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc. -+ * Copyright (C) 2005, 2006 HorizonWimba, Inc. -+ * Copyright (C) 2007 Wimba, Inc. -+ * -+ * Klaus-Peter Junghanns -+ * -+ * Video Conferencing support added by -+ * Neil Stratford -+ * Copyright (C) 2005, 2005 Vipadia Limited -+ * -+ * VAD driven video conferencing, text message support -+ * and miscellaneous enhancements added by -+ * Mihai Balea -+ * -+ * This program may be modified and distributed under the -+ * terms of the GNU General Public License. You should have received -+ * a copy of the GNU General Public License along with this -+ * program; if not, write to the Free Software Foundation, Inc. -+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef _ASTERISK_CONF_H -+#define _ASTERISK_CONF_H -+ -+ -+/* standard includes */ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+ -+#include -+ -+/* asterisk includes */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+//#include -+#include -+ -+ -+#if (SILDET == 2) -+#include -+#endif -+ -+// -+// app_conference defines -+// -+ -+// debug logging level -+ -+// LOG_NOTICE for debugging, LOG_DEBUG for production -+#ifdef APP_CONFERENCE_DEBUG -+#define AST_CONF_DEBUG LOG_NOTICE -+#else -+#define AST_CONF_DEBUG LOG_DEBUG -+#endif -+ -+// -+// feature defines -+// -+ -+// number of times the last non-silent frame should be -+// repeated after silence starts -+#define AST_CONF_CACHE_LAST_FRAME 1 -+ -+// -+// debug defines -+// -+ -+//#define DEBUG_USE_TIMELOG -+ -+//#define DEBUG_FRAME_TIMESTAMPS -+ -+// #define DEBUG_OUTPUT_PCM -+ -+// -+// !!! THESE CONSTANTS SHOULD BE CLEANED UP AND CLARIFIED !!! -+// -+ -+// -+// sample information for AST_FORMAT_SLINEAR format -+// -+ -+#define AST_CONF_SAMPLE_RATE 8000 -+#define AST_CONF_SAMPLE_SIZE 16 -+#define AST_CONF_FRAME_INTERVAL 20 -+//neils#define AST_CONF_FRAME_INTERVAL 30 -+ -+// -+// so, since we cycle approximately every 20ms, -+// we can compute the following values: -+// -+// 160 samples per 20 ms frame -or- -+// ( 8000 samples-per-second * ( 20 ms / 1000 ms-per-second ) ) = 160 samples -+// -+// 320 bytes ( 2560 bits ) of data 20 ms frame -or- -+// ( 160 samples * 16 bits-per-sample / 8 bits-per-byte ) = 320 bytes -+// -+ -+// 160 samples 16-bit signed linear -+#define AST_CONF_BLOCK_SAMPLES 160 -+ -+// 2 bytes per sample ( i.e. 16-bit ) -+#define AST_CONF_BYTES_PER_SAMPLE 2 -+ -+// 320 bytes for each 160 sample frame of 16-bit audio -+#define AST_CONF_FRAME_DATA_SIZE 320 -+ -+// 1000 ms-per-second / 20 ms-per-frame = 50 frames-per-second -+#define AST_CONF_FRAMES_PER_SECOND ( 1000 / AST_CONF_FRAME_INTERVAL ) -+ -+ -+// -+// buffer and queue values -+// -+ -+// account for friendly offset when allocating buffer for frame -+#define AST_CONF_BUFFER_SIZE ( AST_CONF_FRAME_DATA_SIZE + AST_FRIENDLY_OFFSET ) -+ -+// maximum number of frames queued per member -+#define AST_CONF_MAX_QUEUE 100 -+ -+// max video frames in the queue -+#define AST_CONF_MAX_VIDEO_QUEUE 800 -+ -+// max dtmf frames in the queue -+#define AST_CONF_MAX_DTMF_QUEUE 8 -+ -+// max text frames in the queue -+#define AST_CONF_MAX_TEXT_QUEUE 8 -+ -+// minimum number of frames queued per member -+#define AST_CONF_MIN_QUEUE 0 -+ -+// number of queued frames before we start dropping -+#define AST_CONF_QUEUE_DROP_THRESHOLD 40 -+ -+// number of milliseconds between frame drops -+#define AST_CONF_QUEUE_DROP_TIME_LIMIT 750 -+ -+// -+// timer and sleep values -+// -+ -+// milliseconds we're willing to wait for a channel -+// event before we check for outgoing frames -+#define AST_CONF_WAITFOR_LATENCY 40 -+ -+// milliseconds to sleep before trying to process frames -+#define AST_CONF_CONFERENCE_SLEEP 40 -+ -+// milliseconds to wait between state notification updates -+#define AST_CONF_NOTIFICATION_SLEEP 200 -+ -+// -+// warning threshold values -+// -+ -+// number of frames behind before warning -+#define AST_CONF_OUTGOING_FRAMES_WARN 70 -+ -+// number of milliseconds off AST_CONF_FRAME_INTERVAL before warning -+#define AST_CONF_INTERVAL_WARNING 1000 -+ -+// -+// silence detection values -+// -+ -+// toggle silence detection -+#define ENABLE_SILENCE_DETECTION 1 -+ -+// silence threshold -+#define AST_CONF_SILENCE_THRESHOLD 128 -+ -+// speech tail (delay before dropping silent frames, in ms. -+// #define AST_CONF_SPEECH_TAIL 180 -+ -+// number of frames to ignore speex_preprocess() after speech detected -+#define AST_CONF_SKIP_SPEEX_PREPROCESS 20 -+ -+// our speex probability values -+#define AST_CONF_PROB_START 0.05 -+#define AST_CONF_PROB_CONTINUE 0.02 -+ -+ -+// -+// format translation values -+// -+#ifdef AC_USE_G729A -+ #define AC_SUPPORTED_FORMATS 6 -+ enum { AC_SLINEAR_INDEX = 0, AC_ULAW_INDEX, AC_ALAW_INDEX, AC_GSM_INDEX, AC_SPEEX_INDEX, AC_G729A_INDEX } ; -+#else -+ #define AC_SUPPORTED_FORMATS 5 -+ enum { AC_SLINEAR_INDEX = 0, AC_ULAW_INDEX, AC_ALAW_INDEX, AC_GSM_INDEX, AC_SPEEX_INDEX } ; -+#endif -+ -+// -+// VAD based video switching parameters -+// All time related values are in ms -+// -+ -+// Amount of silence required before we decide somebody stopped talking -+#define AST_CONF_VIDEO_STOP_TIMEOUT 2000 -+ -+// Amount of audio required before we decide somebody started talking -+#define AST_CONF_VIDEO_START_TIMEOUT 2000 -+ -+// -+// Text frame control protocol -+// -+#define AST_CONF_CONTROL_CAMERA_DISABLED "CONTROL:CAMERA_DISABLED" -+#define AST_CONF_CONTROL_CAMERA_ENABLED "CONTROL:CAMERA_ENABLED" -+#define AST_CONF_CONTROL_START_VIDEO "CONTROL:STARTVIDEO" -+#define AST_CONF_CONTROL_STOP_VIDEO "CONTROL:STOPVIDEO" -+#define AST_CONF_CONTROL_STOP_VIDEO_TRANSMIT "CONTROL:STOP_VIDEO_TRANSMIT" -+#define AST_CONF_CONTROL_START_VIDEO_TRANSMIT "CONTROL:START_VIDEO_TRANSMIT" -+ -+// utility functions -+void add_milliseconds( struct timeval* tv, long ms ) ; -+ -+#endif -+ -+ -diff --git a/apps/conference/cli.c b/apps/conference/cli.c -new file mode 100644 -index 0000000..3d88ddf ---- /dev/null -+++ b/apps/conference/cli.c -@@ -0,0 +1,1265 @@ -+ -+// $Id: cli.c 884 2007-06-27 14:56:21Z sbalea $ -+ -+/* -+ * app_conference -+ * -+ * A channel independent conference application for Asterisk -+ * -+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH -+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc. -+ * Copyright (C) 2005, 2006 HorizonWimba, Inc. -+ * Copyright (C) 2007 Wimba, Inc. -+ * -+ * Klaus-Peter Junghanns -+ * -+ * Video Conferencing support added by -+ * Neil Stratford -+ * Copyright (C) 2005, 2005 Vipadia Limited -+ * -+ * VAD driven video conferencing, text message support -+ * and miscellaneous enhancements added by -+ * Mihai Balea -+ * -+ * This program may be modified and distributed under the -+ * terms of the GNU General Public License. You should have received -+ * a copy of the GNU General Public License along with this -+ * program; if not, write to the Free Software Foundation, Inc. -+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#include "asterisk/autoconfig.h" -+#include "cli.h" -+ -+static char conference_restart_usage[] = -+ "usage: conference restart\n" -+ " kick all users in all conferences\n" -+; -+ -+static struct ast_cli_entry cli_restart = { -+ { "conference", "restart", NULL }, -+ conference_restart, -+ "restart a conference", -+ conference_restart_usage -+} ; -+ -+ -+int conference_restart( int fd, int argc, char *argv[] ) -+{ -+ if ( argc < 2 ) -+ return RESULT_SHOWUSAGE ; -+ -+ kick_all(); -+ return RESULT_SUCCESS ; -+} -+ -+ -+// -+// debug functions -+// -+ -+static char conference_debug_usage[] = -+ "usage: conference debug [ on | off ]\n" -+ " enable debugging for a conference\n" -+; -+ -+static struct ast_cli_entry cli_debug = { -+ { "conference", "debug", NULL }, -+ conference_debug, -+ "enable debugging for a conference", -+ conference_debug_usage -+} ; -+ -+ -+int conference_debug( int fd, int argc, char *argv[] ) -+{ -+ if ( argc < 3 ) -+ return RESULT_SHOWUSAGE ; -+ -+ // get the conference name -+ const char* name = argv[2] ; -+ -+ // get the new state -+ int state = 0 ; -+ -+ if ( argc == 3 ) -+ { -+ // no state specified, so toggle it -+ state = -1 ; -+ } -+ else -+ { -+ if ( strncasecmp( argv[3], "on", 4 ) == 0 ) -+ state = 1 ; -+ else if ( strncasecmp( argv[3], "off", 3 ) == 0 ) -+ state = 0 ; -+ else -+ return RESULT_SHOWUSAGE ; -+ } -+ -+ int new_state = set_conference_debugging( name, state ) ; -+ -+ if ( new_state == 1 ) -+ { -+ ast_cli( fd, "enabled conference debugging, name => %s, new_state => %d\n", -+ name, new_state ) ; -+ } -+ else if ( new_state == 0 ) -+ { -+ ast_cli( fd, "disabled conference debugging, name => %s, new_state => %d\n", -+ name, new_state ) ; -+ } -+ else -+ { -+ // error setting state -+ ast_cli( fd, "\nunable to set debugging state, name => %s\n\n", name ) ; -+ } -+ -+ return RESULT_SUCCESS ; -+} -+ -+// -+// stats functions -+// -+ -+static char conference_show_stats_usage[] = -+ "usage: conference show stats\n" -+ " display stats for active conferences.\n" -+; -+ -+static struct ast_cli_entry cli_show_stats = { -+ { "conference", "show", "stats", NULL }, -+ conference_show_stats, -+ "show conference stats", -+ conference_show_stats_usage -+} ; -+ -+int conference_show_stats( int fd, int argc, char *argv[] ) -+{ -+ if ( argc < 3 ) -+ return RESULT_SHOWUSAGE ; -+ -+ // get count of active conferences -+ int count = get_conference_count() ; -+ -+ ast_cli( fd, "\n\nCONFERENCE STATS, ACTIVE( %d )\n\n", count ) ; -+ -+ // if zero, go no further -+ if ( count <= 0 ) -+ return RESULT_SUCCESS ; -+ -+ // -+ // get the conference stats -+ // -+ -+ // array of stats structs -+ ast_conference_stats stats[ count ] ; -+ -+ // get stats structs -+ count = get_conference_stats( stats, count ) ; -+ -+ // make sure we were able to fetch some -+ if ( count <= 0 ) -+ { -+ ast_cli( fd, "!!! error fetching conference stats, available => %d !!!\n", count ) ; -+ return RESULT_SUCCESS ; -+ } -+ -+ // -+ // output the conference stats -+ // -+ -+ // output header -+ ast_cli( fd, "%-20.20s %-40.40s\n", "Name", "Stats") ; -+ ast_cli( fd, "%-20.20s %-40.40s\n", "----", "-----") ; -+ -+ ast_conference_stats* s = NULL ; -+ -+ int i; -+ -+ for ( i = 0 ; i < count ; ++i ) -+ { -+ s = &(stats[i]) ; -+ -+ // output this conferences stats -+ ast_cli( fd, "%-20.20s\n", (char*)( &(s->name) )) ; -+ } -+ -+ ast_cli( fd, "\n" ) ; -+ -+ // -+ // drill down to specific stats -+ // -+ -+ if ( argc == 4 ) -+ { -+ // show stats for a particular conference -+ conference_show_stats_name( fd, argv[3] ) ; -+ } -+ -+ return RESULT_SUCCESS ; -+} -+ -+int conference_show_stats_name( int fd, const char* name ) -+{ -+ // not implemented yet -+ return RESULT_SUCCESS ; -+} -+ -+static char conference_list_usage[] = -+ "usage: conference list {}\n" -+ " list members of a conference\n" -+; -+ -+static struct ast_cli_entry cli_list = { -+ { "conference", "list", NULL }, -+ conference_list, -+ "list members of a conference", -+ conference_list_usage -+} ; -+ -+ -+ -+int conference_list( int fd, int argc, char *argv[] ) -+{ -+ int index; -+ -+ if ( argc < 2 ) -+ return RESULT_SHOWUSAGE ; -+ -+ if (argc >= 3) -+ { -+ for (index = 2; index < argc; index++) -+ { -+ // get the conference name -+ const char* name = argv[index] ; -+ show_conference_list( fd, name ); -+ } -+ } -+ else -+ { -+ show_conference_stats(fd); -+ } -+ return RESULT_SUCCESS ; -+} -+ -+ -+int conference_kick( int fd, int argc, char *argv[] ) -+{ -+ if ( argc < 4 ) -+ return RESULT_SHOWUSAGE ; -+ -+ // get the conference name -+ const char* name = argv[2] ; -+ -+ int member_id; -+ sscanf(argv[3], "%d", &member_id); -+ -+ int res = kick_member( name, member_id ); -+ -+ if (res) ast_cli( fd, "User #: %d kicked\n", member_id) ; -+ -+ return RESULT_SUCCESS ; -+} -+ -+static char conference_kick_usage[] = -+ "usage: conference kick \n" -+ " kick member from conference \n" -+; -+ -+static struct ast_cli_entry cli_kick = { -+ { "conference", "kick", NULL }, -+ conference_kick, -+ "kick member from a conference", -+ conference_kick_usage -+} ; -+ -+int conference_kickchannel( int fd, int argc, char *argv[] ) -+{ -+ if ( argc < 4 ) -+ return RESULT_SHOWUSAGE ; -+ -+ const char *name = argv[2] ; -+ const char *channel = argv[3]; -+ -+ int res = kick_channel( name, channel ); -+ -+ if ( !res ) -+ { -+ ast_cli( fd, "Cannot kick channel %s in conference %s\n", channel, name); -+ return RESULT_FAILURE; -+ } -+ -+ return RESULT_SUCCESS ; -+} -+ -+static char conference_kickchannel_usage[] = -+ "usage: conference kickchannel \n" -+ " kick channel from conference\n" -+; -+ -+static struct ast_cli_entry cli_kickchannel = { -+ { "conference", "kickchannel", NULL }, -+ conference_kickchannel, -+ "kick channel from conference", -+ conference_kickchannel_usage -+} ; -+ -+int conference_exit( int fd, int argc, char *argv[] ) -+{ -+ if ( argc < 3 ) -+ return RESULT_SHOWUSAGE ; -+ -+ const char *channel = argv[2]; -+ -+ struct ast_conf_member *member = find_member(channel, 1); -+ if(!member) -+ { -+ ast_cli(fd, "Member %s not found\n", channel); -+ return RESULT_FAILURE; -+ } -+ const char * name = member->conf_name; -+ int res = kick_channel( name, channel ); -+ -+ if ( !res ) -+ { -+ ast_cli(fd, "Cannot exit channel %s from conference %s\n", channel, name); -+ ast_mutex_unlock(&member->lock); -+ return RESULT_FAILURE; -+ } -+ -+ ast_mutex_unlock( &member->lock ) ; -+ return RESULT_SUCCESS ; -+} -+ -+static char conference_exit_usage[] = -+ "usage: conference exit \n" -+ " exit channel from any conference where it in\n"; -+ -+static struct ast_cli_entry cli_exit = { -+ { "conference", "exit", NULL }, -+ conference_exit, -+ "exit channel from any conference where it in", -+ conference_exit_usage -+}; -+ -+int conference_mute( int fd, int argc, char *argv[] ) -+{ -+ if ( argc < 4 ) -+ return RESULT_SHOWUSAGE ; -+ -+ // get the conference name -+ const char* name = argv[2] ; -+ -+ int member_id; -+ sscanf(argv[3], "%d", &member_id); -+ -+ int res = mute_member( name, member_id ); -+ -+ if (res) ast_cli( fd, "User #: %d muted\n", member_id) ; -+ -+ return RESULT_SUCCESS ; -+} -+ -+static char conference_mute_usage[] = -+ "usage: conference mute \n" -+ " mute member in a conference\n" -+; -+ -+static struct ast_cli_entry cli_mute = { -+ { "conference", "mute", NULL }, -+ conference_mute, -+ "mute member in a conference", -+ conference_mute_usage -+} ; -+ -+int conference_mutechannel( int fd, int argc, char *argv[] ) -+{ -+ struct ast_conf_member *member; -+ char *channel; -+ -+ if ( argc < 3 ) -+ return RESULT_SHOWUSAGE ; -+ -+ channel = argv[2]; -+ -+ member = find_member(channel, 1); -+ if(!member) { -+ ast_cli(fd, "Member %s not found\n", channel); -+ return RESULT_FAILURE; -+ } -+ -+ member->mute_audio = 1; -+ ast_mutex_unlock( &member->lock ) ; -+ -+ ast_cli( fd, "Channel #: %s muted\n", argv[2]) ; -+ -+ return RESULT_SUCCESS ; -+} -+ -+static char conference_mutechannel_usage[] = -+ "usage: conference mutechannel \n" -+ " mute channel in a conference\n" -+; -+ -+static struct ast_cli_entry cli_mutechannel = { -+ { "conference", "mutechannel", NULL }, -+ conference_mutechannel, -+ "mute channel in a conference", -+ conference_mutechannel_usage -+} ; -+ -+int conference_viewstream( int fd, int argc, char *argv[] ) -+{ -+ int res; -+ -+ if ( argc < 5 ) -+ return RESULT_SHOWUSAGE ; -+ -+ // get the conference name -+ const char* switch_name = argv[2] ; -+ -+ int member_id, viewstream_id; -+ sscanf(argv[3], "%d", &member_id); -+ sscanf(argv[4], "%d", &viewstream_id); -+ -+ res = viewstream_switch( switch_name, member_id, viewstream_id ); -+ -+ if (res) ast_cli( fd, "User #: %d viewing %d\n", member_id, viewstream_id) ; -+ -+ return RESULT_SUCCESS ; -+} -+ -+static char conference_viewstream_usage[] = -+ "usage: conference viewstream \n" -+ " member will receive video stream \n" -+; -+ -+static struct ast_cli_entry cli_viewstream = { -+ { "conference", "viewstream", NULL }, -+ conference_viewstream, -+ "switch view in a conference", -+ conference_viewstream_usage -+} ; -+ -+int conference_viewchannel( int fd, int argc, char *argv[] ) -+{ -+ int res; -+ -+ if ( argc < 5 ) -+ return RESULT_SHOWUSAGE ; -+ -+ // get the conference name -+ const char* switch_name = argv[2] ; -+ -+ res = viewchannel_switch( switch_name, argv[3], argv[4] ); -+ -+ if (res) ast_cli( fd, "Channel #: %s viewing %s\n", argv[3], argv[4]) ; -+ -+ return RESULT_SUCCESS ; -+} -+ -+static char conference_viewchannel_usage[] = -+ "usage: conference viewchannel \n" -+ " channel will receive video stream \n" -+; -+ -+static struct ast_cli_entry cli_viewchannel = { -+ { "conference", "viewchannel", NULL }, -+ conference_viewchannel, -+ "switch channel in a conference", -+ conference_viewchannel_usage -+} ; -+ -+int conference_unmute( int fd, int argc, char *argv[] ) -+{ -+ if ( argc < 3 ) -+ return RESULT_SHOWUSAGE ; -+ -+ // get the conference name -+ const char* name = argv[2] ; -+ -+ int member_id; -+ sscanf(argv[3], "%d", &member_id); -+ -+ int res = unmute_member( name, member_id ); -+ -+ if (res) ast_cli( fd, "User #: %d unmuted\n", member_id) ; -+ -+ return RESULT_SUCCESS ; -+} -+ -+static char conference_unmute_usage[] = -+ "usage: conference unmute \n" -+ " unmute member in a conference\n" -+; -+ -+static struct ast_cli_entry cli_unmute = { -+ { "conference", "unmute", NULL }, -+ conference_unmute, -+ "unmute member in a conference", -+ conference_unmute_usage -+} ; -+ -+int conference_unmutechannel( int fd, int argc, char *argv[] ) -+{ -+ struct ast_conf_member *member; -+ char *channel; -+ -+ if ( argc < 3 ) -+ return RESULT_SHOWUSAGE ; -+ -+ channel = argv[2]; -+ -+ member = find_member(channel, 1); -+ if(!member) { -+ ast_cli(fd, "Member %s not found\n", channel); -+ return RESULT_FAILURE; -+ } -+ -+ member->mute_audio = 0; -+ ast_mutex_unlock( &member->lock ) ; -+ -+ ast_cli( fd, "Channel #: %s unmuted\n", argv[2]) ; -+ -+ return RESULT_SUCCESS ; -+} -+ -+static char conference_unmutechannel_usage[] = -+ "usage: conference unmutechannel \n" -+ " unmute channel in a conference\n" -+; -+ -+static struct ast_cli_entry cli_unmutechannel = { -+ { "conference", "unmutechannel", NULL }, -+ conference_unmutechannel, -+ "unmute channel in a conference", -+ conference_unmutechannel_usage -+} ; -+ -+// -+// play sound -+// -+static char conference_play_sound_usage[] = -+ "usage: conference play sound [mute]\n" -+ " play sound to conference member .\n" -+ " If mute is specified, all other audio is muted while the sound is played back.\n" -+; -+ -+static struct ast_cli_entry cli_play_sound = { -+ { "conference", "play", "sound", NULL }, -+ conference_play_sound, -+ "play a sound to a conference member", -+ conference_play_sound_usage -+} ; -+ -+int conference_play_sound( int fd, int argc, char *argv[] ) -+{ -+ char *channel, *file; -+ int mute = 0; -+ -+ if ( argc < 5 ) -+ return RESULT_SHOWUSAGE ; -+ -+ channel = argv[3]; -+ file = argv[4]; -+ -+ if(argc > 5 && !strcmp(argv[5], "mute")) -+ mute = 1; -+ -+ int res = play_sound_channel(fd, channel, file, mute); -+ -+ if ( !res ) -+ { -+ ast_cli(fd, "Sound playback failed failed\n"); -+ return RESULT_FAILURE; -+ } -+ return RESULT_SUCCESS ; -+} -+ -+// -+// stop sounds -+// -+ -+static char conference_stop_sounds_usage[] = -+ "usage: conference stop sounds \n" -+ " stop sounds for conference member .\n" -+; -+ -+static struct ast_cli_entry cli_stop_sounds = { -+ { "conference", "stop", "sounds", NULL }, -+ conference_stop_sounds, -+ "stop sounds for a conference member", -+ conference_stop_sounds_usage -+} ; -+ -+int conference_stop_sounds( int fd, int argc, char *argv[] ) -+{ -+ char *channel; -+ -+ if ( argc < 4 ) -+ return RESULT_SHOWUSAGE ; -+ -+ channel = argv[3]; -+ -+ int res = stop_sound_channel(fd, channel); -+ -+ if ( !res ) -+ { -+ ast_cli(fd, "Sound stop failed failed\n"); -+ return RESULT_FAILURE; -+ } -+ return RESULT_SUCCESS ; -+} -+ -+// -+// end conference -+// -+ -+static char conference_end_usage[] = -+ "usage: conference end \n" -+ " ends a conference.\n" -+; -+ -+static struct ast_cli_entry cli_end = { -+ { "conference", "end", NULL }, -+ conference_end, -+ "stops a conference", -+ conference_end_usage -+} ; -+ -+int conference_end( int fd, int argc, char *argv[] ) -+{ -+ // check the args length -+ if ( argc < 3 ) -+ return RESULT_SHOWUSAGE ; -+ -+ // conference name -+ const char* name = argv[2] ; -+ -+ // get the conference -+ if ( end_conference( name, 1 ) != 0 ) -+ { -+ ast_cli( fd, "unable to end the conference, name => %s\n", name ) ; -+ return RESULT_SHOWUSAGE ; -+ } -+ -+ return RESULT_SUCCESS ; -+} -+ -+// -+// E.BUU - Manager conference end. Additional option to just kick everybody out -+// without hangin up channels -+// -+int manager_conference_end(struct mansession *s, const struct message *m) -+{ -+ const char *confname = astman_get_header(m,"Conference"); -+ int hangup = 1; -+ -+ const char * h = astman_get_header(m, "Hangup"); -+ if (h) -+ { -+ hangup = atoi(h); -+ } -+ -+ ast_log( LOG_NOTICE, "Terminating conference %s on manager's request. Hangup: %s.\n", confname, hangup?"YES":"NO" ); -+ if ( end_conference( confname, hangup ) != 0 ) -+ { -+ ast_log( LOG_ERROR, "manager end conf: unable to terminate conference %s.\n", confname ); -+ astman_send_error(s, m, "Failed to terminate\r\n"); -+ return RESULT_FAILURE; -+ } -+ -+ astman_send_ack(s, m, "Conference terminated"); -+ return RESULT_SUCCESS; -+} -+// -+// lock conference to a video source -+// -+static char conference_lock_usage[] = -+ "usage: conference lock \n" -+ " locks incoming video stream for conference to member \n" -+; -+ -+static struct ast_cli_entry cli_lock = { -+ { "conference", "lock", NULL }, -+ conference_lock, -+ "locks incoming video to a member", -+ conference_lock_usage -+} ; -+ -+int conference_lock( int fd, int argc, char *argv[] ) -+{ -+ // check args -+ if ( argc < 4 ) -+ return RESULT_SHOWUSAGE; -+ -+ const char *conference = argv[2]; -+ int member; -+ sscanf(argv[3], "%d", &member); -+ -+ int res = lock_conference(conference, member); -+ -+ if ( !res ) -+ { -+ ast_cli(fd, "Locking failed\n"); -+ return RESULT_FAILURE; -+ } -+ -+ return RESULT_SUCCESS; -+} -+ -+// -+// lock conference to a video source channel -+// -+static char conference_lockchannel_usage[] = -+ "usage: conference lockchannel \n" -+ " locks incoming video stream for conference to channel \n" -+; -+ -+static struct ast_cli_entry cli_lockchannel = { -+ { "conference", "lockchannel", NULL }, -+ conference_lockchannel, -+ "locks incoming video to a channel", -+ conference_lockchannel_usage -+} ; -+ -+int conference_lockchannel( int fd, int argc, char *argv[] ) -+{ -+ // check args -+ if ( argc < 4 ) -+ return RESULT_SHOWUSAGE; -+ -+ const char *conference = argv[2]; -+ const char *channel = argv[3]; -+ -+ int res = lock_conference_channel(conference, channel); -+ -+ if ( !res ) -+ { -+ ast_cli(fd, "Locking failed\n"); -+ return RESULT_FAILURE; -+ } -+ -+ return RESULT_SUCCESS; -+} -+ -+// -+// unlock conference -+// -+static char conference_unlock_usage[] = -+ "usage: conference unlock \n" -+ " unlocks conference \n" -+; -+ -+static struct ast_cli_entry cli_unlock = { -+ { "conference", "unlock", NULL }, -+ conference_unlock, -+ "unlocks conference", -+ conference_unlock_usage -+} ; -+ -+int conference_unlock( int fd, int argc, char *argv[] ) -+{ -+ // check args -+ if ( argc < 3 ) -+ return RESULT_SHOWUSAGE; -+ -+ -+ const char *conference = argv[2]; -+ -+ int res = unlock_conference(conference); -+ -+ if ( !res ) -+ { -+ ast_cli(fd, "Unlocking failed\n"); -+ return RESULT_FAILURE; -+ } -+ -+ return RESULT_SUCCESS; -+} -+ -+// -+// Set conference default video source -+// -+static char conference_set_default_usage[] = -+ "usage: conference set default \n" -+ " sets the default video source for conference to member \n" -+ " Use a negative value for member if you want to clear the default\n" -+; -+ -+static struct ast_cli_entry cli_set_default = { -+ { "conference", "set", "default", NULL }, -+ conference_set_default, -+ "sets default video source", -+ conference_set_default_usage -+} ; -+ -+int conference_set_default(int fd, int argc, char *argv[] ) -+{ -+ // check args -+ if ( argc < 5 ) -+ return RESULT_SHOWUSAGE; -+ -+ const char *conference = argv[3]; -+ int member; -+ sscanf(argv[4], "%d", &member); -+ -+ int res = set_default_id(conference, member); -+ -+ if ( !res ) -+ { -+ ast_cli(fd, "Setting default video id failed\n"); -+ return RESULT_FAILURE; -+ } -+ -+ return RESULT_SUCCESS; -+} -+ -+// -+// Set conference default video source channel -+// -+static char conference_set_defaultchannel_usage[] = -+ "usage: conference set defaultchannel \n" -+ " sets the default video source channel for conference to channel \n" -+; -+ -+static struct ast_cli_entry cli_set_defaultchannel = { -+ { "conference", "set", "defaultchannel", NULL }, -+ conference_set_defaultchannel, -+ "sets default video source channel", -+ conference_set_defaultchannel_usage -+} ; -+ -+int conference_set_defaultchannel(int fd, int argc, char *argv[] ) -+{ -+ // check args -+ if ( argc < 5 ) -+ return RESULT_SHOWUSAGE; -+ -+ const char *conference = argv[3]; -+ const char *channel = argv[4]; -+ -+ int res = set_default_channel(conference, channel); -+ -+ if ( !res ) -+ { -+ ast_cli(fd, "Setting default video id failed\n"); -+ return RESULT_FAILURE; -+ } -+ -+ return RESULT_SUCCESS; -+} -+ -+// -+// Mute video from a member -+// -+static char conference_video_mute_usage[] = -+ "usage: conference video mute \n" -+ " mutes video from member in conference \n" -+; -+ -+static struct ast_cli_entry cli_video_mute = { -+ { "conference", "video", "mute", NULL }, -+ conference_video_mute, -+ "mutes video from a member", -+ conference_video_mute_usage -+} ; -+ -+int conference_video_mute(int fd, int argc, char *argv[] ) -+{ -+ // check args -+ if ( argc < 5 ) -+ return RESULT_SHOWUSAGE; -+ -+ const char *conference = argv[3]; -+ int member; -+ sscanf(argv[4], "%d", &member); -+ -+ int res = video_mute_member(conference, member); -+ -+ if ( !res ) -+ { -+ ast_cli(fd, "Muting video from member %d failed\n", member); -+ return RESULT_FAILURE; -+ } -+ -+ return RESULT_SUCCESS; -+} -+ -+// -+// Unmute video from a member -+// -+static char conference_video_unmute_usage[] = -+ "usage: conference video unmute \n" -+ " unmutes video from member in conference \n" -+; -+ -+static struct ast_cli_entry cli_video_unmute = { -+ { "conference", "video", "unmute", NULL }, -+ conference_video_unmute, -+ "unmutes video from a member", -+ conference_video_unmute_usage -+} ; -+ -+int conference_video_unmute(int fd, int argc, char *argv[] ) -+{ -+ // check args -+ if ( argc < 5 ) -+ return RESULT_SHOWUSAGE; -+ -+ const char *conference = argv[3]; -+ int member; -+ sscanf(argv[4], "%d", &member); -+ -+ int res = video_unmute_member(conference, member); -+ -+ if ( !res ) -+ { -+ ast_cli(fd, "Unmuting video from member %d failed\n", member); -+ return RESULT_FAILURE; -+ } -+ -+ return RESULT_SUCCESS; -+} -+ -+// -+// Mute video from a channel -+// -+static char conference_video_mutechannel_usage[] = -+ "usage: conference video mutechannel \n" -+ " mutes video from channel in conference \n" -+; -+ -+static struct ast_cli_entry cli_video_mutechannel = { -+ { "conference", "video", "mutechannel", NULL }, -+ conference_video_mutechannel, -+ "mutes video from a channel", -+ conference_video_mutechannel_usage -+} ; -+ -+int conference_video_mutechannel(int fd, int argc, char *argv[] ) -+{ -+ // check args -+ if ( argc < 5 ) -+ return RESULT_SHOWUSAGE; -+ -+ const char *conference = argv[3]; -+ const char *channel = argv[4]; -+ -+ int res = video_mute_channel(conference, channel); -+ -+ if ( !res ) -+ { -+ ast_cli(fd, "Muting video from channel %s failed\n", channel); -+ return RESULT_FAILURE; -+ } -+ -+ return RESULT_SUCCESS; -+} -+ -+// -+// Unmute video from a channel -+// -+static char conference_video_unmutechannel_usage[] = -+ "usage: conference video unmutechannel \n" -+ " unmutes video from channel in conference \n" -+; -+ -+static struct ast_cli_entry cli_video_unmutechannel = { -+ { "conference", "video", "unmutechannel", NULL }, -+ conference_video_unmutechannel, -+ "unmutes video from a channel", -+ conference_video_unmutechannel_usage -+} ; -+ -+int conference_video_unmutechannel(int fd, int argc, char *argv[] ) -+{ -+ // check args -+ if ( argc < 5 ) -+ return RESULT_SHOWUSAGE; -+ -+ const char *conference = argv[3]; -+ const char *channel = argv[4]; -+ -+ int res = video_unmute_channel(conference, channel); -+ -+ if ( !res ) -+ { -+ ast_cli(fd, "Unmuting video from channel %s failed\n", channel); -+ return RESULT_FAILURE; -+ } -+ -+ return RESULT_SUCCESS; -+} -+ -+ -+// -+// Text message functions -+// Send a text message to a member -+// -+static char conference_text_usage[] = -+ "usage: conference text \n" -+ " Sends text message to member in conference \n" -+; -+ -+static struct ast_cli_entry cli_text = { -+ { "conference", "text", NULL }, -+ conference_text, -+ "sends a text message to a member", -+ conference_text_usage -+} ; -+ -+int conference_text(int fd, int argc, char *argv[] ) -+{ -+ // check args -+ if ( argc < 5 ) -+ return RESULT_SHOWUSAGE; -+ -+ const char *conference = argv[2]; -+ int member; -+ sscanf(argv[3], "%d", &member); -+ const char *text = argv[4]; -+ -+ int res = send_text(conference, member, text); -+ -+ if ( !res ) -+ { -+ ast_cli(fd, "Sending a text message to member %d failed\n", member); -+ return RESULT_FAILURE; -+ } -+ -+ return RESULT_SUCCESS; -+} -+ -+// -+// Send a text message to a channel -+// -+static char conference_textchannel_usage[] = -+ "usage: conference textchannel \n" -+ " Sends text message to channel in conference \n" -+; -+ -+static struct ast_cli_entry cli_textchannel = { -+ { "conference", "textchannel", NULL }, -+ conference_textchannel, -+ "sends a text message to a channel", -+ conference_textchannel_usage -+} ; -+ -+int conference_textchannel(int fd, int argc, char *argv[] ) -+{ -+ // check args -+ if ( argc < 5 ) -+ return RESULT_SHOWUSAGE; -+ -+ const char *conference = argv[2]; -+ const char *channel = argv[3]; -+ const char *text = argv[4]; -+ -+ int res = send_text_channel(conference, channel, text); -+ -+ if ( !res ) -+ { -+ ast_cli(fd, "Sending a text message to channel %s failed\n", channel); -+ return RESULT_FAILURE; -+ } -+ -+ return RESULT_SUCCESS; -+} -+ -+// -+// Send a text message to all members in a conference -+// -+static char conference_textbroadcast_usage[] = -+ "usage: conference textbroadcast \n" -+ " Sends text message to all members in conference \n" -+; -+ -+static struct ast_cli_entry cli_textbroadcast = { -+ { "conference", "textbroadcast", NULL }, -+ conference_textbroadcast, -+ "sends a text message to all members in a conference", -+ conference_textbroadcast_usage -+} ; -+ -+int conference_textbroadcast(int fd, int argc, char *argv[] ) -+{ -+ // check args -+ if ( argc < 4 ) -+ return RESULT_SHOWUSAGE; -+ -+ const char *conference = argv[2]; -+ const char *text = argv[3]; -+ -+ int res = send_text_broadcast(conference, text); -+ -+ if ( !res ) -+ { -+ ast_cli(fd, "Sending a text broadcast to conference %s failed\n", conference); -+ return RESULT_FAILURE; -+ } -+ -+ return RESULT_SUCCESS; -+} -+ -+// -+// Associate two members -+// Audio from the source member will drive VAD based video switching for the destination member -+// If the destination member is missing or negative, break any existing association -+// -+static char conference_drive_usage[] = -+ "usage: conference drive [destination member]\n" -+ " Drives VAD video switching of using audio from in conference \n" -+ " If destination is missing or negative, break existing association\n" -+; -+ -+static struct ast_cli_entry cli_drive = { -+ { "conference", "drive", NULL }, -+ conference_drive, -+ "pairs two members to drive VAD-based video switching", -+ conference_drive_usage -+} ; -+ -+int conference_drive(int fd, int argc, char *argv[] ) -+{ -+ // check args -+ if ( argc < 4 ) -+ return RESULT_SHOWUSAGE; -+ -+ const char *conference = argv[2]; -+ int src_member = -1; -+ int dst_member = -1; -+ sscanf(argv[3], "%d", &src_member); -+ if ( argc > 4 ) -+ sscanf(argv[4], "%d", &dst_member); -+ -+ int res = drive(conference, src_member, dst_member); -+ -+ if ( !res ) -+ { -+ ast_cli(fd, "Pairing members %d and %d failed\n", src_member, dst_member); -+ return RESULT_FAILURE; -+ } -+ -+ return RESULT_SUCCESS; -+} -+ -+// -+// Associate two channels -+// Audio from the source channel will drive VAD based video switching for the destination channel -+// If the destination channel is missing, break any existing association -+// -+static char conference_drivechannel_usage[] = -+ "usage: conference drive [destination channel]\n" -+ " Drives VAD video switching of using audio from in conference \n" -+ " If destination is missing, break existing association\n" -+; -+ -+static struct ast_cli_entry cli_drivechannel = { -+ { "conference", "drivechannel", NULL }, -+ conference_drivechannel, -+ "pairs two channels to drive VAD-based video switching", -+ conference_drivechannel_usage -+} ; -+ -+int conference_drivechannel(int fd, int argc, char *argv[] ) -+{ -+ // check args -+ if ( argc < 4 ) -+ return RESULT_SHOWUSAGE; -+ -+ const char *conference = argv[2]; -+ const char *src_channel = argv[3]; -+ const char *dst_channel = NULL; -+ if ( argc > 4 ) -+ dst_channel = argv[4]; -+ -+ int res = drive_channel(conference, src_channel, dst_channel); -+ -+ if ( !res ) -+ { -+ ast_cli(fd, "Pairing channels %s and %s failed\n", src_channel, dst_channel); -+ return RESULT_FAILURE; -+ } -+ -+ return RESULT_SUCCESS; -+} -+ -+ -+// -+// cli initialization function -+// -+ -+void register_conference_cli( void ) -+{ -+ ast_cli_register( &cli_restart ); -+ ast_cli_register( &cli_debug ) ; -+ ast_cli_register( &cli_show_stats ) ; -+ ast_cli_register( &cli_list ); -+ ast_cli_register( &cli_kick ); -+ ast_cli_register( &cli_kickchannel ); -+ ast_cli_register( &cli_exit ); -+ ast_cli_register( &cli_mute ); -+ ast_cli_register( &cli_mutechannel ); -+ ast_cli_register( &cli_viewstream ); -+ ast_cli_register( &cli_viewchannel ); -+ ast_cli_register( &cli_unmute ); -+ ast_cli_register( &cli_unmutechannel ); -+ ast_cli_register( &cli_play_sound ) ; -+ ast_cli_register( &cli_stop_sounds ) ; -+ ast_cli_register( &cli_end ); -+ ast_cli_register( &cli_lock ); -+ ast_cli_register( &cli_lockchannel ); -+ ast_cli_register( &cli_unlock ); -+ ast_cli_register( &cli_set_default ); -+ ast_cli_register( &cli_set_defaultchannel ); -+ ast_cli_register( &cli_video_mute ) ; -+ ast_cli_register( &cli_video_unmute ) ; -+ ast_cli_register( &cli_video_mutechannel ) ; -+ ast_cli_register( &cli_video_unmutechannel ) ; -+ ast_cli_register( &cli_text ); -+ ast_cli_register( &cli_textchannel ); -+ ast_cli_register( &cli_textbroadcast ); -+ ast_cli_register( &cli_drive ); -+ ast_cli_register( &cli_drivechannel ); -+ ast_manager_register( "ConferenceList", 0, manager_conference_list, "Conference List" ); -+ ast_manager_register( "ConferenceEnd", EVENT_FLAG_CALL, manager_conference_end, "Terminate a conference" ); -+ -+} -+ -+void unregister_conference_cli( void ) -+{ -+ ast_cli_unregister( &cli_restart ); -+ ast_cli_unregister( &cli_debug ) ; -+ ast_cli_unregister( &cli_show_stats ) ; -+ ast_cli_unregister( &cli_list ); -+ ast_cli_unregister( &cli_kick ); -+ ast_cli_unregister( &cli_kickchannel ); -+ ast_cli_unregister( &cli_exit ); -+ ast_cli_unregister( &cli_mute ); -+ ast_cli_unregister( &cli_mutechannel ); -+ ast_cli_unregister( &cli_viewstream ); -+ ast_cli_unregister( &cli_viewchannel ); -+ ast_cli_unregister( &cli_unmute ); -+ ast_cli_unregister( &cli_unmutechannel ); -+ ast_cli_unregister( &cli_play_sound ) ; -+ ast_cli_unregister( &cli_stop_sounds ) ; -+ ast_cli_unregister( &cli_end ); -+ ast_cli_unregister( &cli_lock ); -+ ast_cli_unregister( &cli_lockchannel ); -+ ast_cli_unregister( &cli_unlock ); -+ ast_cli_unregister( &cli_set_default ); -+ ast_cli_unregister( &cli_set_defaultchannel ); -+ ast_cli_unregister( &cli_video_mute ) ; -+ ast_cli_unregister( &cli_video_unmute ) ; -+ ast_cli_unregister( &cli_video_mutechannel ) ; -+ ast_cli_unregister( &cli_video_unmutechannel ) ; -+ ast_cli_unregister( &cli_text ); -+ ast_cli_unregister( &cli_textchannel ); -+ ast_cli_unregister( &cli_textbroadcast ); -+ ast_cli_unregister( &cli_drive ); -+ ast_cli_unregister( &cli_drivechannel ); -+ ast_manager_unregister( "ConferenceList" ); -+ ast_manager_unregister( "ConferenceEnd" ); -+} -diff --git a/apps/conference/cli.h b/apps/conference/cli.h -new file mode 100644 -index 0000000..34b79d6 ---- /dev/null -+++ b/apps/conference/cli.h -@@ -0,0 +1,99 @@ -+ -+// $Id: cli.h 880 2007-04-25 15:23:59Z jpgrayson $ -+ -+/* -+ * app_conference -+ * -+ * A channel independent conference application for Asterisk -+ * -+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH -+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc. -+ * Copyright (C) 2005, 2006 HorizonWimba, Inc. -+ * Copyright (C) 2007 Wimba, Inc. -+ * -+ * Klaus-Peter Junghanns -+ * -+ * Video Conferencing support added by -+ * Neil Stratford -+ * Copyright (C) 2005, 2005 Vipadia Limited -+ * -+ * VAD driven video conferencing, text message support -+ * and miscellaneous enhancements added by -+ * Mihai Balea -+ * -+ * This program may be modified and distributed under the -+ * terms of the GNU General Public License. You should have received -+ * a copy of the GNU General Public License along with this -+ * program; if not, write to the Free Software Foundation, Inc. -+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef _APP_CONF_CLI_H -+#define _APP_CONF_CLI_H -+ -+// -+// includes -+// -+ -+#include "app_conference.h" -+#include "common.h" -+ -+// -+// function declarations -+// -+ -+int conference_show_stats( int fd, int argc, char *argv[] ) ; -+int conference_show_stats_name( int fd, const char* name ) ; -+ -+int conference_restart( int fd, int argc, char *argv[] ); -+ -+int conference_debug( int fd, int argc, char *argv[] ) ; -+int conference_no_debug( int fd, int argc, char *argv[] ) ; -+ -+int conference_list( int fd, int argc, char *argv[] ) ; -+int conference_kick( int fd, int argc, char *argv[] ) ; -+int conference_kickchannel( int fd, int argc, char *argv[] ) ; -+ -+int conference_exit( int fd, int argc, char *argv[] ) ; -+ -+int conference_mute( int fd, int argc, char *argv[] ) ; -+int conference_unmute( int fd, int argc, char *argv[] ) ; -+int conference_mutechannel( int fd, int argc, char *argv[] ) ; -+int conference_unmutechannel( int fd, int argc, char *argv[] ) ; -+int conference_viewstream( int fd, int argc, char *argv[] ) ; -+int conference_viewchannel( int fd, int argc, char *argv[] ) ; -+ -+int conference_play_sound( int fd, int argc, char *argv[] ) ; -+int conference_stop_sounds( int fd, int argc, char *argv[] ) ; -+ -+int conference_play_video( int fd, int argc, char *argv[] ) ; -+int conference_stop_videos( int fd, int argc, char *argv[] ) ; -+ -+int conference_end( int fd, int argc, char *argv[] ) ; -+ -+int conference_lock( int fd, int argc, char *argv[] ) ; -+int conference_lockchannel( int fd, int argc, char *argv[] ) ; -+int conference_unlock( int fd, int argc, char *argv[] ) ; -+ -+int conference_set_default(int fd, int argc, char *argv[] ) ; -+int conference_set_defaultchannel(int fd, int argc, char *argv[] ) ; -+ -+int conference_video_mute(int fd, int argc, char *argv[] ) ; -+int conference_video_mutechannel(int fd, int argc, char *argv[] ) ; -+int conference_video_unmute(int fd, int argc, char *argv[] ) ; -+int conference_video_unmutechannel(int fd, int argc, char *argv[] ) ; -+ -+int conference_text( int fd, int argc, char *argv[] ) ; -+int conference_textchannel( int fd, int argc, char *argv[] ) ; -+int conference_textbroadcast( int fd, int argc, char *argv[] ) ; -+ -+int conference_drive( int fd, int argc, char *argv[] ) ; -+int conference_drivechannel(int fd, int argc, char *argv[] ); -+ -+int manager_conference_end(struct mansession *s, const struct message *m); -+ -+void register_conference_cli( void ) ; -+void unregister_conference_cli( void ) ; -+ -+ -+#endif -diff --git a/apps/conference/common.h b/apps/conference/common.h -new file mode 100644 -index 0000000..989398c ---- /dev/null -+++ b/apps/conference/common.h -@@ -0,0 +1,63 @@ -+ -+// $Id: common.h 880 2007-04-25 15:23:59Z jpgrayson $ -+ -+/* -+ * app_conference -+ * -+ * A channel independent conference application for Asterisk -+ * -+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH -+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc. -+ * Copyright (C) 2005, 2006 HorizonWimba, Inc. -+ * Copyright (C) 2007 Wimba, Inc. -+ * -+ * Klaus-Peter Junghanns -+ * -+ * Video Conferencing support added by -+ * Neil Stratford -+ * Copyright (C) 2005, 2005 Vipadia Limited -+ * -+ * VAD driven video conferencing, text message support -+ * and miscellaneous enhancements added by -+ * Mihai Balea -+ * -+ * This program may be modified and distributed under the -+ * terms of the GNU General Public License. You should have received -+ * a copy of the GNU General Public License along with this -+ * program; if not, write to the Free Software Foundation, Inc. -+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef _APP_CONF_COMMON_H -+#define _APP_CONF_COMMON_H -+ -+#include -+ -+// typedef includes -+#include "conf_frame.h" -+ -+// function includesee -+//#include "member.h" -+#include "conference.h" -+#include "frame.h" -+#include "cli.h" -+ -+/* Utility functions */ -+ -+/* LOG the time taken to execute a function (like lock acquisition */ -+#if 1 -+#define TIMELOG(func,min,message) \ -+ do { \ -+ struct timeval t1, t2; \ -+ int diff; \ -+ t1 = ast_tvnow(); \ -+ func; \ -+ t2 = ast_tvnow(); \ -+ if ( (diff = ast_tvdiff_ms(t2, t1)) > min ) \ -+ ast_log( AST_CONF_DEBUG, "TimeLog: %s: %d ms\n", message, diff); \ -+ } while (0) -+#else -+#define TIMELOG(func,min,message) func -+#endif -+ -+#endif -diff --git a/apps/conference/conf_frame.h b/apps/conference/conf_frame.h -new file mode 100644 -index 0000000..e73e57a ---- /dev/null -+++ b/apps/conference/conf_frame.h -@@ -0,0 +1,73 @@ -+ -+// $Id: conf_frame.h 880 2007-04-25 15:23:59Z jpgrayson $ -+ -+/* -+ * app_conference -+ * -+ * A channel independent conference application for Asterisk -+ * -+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH -+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc. -+ * Copyright (C) 2005, 2006 HorizonWimba, Inc. -+ * Copyright (C) 2007 Wimba, Inc. -+ * -+ * Klaus-Peter Junghanns -+ * -+ * Video Conferencing support added by -+ * Neil Stratford -+ * Copyright (C) 2005, 2005 Vipadia Limited -+ * -+ * VAD driven video conferencing, text message support -+ * and miscellaneous enhancements added by -+ * Mihai Balea -+ * -+ * This program may be modified and distributed under the -+ * terms of the GNU General Public License. You should have received -+ * a copy of the GNU General Public License along with this -+ * program; if not, write to the Free Software Foundation, Inc. -+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef _APP_CONF_STRUCTS_H -+#define _APP_CONF_STRUCTS_H -+ -+// -+// includes -+// -+ -+#include "app_conference.h" -+#include "common.h" -+ -+// -+// struct declarations -+// -+ -+typedef struct conf_frame -+{ -+ // frame audio data -+ struct ast_frame* fr ; -+ -+ // array of converted versions for listeners -+ struct ast_frame* converted[ AC_SUPPORTED_FORMATS ] ; -+ -+ // pointer to the frame's owner -+ struct ast_conf_member* member ; // who sent this frame -+ -+ // frame meta data -+// struct timeval timestamp ; -+// unsigned long cycleid ; -+// int priority ; -+ -+ // linked-list pointers -+ struct conf_frame* next ; -+ struct conf_frame* prev ; -+ -+ // should this frame be preserved -+ short static_frame ; -+ -+ // pointer to mixing buffer -+ char* mixed_buffer ; -+} conf_frame ; -+ -+ -+#endif -diff --git a/apps/conference/conference.c b/apps/conference/conference.c -new file mode 100644 -index 0000000..72c71d9 ---- /dev/null -+++ b/apps/conference/conference.c -@@ -0,0 +1,2923 @@ -+ -+// $Id: conference.c 886 2007-08-06 14:33:34Z bcholew $ -+ -+/* -+ * app_conference -+ * -+ * A channel independent conference application for Asterisk -+ * -+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH -+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc. -+ * Copyright (C) 2005, 2006 HorizonWimba, Inc. -+ * Copyright (C) 2007 Wimba, Inc. -+ * -+ * Klaus-Peter Junghanns -+ * -+ * Video Conferencing support added by -+ * Neil Stratford -+ * Copyright (C) 2005, 2005 Vipadia Limited -+ * -+ * VAD driven video conferencing, text message support -+ * and miscellaneous enhancements added by -+ * Mihai Balea -+ * -+ * This program may be modified and distributed under the -+ * terms of the GNU General Public License. You should have received -+ * a copy of the GNU General Public License along with this -+ * program; if not, write to the Free Software Foundation, Inc. -+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#include "asterisk/autoconfig.h" -+#include "conference.h" -+#include "asterisk/utils.h" -+ -+// -+// static variables -+// -+ -+// single-linked list of current conferences -+struct ast_conference *conflist = NULL ; -+ -+// mutex for synchronizing access to conflist -+//static ast_mutex_t conflist_lock = AST_MUTEX_INITIALIZER ; -+AST_MUTEX_DEFINE_STATIC(conflist_lock); -+ -+static int conference_count = 0 ; -+ -+ -+// -+// main conference function -+// -+ -+void conference_exec( struct ast_conference *conf ) -+{ -+ -+ struct ast_conf_member *next_member; -+ struct ast_conf_member *member, *video_source_member, *dtmf_source_member;; -+ struct conf_frame *cfr, *spoken_frames, *send_frames; -+ -+ // count number of speakers, number of listeners -+ int speaker_count ; -+ int listener_count ; -+ -+ ast_log( AST_CONF_DEBUG, "Entered conference_exec, name => %s\n", conf->name ) ; -+ -+ // timer timestamps -+ struct timeval base, curr, notify ; -+ base = notify = ast_tvnow(); -+ -+ // holds differences of curr and base -+ long time_diff = 0 ; -+ long time_sleep = 0 ; -+ -+ int since_last_slept = 0 ; -+ -+ // -+ // variables for checking thread frequency -+ // -+ -+ // count to AST_CONF_FRAMES_PER_SECOND -+ int tf_count = 0 ; -+ long tf_diff = 0 ; -+ float tf_frequency = 0.0 ; -+ -+ struct timeval tf_base, tf_curr ; -+ tf_base = ast_tvnow(); -+ -+ // -+ // main conference thread loop -+ // -+ -+ -+ while ( 42 == 42 ) -+ { -+ // update the current timestamp -+ curr = ast_tvnow(); -+ -+ // calculate difference in timestamps -+ time_diff = ast_tvdiff_ms(curr, base); -+ -+ // calculate time we should sleep -+ time_sleep = AST_CONF_FRAME_INTERVAL - time_diff ; -+ -+ if ( time_sleep > 0 ) -+ { -+ // sleep for sleep_time ( as milliseconds ) -+ usleep( time_sleep * 1000 ) ; -+ -+ // reset since last slept counter -+ since_last_slept = 0 ; -+ -+ continue ; -+ } -+ else -+ { -+ // long sleep warning -+ if ( -+ since_last_slept == 0 -+ && time_diff > AST_CONF_CONFERENCE_SLEEP * 2 -+ ) -+ { -+ ast_log( -+ AST_CONF_DEBUG, -+ "long scheduling delay, time_diff => %ld, AST_CONF_FRAME_INTERVAL => %d\n", -+ time_diff, AST_CONF_FRAME_INTERVAL -+ ) ; -+ } -+ -+ // increment times since last slept -+ ++since_last_slept ; -+ -+ // sleep every other time -+ if ( since_last_slept % 2 ) -+ usleep( 0 ) ; -+ } -+ -+ // adjust the timer base ( it will be used later to timestamp outgoing frames ) -+ add_milliseconds( &base, AST_CONF_FRAME_INTERVAL ) ; -+ -+ // -+ // check thread frequency -+ // -+ -+ if ( ++tf_count >= AST_CONF_FRAMES_PER_SECOND ) -+ { -+ // update current timestamp -+ tf_curr = ast_tvnow(); -+ -+ // compute timestamp difference -+ tf_diff = ast_tvdiff_ms(tf_curr, tf_base); -+ -+ // compute sampling frequency -+ tf_frequency = ( float )( tf_diff ) / ( float )( tf_count ) ; -+ -+ if ( -+ ( tf_frequency <= ( float )( AST_CONF_FRAME_INTERVAL - 1 ) ) -+ || ( tf_frequency >= ( float )( AST_CONF_FRAME_INTERVAL + 1 ) ) -+ ) -+ { -+ ast_log( -+ LOG_WARNING, -+ "processed frame frequency variation, name => %s, tf_count => %d, tf_diff => %ld, tf_frequency => %2.4f\n", -+ conf->name, tf_count, tf_diff, tf_frequency -+ ) ; -+ } -+ -+ // reset values -+ tf_base = tf_curr ; -+ tf_count = 0 ; -+ } -+ -+ //-----------------// -+ // INCOMING FRAMES // -+ //-----------------// -+ -+ // ast_log( AST_CONF_DEBUG, "PROCESSING FRAMES, conference => %s, step => %d, ms => %ld\n", -+ // conf->name, step, ( base.tv_usec / 20000 ) ) ; -+ -+ // -+ // check if the conference is empty and if so -+ // remove it and break the loop -+ // -+ -+ // acquire the conference list lock -+ ast_mutex_lock(&conflist_lock); -+ -+ // acquire the conference mutex -+ ast_mutex_lock(&conf->lock); -+ -+ if ( conf->membercount == 0 ) -+ { -+ if (conf->debug_flag) -+ { -+ ast_log( LOG_NOTICE, "removing conference, count => %d, name => %s\n", conf->membercount, conf->name ) ; -+ } -+ remove_conf( conf ) ; // stop the conference -+ -+ // We don't need to release the conf mutex, since it was destroyed anyway -+ -+ // release the conference list lock -+ ast_mutex_unlock(&conflist_lock); -+ -+ break ; // break from main processing loop -+ } -+ -+ // release the conference mutex -+ ast_mutex_unlock(&conf->lock); -+ -+ // release the conference list lock -+ ast_mutex_unlock(&conflist_lock); -+ -+ -+ // -+ // Start processing frames -+ // -+ -+ // acquire conference mutex -+ TIMELOG(ast_mutex_lock( &conf->lock ),1,"conf thread conf lock"); -+ -+ if ( conf->membercount == 0 ) -+ { -+ // release the conference mutex -+ ast_mutex_unlock(&conf->lock); -+ continue; // We'll check again at the top of the loop -+ } -+ -+ // update the current delivery time -+ conf->delivery_time = base ; -+ -+ // -+ // loop through the list of members -+ // ( conf->memberlist is a single-linked list ) -+ // -+ -+ // ast_log( AST_CONF_DEBUG, "begin processing incoming audio, name => %s\n", conf->name ) ; -+ -+ // reset speaker and listener count -+ speaker_count = 0 ; -+ listener_count = 0 ; -+ -+ // get list of conference members -+ member = conf->memberlist ; -+ -+ // reset pointer lists -+ spoken_frames = NULL ; -+ -+ // reset video source -+ video_source_member = NULL; -+ -+ // reset dtmf source -+ dtmf_source_member = NULL; -+ -+ // loop over member list to retrieve queued frames -+ while ( member != NULL ) -+ { -+ // take note of next member - before it's too late -+ next_member = member->next; -+ -+ // this MIGHT delete member -+ member_process_spoken_frames(conf,member,&spoken_frames,time_diff, -+ &listener_count, &speaker_count); -+ -+ // adjust our pointer to the next inline -+ member = next_member; -+ } -+ -+ // ast_log( AST_CONF_DEBUG, "finished processing incoming audio, name => %s\n", conf->name ) ; -+ -+ -+ //---------------// -+ // MIXING FRAMES // -+ //---------------// -+ -+ // mix frames and get batch of outgoing frames -+ send_frames = mix_frames( spoken_frames, speaker_count, listener_count ) ; -+ -+ // accounting: if there are frames, count them as one incoming frame -+ if ( send_frames != NULL ) -+ { -+ // set delivery timestamp -+ //set_conf_frame_delivery( send_frames, base ) ; -+// ast_log ( LOG_WARNING, "base = %d,%d: conf->delivery_time = %d,%d\n",base.tv_sec,base.tv_usec, conf->delivery_time.tv_sec, conf->delivery_time.tv_usec); -+ -+ // ast_log( AST_CONF_DEBUG, "base => %ld.%ld %d\n", base.tv_sec, base.tv_usec, ( int )( base.tv_usec / 1000 ) ) ; -+ -+ conf->stats.frames_in++ ; -+ } -+ -+ //-----------------// -+ // OUTGOING FRAMES // -+ //-----------------// -+ -+ // -+ // loop over member list to queue outgoing frames -+ // -+ for ( member = conf->memberlist ; member != NULL ; member = member->next ) -+ { -+ member_process_outgoing_frames(conf, member, send_frames); -+ } -+ -+ //-------// -+ // VIDEO // -+ //-------// -+ -+ // loop over the incoming frames and send to all outgoing -+ // TODO: this is an O(n^2) algorithm. Can we speed it up without sacrificing per-member switching? -+ for (video_source_member = conf->memberlist; -+ video_source_member != NULL; -+ video_source_member = video_source_member->next) -+ { -+ while ((cfr = get_incoming_video_frame( video_source_member ))) -+ { -+ for (member = conf->memberlist; member != NULL; member = member->next) -+ { -+ // skip members that are not ready or are not supposed to receive video -+ if ( !member->ready_for_outgoing || member->norecv_video ) -+ continue ; -+ -+ if ( conf->video_locked ) -+ { -+ // Always send video from the locked source -+ if ( conf->current_video_source_id == video_source_member->id ) -+ queue_outgoing_video_frame(member, cfr->fr, conf->delivery_time); -+ } else -+ { -+ // If the member has vad switching disabled and dtmf switching enabled, use that -+ if ( member->dtmf_switch && -+ !member->vad_switch && -+ member->req_id == video_source_member->id -+ ) -+ { -+ queue_outgoing_video_frame(member, cfr->fr, conf->delivery_time); -+ } else -+ { -+ // If no dtmf switching, then do VAD switching -+ // The VAD switching decision code should make sure that our video source -+ // is legit -+ if ( (conf->current_video_source_id == video_source_member->id) || -+ (conf->current_video_source_id < 0 && -+ conf->default_video_source_id == video_source_member->id -+ ) -+ ) -+ { -+ queue_outgoing_video_frame(member, cfr->fr, conf->delivery_time); -+ } -+ } -+ -+ -+ } -+ } -+ // Garbage collection -+ delete_conf_frame(cfr); -+ } -+ } -+ -+ //------// -+ // DTMF // -+ //------// -+ -+ // loop over the incoming frames and send to all outgoing -+ for (dtmf_source_member = conf->memberlist; dtmf_source_member != NULL; dtmf_source_member = dtmf_source_member->next) -+ { -+ while ((cfr = get_incoming_dtmf_frame( dtmf_source_member ))) -+ { -+ for (member = conf->memberlist; member != NULL; member = member->next) -+ { -+ // skip members that are not ready -+ if ( member->ready_for_outgoing == 0 ) -+ { -+ continue ; -+ } -+ -+ if (member != dtmf_source_member) -+ { -+ // Send the latest frame -+ queue_outgoing_dtmf_frame(member, cfr->fr); -+ } -+ } -+ // Garbage collection -+ delete_conf_frame(cfr); -+ } -+ } -+ -+ //---------// -+ // CLEANUP // -+ //---------// -+ -+ // clean up send frames -+ while ( send_frames != NULL ) -+ { -+ // accouting: count all frames and mixed frames -+ if ( send_frames->member == NULL ) -+ conf->stats.frames_out++ ; -+ else -+ conf->stats.frames_mixed++ ; -+ -+ // delete the frame -+ send_frames = delete_conf_frame( send_frames ) ; -+ } -+ -+ // -+ // notify the manager of state changes every 100 milliseconds -+ // we piggyback on this for VAD switching logic -+ // -+ -+ if ( ( ast_tvdiff_ms(curr, notify) / AST_CONF_NOTIFICATION_SLEEP ) >= 1 ) -+ { -+ // Do VAD switching logic -+ // We need to do this here since send_state_change_notifications -+ // resets the flags -+ if ( !conf->video_locked ) -+ do_VAD_switching(conf); -+ -+ // send the notifications -+ send_state_change_notifications( conf->memberlist ) ; -+ -+ // increment the notification timer base -+ add_milliseconds( ¬ify, AST_CONF_NOTIFICATION_SLEEP ) ; -+ } -+ -+ // release conference mutex -+ ast_mutex_unlock( &conf->lock ) ; -+ -+ // !!! TESTING !!! -+ // usleep( 1 ) ; -+ } -+ // end while ( 42 == 42 ) -+ -+ // -+ // exit the conference thread -+ // -+ -+ ast_log( AST_CONF_DEBUG, "exit conference_exec\n" ) ; -+ -+ // exit the thread -+ pthread_exit( NULL ) ; -+ -+ return ; -+} -+ -+// -+// manange conference functions -+// -+ -+// called by app_conference.c:load_module() -+void init_conference( void ) -+{ -+ ast_mutex_init( &conflist_lock ) ; -+} -+ -+struct ast_conference* start_conference( struct ast_conf_member* member ) -+{ -+ // check input -+ if ( member == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to handle null member\n" ) ; -+ return NULL ; -+ } -+ -+ struct ast_conference* conf = NULL ; -+ -+ // acquire the conference list lock -+ ast_mutex_lock(&conflist_lock); -+ -+ -+ -+ // look for an existing conference -+ ast_log( AST_CONF_DEBUG, "attempting to find requested conference\n" ) ; -+ conf = find_conf( member->conf_name ) ; -+ -+ // unable to find an existing conference, try to create one -+ if ( conf == NULL ) -+ { -+ // create a new conference -+ ast_log( AST_CONF_DEBUG, "attempting to create requested conference\n" ) ; -+ -+ // create the new conference with one member -+ conf = create_conf( member->conf_name, member ) ; -+ -+ // return an error if create_conf() failed -+ if ( conf == NULL ) -+ ast_log( LOG_ERROR, "unable to find or create requested conference\n" ) ; -+ } -+ else -+ { -+ // -+ // existing conference found, add new member to the conference -+ // -+ // once we call add_member(), this thread -+ // is responsible for calling delete_member() -+ // -+ add_member( member, conf ) ; -+ } -+ -+ // release the conference list lock -+ ast_mutex_unlock(&conflist_lock); -+ -+ return conf ; -+} -+ -+// This function should be called with conflist_lock mutex being held -+struct ast_conference* find_conf( const char* name ) -+{ -+ // no conferences exist -+ if ( conflist == NULL ) -+ { -+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", name ) ; -+ return NULL ; -+ } -+ -+ struct ast_conference *conf = conflist ; -+ -+ // loop through conf list -+ while ( conf != NULL ) -+ { -+ if ( strncasecmp( (char*)&(conf->name), name, 80 ) == 0 ) -+ { -+ // found conf name match -+ ast_log( AST_CONF_DEBUG, "found conference in conflist, name => %s\n", name ) ; -+ return conf; -+ } -+ conf = conf->next ; -+ } -+ -+ ast_log( AST_CONF_DEBUG, "unable to find conference in conflist, name => %s\n", name ) ; -+ return NULL; -+} -+ -+// This function should be called with conflist_lock held -+struct ast_conference* create_conf( char* name, struct ast_conf_member* member ) -+{ -+ ast_log( AST_CONF_DEBUG, "entered create_conf, name => %s\n", name ) ; -+ -+ // -+ // allocate memory for conference -+ // -+ -+ struct ast_conference *conf = malloc( sizeof( struct ast_conference ) ) ; -+ -+ if ( conf == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to malloc ast_conference\n" ) ; -+ return NULL ; -+ } -+ -+ // -+ // initialize conference -+ // -+ -+ conf->next = NULL ; -+ conf->memberlist = NULL ; -+ -+ conf->membercount = 0 ; -+ conf->conference_thread = -1 ; -+ -+ conf->debug_flag = 0 ; -+ -+ conf->id_count = 0; -+ -+ conf->default_video_source_id = -1; -+ conf->current_video_source_id = -1; -+ //conf->current_video_source_timestamp = ast_tvnow(); -+ conf->video_locked = 0; -+ -+ // zero stats -+ memset( &conf->stats, 0x0, sizeof( ast_conference_stats ) ) ; -+ -+ // record start time -+ conf->stats.time_entered = ast_tvnow(); -+ -+ // copy name to conference -+ strncpy( (char*)&(conf->name), name, sizeof(conf->name) - 1 ) ; -+ strncpy( (char*)&(conf->stats.name), name, sizeof(conf->name) - 1 ) ; -+ -+ // initialize mutexes -+ ast_mutex_init( &conf->lock ) ; -+ -+ // build translation paths -+ conf->from_slinear_paths[ AC_SLINEAR_INDEX ] = NULL ; -+ conf->from_slinear_paths[ AC_ULAW_INDEX ] = ast_translator_build_path( AST_FORMAT_ULAW, AST_FORMAT_SLINEAR ) ; -+ conf->from_slinear_paths[ AC_ALAW_INDEX ] = ast_translator_build_path( AST_FORMAT_ALAW, AST_FORMAT_SLINEAR ) ; -+ conf->from_slinear_paths[ AC_GSM_INDEX ] = ast_translator_build_path( AST_FORMAT_GSM, AST_FORMAT_SLINEAR ) ; -+ conf->from_slinear_paths[ AC_SPEEX_INDEX ] = ast_translator_build_path( AST_FORMAT_SPEEX, AST_FORMAT_SLINEAR ) ; -+#ifdef AC_USE_G729A -+ conf->from_slinear_paths[ AC_G729A_INDEX ] = ast_translator_build_path( AST_FORMAT_G729A, AST_FORMAT_SLINEAR ) ; -+#endif -+ -+ // add the initial member -+ add_member( member, conf ) ; -+ -+ ast_log( AST_CONF_DEBUG, "added new conference to conflist, name => %s\n", name ) ; -+ -+ // -+ // spawn thread for new conference, using conference_exec( conf ) -+ // -+ // acquire conference mutexes -+ ast_mutex_lock( &conf->lock ) ; -+ -+ if ( ast_pthread_create( &conf->conference_thread, NULL, (void*)conference_exec, conf ) == 0 ) -+ { -+ // detach the thread so it doesn't leak -+ pthread_detach( conf->conference_thread ) ; -+ -+ // prepend new conference to conflist -+ conf->next = conflist ; -+ conflist = conf ; -+ -+ // release conference mutexes -+ ast_mutex_unlock( &conf->lock ) ; -+ -+ ast_log( AST_CONF_DEBUG, "started conference thread for conference, name => %s\n", conf->name ) ; -+ } -+ else -+ { -+ ast_log( LOG_ERROR, "unable to start conference thread for conference %s\n", conf->name ) ; -+ -+ conf->conference_thread = -1 ; -+ -+ // release conference mutexes -+ ast_mutex_unlock( &conf->lock ) ; -+ -+ // clean up conference -+ free( conf ) ; -+ conf = NULL ; -+ } -+ -+ // count new conference -+ if ( conf != NULL ) -+ ++conference_count ; -+ -+ return conf ; -+} -+ -+//This function should be called with conflist_lock and conf->lock held -+void remove_conf( struct ast_conference *conf ) -+{ -+ int c; -+ -+ // ast_log( AST_CONF_DEBUG, "attempting to remove conference, name => %s\n", conf->name ) ; -+ -+ struct ast_conference *conf_current = conflist ; -+ struct ast_conference *conf_temp = NULL ; -+ -+ // loop through list of conferences -+ while ( conf_current != NULL ) -+ { -+ // if conf_current point to the passed conf, -+ if ( conf_current == conf ) -+ { -+ if ( conf_temp == NULL ) -+ { -+ // this is the first conf in the list, so we just point -+ // conflist past the current conf to the next -+ conflist = conf_current->next ; -+ } -+ else -+ { -+ // this is not the first conf in the list, so we need to -+ // point the preceeding conf to the next conf in the list -+ conf_temp->next = conf_current->next ; -+ } -+ -+ // -+ // do some frame clean up -+ // -+ -+ for ( c = 0 ; c < AC_SUPPORTED_FORMATS ; ++c ) -+ { -+ // free the translation paths -+ if ( conf_current->from_slinear_paths[ c ] != NULL ) -+ { -+ ast_translator_free_path( conf_current->from_slinear_paths[ c ] ) ; -+ conf_current->from_slinear_paths[ c ] = NULL ; -+ } -+ } -+ -+ // calculate time in conference -+ // total time converted to seconds -+ long tt = ast_tvdiff_ms(ast_tvnow(), -+ conf_current->stats.time_entered) / 1000; -+ -+ // report accounting information -+ if (conf->debug_flag) -+ { -+ ast_log( LOG_NOTICE, "conference accounting, fi => %ld, fo => %ld, fm => %ld, tt => %ld\n", -+ conf_current->stats.frames_in, conf_current->stats.frames_out, conf_current->stats.frames_mixed, tt ) ; -+ -+ ast_log( AST_CONF_DEBUG, "removed conference, name => %s\n", conf_current->name ) ; -+ } -+ -+ ast_mutex_unlock( &conf_current->lock ) ; -+ -+ free( conf_current ) ; -+ conf_current = NULL ; -+ -+ break ; -+ } -+ -+ // save a refence to the soon to be previous conf -+ conf_temp = conf_current ; -+ -+ // move conf_current to the next in the list -+ conf_current = conf_current->next ; -+ } -+ -+ // count new conference -+ --conference_count ; -+ -+ return ; -+} -+ -+int get_new_id( struct ast_conference *conf ) -+{ -+ // must have the conf lock when calling this -+ int newid; -+ struct ast_conf_member *othermember; -+ // get a video ID for this member -+ newid = 0; -+ othermember = conf->memberlist; -+ while (othermember) -+ { -+ if (othermember->id == newid) -+ { -+ newid++; -+ othermember = conf->memberlist; -+ } -+ else -+ { -+ othermember = othermember->next; -+ } -+ } -+ return newid; -+} -+ -+ -+int end_conference(const char *name, int hangup ) -+{ -+ struct ast_conference *conf; -+ -+ // acquire the conference list lock -+ ast_mutex_lock(&conflist_lock); -+ -+ conf = find_conf(name); -+ if ( conf == NULL ) -+ { -+ ast_log( LOG_WARNING, "could not find conference\n" ) ; -+ -+ // release the conference list lock -+ ast_mutex_unlock(&conflist_lock); -+ -+ return -1 ; -+ } -+ -+ // acquire the conference lock -+ ast_mutex_lock( &conf->lock ) ; -+ -+ // get list of conference members -+ struct ast_conf_member* member = conf->memberlist ; -+ -+ // loop over member list and request hangup -+ while ( member != NULL ) -+ { -+ // acquire member mutex and request hangup -+ // or just kick -+ ast_mutex_lock( &member->lock ) ; -+ if (hangup) -+ ast_softhangup( member->chan, 1 ) ; -+ else -+ member->kick_flag = 1; -+ ast_mutex_unlock( &member->lock ) ; -+ -+ // go on to the next member -+ // ( we have the conf lock, so we know this is okay ) -+ member = member->next ; -+ } -+ -+ // release the conference lock -+ ast_mutex_unlock( &conf->lock ) ; -+ -+ // release the conference list lock -+ ast_mutex_unlock(&conflist_lock); -+ -+ return 0 ; -+} -+ -+// -+// member-related functions -+// -+ -+// This function should be called with conflist_lock held -+void add_member( struct ast_conf_member *member, struct ast_conference *conf ) -+{ -+ int newid, last_id; -+ struct ast_conf_member *othermember; -+ int count; -+ -+ if ( conf == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to add member to NULL conference\n" ) ; -+ return ; -+ } -+ -+ // acquire the conference lock -+ ast_mutex_lock( &conf->lock ) ; -+ -+ if (member->id < 0) -+ { -+ // get an ID for this member -+ newid = get_new_id( conf ); -+ member->id = newid; -+ } else -+ { -+ // boot anyone who has this id already -+ othermember = conf->memberlist; -+ while (othermember) -+ { -+ if (othermember->id == member->id) -+ othermember->id = -1; -+ othermember = othermember->next; -+ } -+ } -+ -+ if ( member->mute_video ) -+ { -+ send_text_message_to_member(member, AST_CONF_CONTROL_STOP_VIDEO); -+ } -+ -+ // set a long term id -+ int new_initial_id = 0; -+ othermember = conf->memberlist; -+ while (othermember) -+ { -+ if (othermember->initial_id >= new_initial_id) -+ new_initial_id++; -+ -+ othermember = othermember->next; -+ } -+ member->initial_id = new_initial_id; -+ -+ -+ ast_log( AST_CONF_DEBUG, "new video id %d\n", newid) ; -+ -+ if (conf->memberlist) last_id = conf->memberlist->id; -+ else last_id = 0; -+ -+ if (member->req_id < 0) // otherwise pre-selected in create_member -+ { -+ // want to watch the last person to 0 or 1 (for now) -+ if (member->id > 0) member->req_id = 0; -+ else member->req_id = 1; -+ } -+ -+ member->next = conf->memberlist ; // next is now list -+ conf->memberlist = member ; // member is now at head of list -+ -+ // update conference stats -+ count = count_member( member, conf, 1 ) ; -+ -+ ast_log( AST_CONF_DEBUG, "member added to conference, name => %s\n", conf->name ) ; -+ -+ // release the conference lock -+ ast_mutex_unlock( &conf->lock ) ; -+ -+ return ; -+} -+ -+int remove_member( struct ast_conf_member* member, struct ast_conference* conf ) -+{ -+ // check for member -+ if ( member == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to remove NULL member\n" ) ; -+ return -1 ; -+ } -+ -+ // check for conference -+ if ( conf == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to remove member from NULL conference\n" ) ; -+ return -1 ; -+ } -+ -+ // -+ // loop through the member list looking -+ // for the requested member -+ // -+ -+ ast_mutex_lock( &conf->lock ); -+ -+ struct ast_conf_member *member_list = conf->memberlist ; -+ struct ast_conf_member *member_temp = NULL ; -+ -+ int count = -1 ; // default return code -+ -+ while ( member_list != NULL ) -+ { -+ // set conference to send no_video to anyone who was watching us -+ ast_mutex_lock( &member_list->lock ) ; -+ if (member_list->req_id == member->id) -+ { -+ member_list->conference = 1; -+ } -+ ast_mutex_unlock( &member_list->lock ) ; -+ member_list = member_list->next ; -+ } -+ -+ member_list = conf->memberlist ; -+ -+ int member_is_moderator = member->ismoderator; -+ -+ while ( member_list != NULL ) -+ { -+ // If member is driven by the currently visited member, break the association -+ if ( member_list->driven_member == member ) -+ { -+ // Acquire member mutex -+ ast_mutex_lock(&member_list->lock); -+ -+ member_list->driven_member = NULL; -+ -+ // Release member mutex -+ ast_mutex_unlock(&member_list->lock); -+ } -+ -+ if ( member_list == member ) -+ { -+ -+ // -+ // log some accounting information -+ // -+ -+ // calculate time in conference (in seconds) -+ long tt = ast_tvdiff_ms(ast_tvnow(), -+ member->time_entered) / 1000; -+ -+ if (conf->debug_flag) -+ { -+ ast_log( -+ LOG_NOTICE, -+ "member accounting, channel => %s, te => %ld, fi => %ld, fid => %ld, fo => %ld, fod => %ld, tt => %ld\n", -+ member->channel_name, -+ member->time_entered.tv_sec, member->frames_in, member->frames_in_dropped, -+ member->frames_out, member->frames_out_dropped, tt -+ ) ; -+ } -+ -+ // -+ // if this is the first member in the linked-list, -+ // skip over the first member in the list, else -+ // -+ // point the previous 'next' to the current 'next', -+ // thus skipping the current member in the list -+ // -+ if ( member_temp == NULL ) -+ conf->memberlist = member->next ; -+ else -+ member_temp->next = member->next ; -+ -+ // update conference stats -+ count = count_member( member, conf, 0 ) ; -+ -+ // Check if member is the default or current video source -+ if ( conf->current_video_source_id == member->id ) -+ { -+ if ( conf->video_locked ) -+ unlock_conference(conf->name); -+ do_video_switching(conf, conf->default_video_source_id, 0); -+ } else if ( conf->default_video_source_id == member->id ) -+ { -+ conf->default_video_source_id = -1; -+ } -+ -+ // output to manager... -+ manager_event( -+ EVENT_FLAG_CALL, -+ "ConferenceLeave", -+ "ConferenceName: %s\r\n" -+ "Member: %d\r\n" -+ "Channel: %s\r\n" -+ "CallerID: %s\r\n" -+ "CallerIDName: %s\r\n" -+ "Duration: %ld\r\n" -+ "Count: %d\r\n", -+ conf->name, -+ member->id, -+ member->channel_name, -+ member->callerid, -+ member->callername, -+ tt, count -+ ) ; -+ -+ // save a pointer to the current member, -+ // and then point to the next member in the list -+ member_list = member_list->next ; -+ -+ // leave member_temp alone. -+ // it already points to the previous (or NULL). -+ // it will still be the previous after member is deleted -+ -+ // notify others about leave -+ char * leave_snd = member->leave_snd; -+ if (conf->membercount && strcmp(leave_snd, "-")>1) -+ { -+ struct ast_conf_member *membertest = conf->memberlist; -+ while (membertest != NULL) -+ { -+ if (membertest != member) -+ { -+ // lock member for basic_play_sound -+ ast_mutex_lock(&membertest->lock); -+ -+ // basic_play_sound unlock member automatically. do not mute on enter message -+ if (!basic_play_sound (membertest, leave_snd, 0)) -+ { -+ ast_log(LOG_ERROR, "playing conference[%d] leave message <%s> FAILED on <%s>\n", conf->membercount, leave_snd, membertest->channel_name) ; -+ } -+ else -+ { -+ ast_log(LOG_NOTICE, "playing conference[%d] leave message <%s> on <%s>\n", conf->membercount, leave_snd, membertest->channel_name); -+ } -+ membertest = membertest->next; -+ } -+ } -+ } -+ -+ // delete the member -+ delete_member( member ) ; -+ -+ ast_log( AST_CONF_DEBUG, "removed member from conference, name => %s, remaining => %d\n", -+ conf->name, conf->membercount ) ; -+ -+ //break ; -+ } -+ else -+ { -+ // if member is a moderator, we end the conference when they leave -+ if ( member_is_moderator ) -+ { -+ ast_mutex_lock( &member_list->lock ) ; -+ member_list->kick_flag = 1; -+ ast_mutex_unlock( &member_list->lock ) ; -+ } -+ -+ // save a pointer to the current member, -+ // and then point to the next member in the list -+ member_temp = member_list ; -+ member_list = member_list->next ; -+ } -+ } -+ ast_mutex_unlock( &conf->lock ); -+ -+ // return -1 on error, or the number of members -+ // remaining if the requested member was deleted -+ return count ; -+} -+ -+int count_member( struct ast_conf_member* member, struct ast_conference* conf, short add_member ) -+{ -+ if ( member == NULL || conf == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to count member\n" ) ; -+ return -1 ; -+ } -+ -+ short delta = ( add_member == 1 ) ? 1 : -1 ; -+ -+ // increment member count -+ conf->membercount += delta ; -+ -+ return conf->membercount ; -+} -+ -+// -+// queue incoming frame functions -+// -+ -+ -+ -+ -+// -+// get conference stats -+// -+ -+// -+// returns: -1 => error, 0 => debugging off, 1 => debugging on -+// state: on => 1, off => 0, toggle => -1 -+// -+int set_conference_debugging( const char* name, int state ) -+{ -+ if ( name == NULL ) -+ return -1 ; -+ -+ // acquire mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ struct ast_conference *conf = conflist ; -+ int new_state = -1 ; -+ -+ // loop through conf list -+ while ( conf != NULL ) -+ { -+ if ( strncasecmp( (const char*)&(conf->name), name, 80 ) == 0 ) -+ { -+ // lock conference -+ // ast_mutex_lock( &(conf->lock) ) ; -+ -+ // toggle or set the state -+ if ( state == -1 ) -+ conf->debug_flag = ( conf->debug_flag == 0 ) ? 1 : 0 ; -+ else -+ conf->debug_flag = ( state == 0 ) ? 0 : 1 ; -+ -+ new_state = conf->debug_flag ; -+ -+ // unlock conference -+ // ast_mutex_unlock( &(conf->lock) ) ; -+ -+ break ; -+ } -+ -+ conf = conf->next ; -+ } -+ -+ // release mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return new_state ; -+} -+ -+ -+int get_conference_count( void ) -+{ -+ return conference_count ; -+} -+ -+int show_conference_stats ( int fd ) -+{ -+ // no conferences exist -+ if ( conflist == NULL ) -+ { -+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized.\n") ; -+ return 0 ; -+ } -+ -+ // acquire mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ struct ast_conference *conf = conflist ; -+ -+ ast_cli( fd, "%-20.20s %-40.40s\n", "Name", "Members") ; -+ -+ // loop through conf list -+ while ( conf != NULL ) -+ { -+ ast_cli( fd, "%-20.20s %3d\n", conf->name, conf->membercount ) ; -+ conf = conf->next ; -+ } -+ -+ // release mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return 1 ; -+} -+ -+int show_conference_list ( int fd, const char *name ) -+{ -+ struct ast_conf_member *member; -+ -+ // no conferences exist -+ if ( conflist == NULL ) -+ { -+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", name ) ; -+ return 0 ; -+ } -+ -+ // acquire mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ struct ast_conference *conf = conflist ; -+ -+ // loop through conf list -+ while ( conf != NULL ) -+ { -+ if ( strncasecmp( (const char*)&(conf->name), name, 80 ) == 0 ) -+ { -+ // acquire conference mutex -+ ast_mutex_lock(&conf->lock); -+ -+ // do the biz -+ member = conf->memberlist ; -+ while ( member != NULL ) -+ { -+ ast_cli( fd, "User #: %d ", member->id ) ; -+ ast_cli( fd, "Channel: %s ", member->channel_name ) ; -+ -+ ast_cli( fd, "Flags:"); -+ if ( member->mute_video ) ast_cli( fd, "C"); -+ if ( member->norecv_video ) ast_cli( fd, "c"); -+ if ( member->mute_audio ) ast_cli( fd, "L"); -+ if ( member->norecv_audio ) ast_cli( fd, "l"); -+ if ( member->vad_flag ) ast_cli( fd, "V"); -+ if ( member->denoise_flag ) ast_cli( fd, "D"); -+ if ( member->agc_flag ) ast_cli( fd, "A"); -+ if ( member->dtmf_switch ) ast_cli( fd, "X"); -+ if ( member->dtmf_relay ) ast_cli( fd, "R"); -+ if ( member->vad_switch ) ast_cli( fd, "S"); -+ if ( member->ismoderator ) ast_cli( fd, "M"); -+ if ( member->no_camera ) ast_cli( fd, "N"); -+ if ( member->does_text ) ast_cli( fd, "t"); -+ if ( member->via_telephone ) ast_cli( fd, "T"); -+ ast_cli( fd, " " ); -+ -+ if ( member->id == conf->default_video_source_id ) -+ ast_cli(fd, "Default "); -+ if ( member->id == conf->current_video_source_id ) -+ { -+ ast_cli(fd, "Showing "); -+ if ( conf->video_locked ) -+ ast_cli(fd, "Locked "); -+ } -+ if ( member->driven_member != NULL ) -+ { -+ ast_cli(fd, "Driving:%s(%d) ", member->driven_member->channel_name, member->driven_member->id); -+ } -+ -+ ast_cli( fd, "\n"); -+ member = member->next; -+ } -+ -+ // release conference mutex -+ ast_mutex_unlock(&conf->lock); -+ -+ break ; -+ } -+ -+ conf = conf->next ; -+ } -+ -+ // release mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return 1 ; -+} -+ -+/* Dump list of conference info */ -+int manager_conference_list( struct mansession *s, const struct message *m ) -+{ -+ const char *id = astman_get_header(m,"ActionID"); -+ const char *conffilter = astman_get_header(m,"Conference"); -+ char idText[256] = ""; -+ struct ast_conf_member *member; -+ -+ astman_send_ack(s, m, "Conference list will follow"); -+ -+ // no conferences exist -+ if ( conflist == NULL ) -+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", conffilter );; -+ -+ if (!ast_strlen_zero(id)) { -+ snprintf(idText,256,"ActionID: %s\r\n",id); -+ } -+ -+ // acquire mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ struct ast_conference *conf = conflist ; -+ -+ // loop through conf list -+ while ( conf != NULL ) -+ { -+ if ( strncasecmp( (const char*)&(conf->name), conffilter, 80 ) == 0 ) -+ { -+ // do the biz -+ member = conf->memberlist ; -+ while (member != NULL) -+ { -+ astman_append(s, "Event: ConferenceEntry\r\n" -+ "ConferenceName: %s\r\n" -+ "Member: %d\r\n" -+ "Channel: %s\r\n" -+ "CallerID: %s\r\n" -+ "CallerIDName: %s\r\n" -+ "Muted: %s\r\n" -+ "VideoMuted: %s\r\n" -+ "Default: %s\r\n" -+ "Current: %s\r\n" -+ "%s" -+ "\r\n", -+ conf->name, -+ member->id, -+ member->channel_name, -+ member->chan->cid.cid_num ? member->chan->cid.cid_num : "unknown", -+ member->chan->cid.cid_name ? member->chan->cid.cid_name : "unknown", -+ member->mute_audio ? "YES" : "NO", -+ member->mute_video ? "YES" : "NO", -+ member->id == conf->default_video_source_id ? "YES" : "NO", -+ member->id == conf->current_video_source_id ? "YES" : "NO", -+ idText); -+ member = member->next; -+ } -+ break ; -+ } -+ -+ conf = conf->next ; -+ } -+ -+ // release mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ astman_append(s, -+ "Event: ConferenceListComplete\r\n" -+ "%s" -+ "\r\n",idText); -+ -+ return RESULT_SUCCESS; -+} -+ -+int kick_member ( const char* confname, int user_id) -+{ -+ struct ast_conf_member *member; -+ int res = 0; -+ -+ // no conferences exist -+ if ( conflist == NULL ) -+ { -+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ; -+ return 0 ; -+ } -+ -+ // acquire mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ struct ast_conference *conf = conflist ; -+ -+ // loop through conf list -+ while ( conf != NULL ) -+ { -+ if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 ) -+ { -+ // do the biz -+ ast_mutex_lock( &conf->lock ) ; -+ member = conf->memberlist ; -+ while (member != NULL) -+ { -+ if (member->id == user_id) -+ { -+ ast_mutex_lock( &member->lock ) ; -+ member->kick_flag = 1; -+ //ast_soft_hangup(member->chan); -+ ast_mutex_unlock( &member->lock ) ; -+ -+ res = 1; -+ } -+ member = member->next; -+ } -+ ast_mutex_unlock( &conf->lock ) ; -+ break ; -+ } -+ -+ conf = conf->next ; -+ } -+ -+ // release mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return res ; -+} -+ -+int kick_channel ( const char *confname, const char *channel) -+{ -+ struct ast_conf_member *member; -+ int res = 0; -+ -+ // no conferences exist -+ if ( conflist == NULL ) -+ { -+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ; -+ return 0 ; -+ } -+ -+ if ( confname == NULL || channel == NULL || strlen(confname) == 0 || strlen(channel) == 0 ) -+ return 0; -+ -+ // acquire mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ struct ast_conference *conf = conflist ; -+ -+ // loop through conf list -+ while ( conf != NULL ) -+ { -+ if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 ) -+ { -+ // do the biz -+ ast_mutex_lock( &conf->lock ) ; -+ member = conf->memberlist ; -+ while ( member != NULL ) -+ { -+ if ( strncasecmp( member->channel_name, channel, 80 ) == 0 ) -+ { -+ ast_mutex_lock( &member->lock ) ; -+ member->kick_flag = 1; -+ //ast_soft_hangup(member->chan); -+ ast_mutex_unlock( &member->lock ) ; -+ -+ res = 1; -+ } -+ member = member->next; -+ } -+ ast_mutex_unlock( &conf->lock ) ; -+ break ; -+ } -+ -+ conf = conf->next ; -+ } -+ -+ // release mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return res ; -+} -+ -+int kick_all ( void ) -+{ -+ struct ast_conf_member *member; -+ int res = 0; -+ -+ // no conferences exist -+ if ( conflist == NULL ) -+ { -+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized\n" ) ; -+ return 0 ; -+ } -+ -+ // acquire mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ struct ast_conference *conf = conflist ; -+ -+ // loop through conf list -+ while ( conf != NULL ) -+ { -+ // do the biz -+ ast_mutex_lock( &conf->lock ) ; -+ member = conf->memberlist ; -+ while (member != NULL) -+ { -+ ast_mutex_lock( &member->lock ) ; -+ member->kick_flag = 1; -+ ast_mutex_unlock( &member->lock ) ; -+ member = member->next; -+ } -+ ast_mutex_unlock( &conf->lock ) ; -+ break ; -+ -+ conf = conf->next ; -+ } -+ -+ // release mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return res ; -+} -+ -+int mute_member ( const char* confname, int user_id) -+{ -+ struct ast_conf_member *member; -+ int res = 0; -+ -+ // no conferences exist -+ if ( conflist == NULL ) -+ { -+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ; -+ return 0 ; -+ } -+ -+ // acquire mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ struct ast_conference *conf = conflist ; -+ -+ // loop through conf list -+ while ( conf != NULL ) -+ { -+ if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 ) -+ { -+ // do the biz -+ ast_mutex_lock( &conf->lock ) ; -+ member = conf->memberlist ; -+ while (member != NULL) -+ { -+ if (member->id == user_id) -+ { -+ ast_mutex_lock( &member->lock ) ; -+ member->mute_audio = 1; -+ ast_mutex_unlock( &member->lock ) ; -+ res = 1; -+ } -+ member = member->next; -+ } -+ ast_mutex_unlock( &conf->lock ) ; -+ break ; -+ } -+ -+ conf = conf->next ; -+ } -+ -+ // release mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return res ; -+} -+ -+int mute_channel ( const char* confname, const char* user_chan) -+{ -+ struct ast_conf_member *member; -+ int res = 0; -+ -+ // no conferences exist -+ if ( conflist == NULL ) -+ { -+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ; -+ return 0 ; -+ } -+ -+ // acquire mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ struct ast_conference *conf = conflist ; -+ -+ // loop through conf list -+ while ( conf != NULL ) -+ { -+ if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 ) -+ { -+ // do the biz -+ ast_mutex_lock( &conf->lock ) ; -+ member = conf->memberlist ; -+ while (member != NULL) -+ { -+ if (strncasecmp( member->channel_name, user_chan, 80 ) == 0) -+ { -+ ast_mutex_lock( &member->lock ) ; -+ member->mute_audio = 1; -+ ast_mutex_unlock( &member->lock ) ; -+ res = 1; -+ } -+ member = member->next; -+ } -+ ast_mutex_unlock( &conf->lock ) ; -+ break ; -+ } -+ -+ conf = conf->next ; -+ } -+ -+ // release mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return res ; -+} -+ -+int unmute_member ( const char* confname, int user_id) -+{ -+ struct ast_conf_member *member; -+ int res = 0; -+ -+ // no conferences exist -+ if ( conflist == NULL ) -+ { -+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ; -+ return 0 ; -+ } -+ -+ // acquire mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ struct ast_conference *conf = conflist ; -+ -+ // loop through conf list -+ while ( conf != NULL ) -+ { -+ if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 ) -+ { -+ // do the biz -+ ast_mutex_lock( &conf->lock ) ; -+ member = conf->memberlist ; -+ while (member != NULL) -+ { -+ if (member->id == user_id) -+ { -+ ast_mutex_lock( &member->lock ) ; -+ member->mute_audio = 0; -+ ast_mutex_unlock( &member->lock ) ; -+ res = 1; -+ } -+ member = member->next; -+ } -+ ast_mutex_unlock( &conf->lock ) ; -+ break ; -+ } -+ -+ conf = conf->next ; -+ } -+ -+ // release mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return res ; -+} -+ -+int unmute_channel (const char* confname, const char* user_chan) -+{ -+ struct ast_conf_member *member; -+ int res = 0; -+ -+ // no conferences exist -+ if ( conflist == NULL ) -+ { -+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ; -+ return 0 ; -+ } -+ -+ // acquire mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ struct ast_conference *conf = conflist ; -+ -+ // loop through conf list -+ while ( conf != NULL ) -+ { -+ if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 ) -+ { -+ // do the biz -+ ast_mutex_lock( &conf->lock ) ; -+ member = conf->memberlist ; -+ while (member != NULL) -+ { -+ if (strncasecmp( member->channel_name, user_chan, 80 ) == 0) -+ { -+ ast_mutex_lock( &member->lock ) ; -+ member->mute_audio = 0; -+ ast_mutex_unlock( &member->lock ) ; -+ res = 1; -+ } -+ member = member->next; -+ } -+ ast_mutex_unlock( &conf->lock ) ; -+ break ; -+ } -+ -+ conf = conf->next ; -+ } -+ -+ // release mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return res ; -+} -+ -+int viewstream_switch ( const char* confname, int user_id, int stream_id ) -+{ -+ struct ast_conf_member *member; -+ int res = 0; -+ -+ // no conferences exist -+ if ( conflist == NULL ) -+ { -+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ; -+ return 0 ; -+ } -+ -+ // acquire mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ struct ast_conference *conf = conflist ; -+ -+ // loop through conf list -+ while ( conf != NULL ) -+ { -+ if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 ) -+ { -+ // do the biz -+ ast_mutex_lock( &conf->lock ) ; -+ member = conf->memberlist ; -+ while (member != NULL) -+ { -+ if (member->id == user_id) -+ { -+ // switch the video -+ ast_mutex_lock( &member->lock ) ; -+ -+ member->req_id = stream_id; -+ member->conference = 1; -+ -+ ast_mutex_unlock( &member->lock ) ; -+ res = 1; -+ } -+ member = member->next; -+ } -+ ast_mutex_unlock( &conf->lock ) ; -+ break ; -+ } -+ -+ conf = conf->next ; -+ } -+ -+ // release mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return res ; -+} -+ -+int viewchannel_switch ( const char* confname, const char* userchan, const char* streamchan ) -+{ -+ struct ast_conf_member *member; -+ int res = 0; -+ int stream_id = -1; -+ -+ // no conferences exist -+ if ( conflist == NULL ) -+ { -+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ; -+ return 0 ; -+ } -+ -+ // acquire mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ struct ast_conference *conf = conflist ; -+ -+ // loop through conf list -+ while ( conf != NULL ) -+ { -+ if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 ) -+ { -+ // do the biz -+ ast_mutex_lock( &conf->lock ) ; -+ member = conf->memberlist ; -+ while (member != NULL) -+ { -+ if (strncasecmp( member->channel_name, streamchan, 80 ) == 0) -+ { -+ stream_id = member->id; -+ } -+ member = member->next; -+ } -+ ast_mutex_unlock( &conf->lock ) ; -+ if (stream_id >= 0) -+ { -+ // do the biz -+ ast_mutex_lock( &conf->lock ) ; -+ member = conf->memberlist ; -+ while (member != NULL) -+ { -+ if (strncasecmp( member->channel_name, userchan, 80 ) == 0) -+ { -+ // switch the video -+ ast_mutex_lock( &member->lock ) ; -+ -+ member->req_id = stream_id; -+ member->conference = 1; -+ -+ ast_mutex_unlock( &member->lock ) ; -+ res = 1; -+ } -+ member = member->next; -+ } -+ ast_mutex_unlock( &conf->lock ) ; -+ } -+ break ; -+ } -+ -+ conf = conf->next ; -+ } -+ -+ // release mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return res ; -+} -+ -+int get_conference_stats( ast_conference_stats* stats, int requested ) -+{ -+ // no conferences exist -+ if ( conflist == NULL ) -+ { -+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialize\n" ) ; -+ return 0 ; -+ } -+ -+ // acquire mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ // compare the number of requested to the number of available conferences -+ requested = ( get_conference_count() < requested ) ? get_conference_count() : requested ; -+ -+ // -+ // loop through conf list -+ // -+ -+ struct ast_conference* conf = conflist ; -+ int count = 0 ; -+ -+ while ( count <= requested && conf != NULL ) -+ { -+ // copy stats struct to array -+ stats[ count ] = conf->stats ; -+ -+ conf = conf->next ; -+ ++count ; -+ } -+ -+ // release mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return count ; -+} -+ -+int get_conference_stats_by_name( ast_conference_stats* stats, const char* name ) -+{ -+ // no conferences exist -+ if ( conflist == NULL ) -+ { -+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", name ) ; -+ return 0 ; -+ } -+ -+ // make sure stats is null -+ stats = NULL ; -+ -+ // acquire mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ struct ast_conference *conf = conflist ; -+ -+ // loop through conf list -+ while ( conf != NULL ) -+ { -+ if ( strncasecmp( (const char*)&(conf->name), name, 80 ) == 0 ) -+ { -+ // copy stats for found conference -+ *stats = conf->stats ; -+ break ; -+ } -+ -+ conf = conf->next ; -+ } -+ -+ // release mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return ( stats == NULL ) ? 0 : 1 ; -+} -+ -+struct ast_conf_member *find_member (const char *chan, int lock) -+{ -+ struct ast_conf_member *found = NULL; -+ struct ast_conf_member *member; -+ struct ast_conference *conf; -+ -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ conf = conflist; -+ -+ // loop through conf list -+ while ( conf != NULL && !found ) -+ { -+ // lock conference -+ ast_mutex_lock( &conf->lock ); -+ -+ member = conf->memberlist ; -+ -+ while (member != NULL) -+ { -+ if(!strcmp(member->channel_name, chan)) { -+ found = member; -+ if(lock) -+ ast_mutex_lock(&member->lock); -+ break; -+ } -+ member = member->next; -+ } -+ -+ // unlock conference -+ ast_mutex_unlock( &conf->lock ); -+ -+ conf = conf->next ; -+ } -+ -+ // release mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return found; -+} -+ -+// All the VAD-based video switching magic happens here -+// This function should be called inside conference_exec -+// The conference mutex should be locked, we don't have to do it here -+void do_VAD_switching(struct ast_conference *conf) -+{ -+ struct ast_conf_member *member; -+ struct timeval current_time; -+ long longest_speaking; -+ struct ast_conf_member *longest_speaking_member; -+ int current_silent, current_no_camera, current_video_mute; -+ int default_no_camera, default_video_mute; -+ -+ current_time = ast_tvnow(); -+ -+ // Scan the member list looking for the longest speaking member -+ // We also check if the currently speaking member has been silent for a while -+ // Also, we check for camera disabled or video muted members -+ // We say that a member is speaking after his speaking state has been on for -+ // at least AST_CONF_VIDEO_START_TIMEOUT ms -+ // We say that a member is silent after his speaking state has been off for -+ // at least AST_CONF_VIDEO_STOP_TIMEOUT ms -+ longest_speaking = 0; -+ longest_speaking_member = NULL; -+ current_silent = 0; -+ current_no_camera = 0; -+ current_video_mute = 0; -+ default_no_camera = 0; -+ default_video_mute = 0; -+ for ( member = conf->memberlist ; -+ member != NULL ; -+ member = member->next ) -+ { -+ // Has the state changed since last time through this loop? Notify! -+ if ( member->speaking_state_notify ) -+ { -+/* fprintf(stderr, "Mihai: member %d, channel %s has changed state to %s\n", -+ member->id, -+ member->channel_name, -+ ((member->speaking_state == 1 ) ? "speaking" : "silent") -+ ); */ -+ } -+ -+ // If a member connects via telephone, they don't have video -+ if ( member->via_telephone ) -+ continue; -+ -+ // We check for no VAD switching, video-muted or camera disabled -+ // If yes, this member will not be considered as a candidate for switching -+ // If this is the currently speaking member, then mark it so we force a switch -+ if ( !member->vad_switch ) -+ continue; -+ -+ if ( member->mute_video ) -+ { -+ if ( member->id == conf->default_video_source_id ) -+ default_video_mute = 1; -+ if ( member->id == conf->current_video_source_id ) -+ current_video_mute = 1; -+ else -+ continue; -+ } -+ -+ if ( member->no_camera ) -+ { -+ if ( member->id == conf->default_video_source_id ) -+ default_no_camera = 1; -+ if ( member->id == conf->current_video_source_id ) -+ current_no_camera = 1; -+ else -+ continue; -+ } -+ -+ // Check if current speaker has been silent for a while -+ if ( member->id == conf->current_video_source_id && -+ member->speaking_state == 0 && -+ ast_tvdiff_ms(current_time, member->last_state_change) > AST_CONF_VIDEO_STOP_TIMEOUT ) -+ { -+ current_silent = 1; -+ } -+ -+ // Find a candidate to switch to by looking for the longest speaking member -+ // We exclude the current video source from the search -+ if ( member->id != conf->current_video_source_id && member->speaking_state == 1 ) -+ { -+ long tmp = ast_tvdiff_ms(current_time, member->last_state_change); -+ if ( tmp > AST_CONF_VIDEO_START_TIMEOUT && tmp > longest_speaking ) -+ { -+ longest_speaking = tmp; -+ longest_speaking_member = member; -+ } -+ } -+ } -+ -+ // We got our results, now let's make a decision -+ // If the currently speaking member has been marked as silent, then we take the longest -+ // speaking member. If no member is speaking, we go to default -+ // As a policy we don't want to switch away from a member that is speaking -+ // however, we might need to refine this to avoid a situation when a member has a -+ // low noise threshold or its VAD is simply stuck -+ if ( current_silent || current_no_camera || current_video_mute || conf->current_video_source_id < 0 ) -+ { -+ if ( longest_speaking_member != NULL ) -+ { -+ do_video_switching(conf, longest_speaking_member->id, 0); -+ } else -+ { -+ // If there's nobody speaking and we have a default that can send video, switch to it -+ // If not, then switch to empty (-1) -+ if ( conf->default_video_source_id >= 0 && -+ !default_no_camera && -+ !default_video_mute -+ ) -+ do_video_switching(conf, conf->default_video_source_id, 0); -+ else -+ do_video_switching(conf, -1, 0); -+ } -+ } -+} -+ -+int lock_conference(const char *conference, int member_id) -+{ -+ struct ast_conference *conf; -+ struct ast_conf_member *member; -+ int res; -+ -+ if ( conference == NULL || member_id < 0 ) -+ return -1 ; -+ -+ // acquire mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ // Look for conference -+ res = 0; -+ for ( conf = conflist ; conf != NULL ; conf = conf->next ) -+ { -+ if ( strcmp(conference, conf->name) == 0 ) -+ { -+ // Search member list for our member -+ // acquire conference mutex -+ ast_mutex_lock( &conf->lock ); -+ -+ for ( member = conf->memberlist ; member != NULL ; member = member->next ) -+ { -+ if ( member->id == member_id && !member->mute_video ) -+ { -+ do_video_switching(conf, member_id, 0); -+ conf->video_locked = 1; -+ res = 1; -+ -+ manager_event(EVENT_FLAG_CALL, "ConferenceLock", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name); -+ break; -+ } -+ } -+ -+ // Release conference mutex -+ ast_mutex_unlock( &conf->lock ); -+ break; -+ } -+ } -+ -+ // release mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return res; -+} -+ -+int lock_conference_channel(const char *conference, const char *channel) -+{ -+ struct ast_conference *conf; -+ struct ast_conf_member *member; -+ int res; -+ -+ if ( conference == NULL || channel == NULL ) -+ return -1 ; -+ -+ // acquire mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ // Look for conference -+ res = 0; -+ for ( conf = conflist ; conf != NULL ; conf = conf->next ) -+ { -+ if ( strcmp(conference, conf->name) == 0 ) -+ { -+ // Search member list for our member -+ // acquire conference mutex -+ ast_mutex_lock( &conf->lock ); -+ -+ for ( member = conf->memberlist ; member != NULL ; member = member->next ) -+ { -+ if ( strcmp(channel, member->channel_name) == 0 && !member->mute_video ) -+ { -+ do_video_switching(conf, member->id, 0); -+ conf->video_locked = 1; -+ res = 1; -+ -+ manager_event(EVENT_FLAG_CALL, "ConferenceLock", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name); -+ break; -+ } -+ } -+ -+ // Release conference mutex -+ ast_mutex_unlock( &conf->lock ); -+ break; -+ } -+ } -+ -+ // release mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return res; -+} -+ -+int unlock_conference(const char *conference) -+{ -+ struct ast_conference *conf; -+ int res; -+ -+ if ( conference == NULL ) -+ return -1; -+ -+ // acquire conference list mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ // Look for conference -+ res = 0; -+ for ( conf = conflist ; conf != NULL ; conf = conf->next ) -+ { -+ if ( strcmp(conference, conf->name) == 0 ) -+ { -+ conf->video_locked = 0; -+ manager_event(EVENT_FLAG_CALL, "ConferenceUnlock", "ConferenceName: %s\r\n", conf->name); -+ do_video_switching(conf, conf->default_video_source_id, 0); -+ res = 1; -+ -+ break; -+ } -+ } -+ -+ // release conference list mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return res; -+} -+ -+int set_default_id(const char *conference, int member_id) -+{ -+ struct ast_conference *conf; -+ struct ast_conf_member *member; -+ int res; -+ -+ if ( conference == NULL ) -+ return -1 ; -+ -+ // acquire conference list mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ // Look for conference -+ res = 0; -+ for ( conf = conflist ; conf != NULL ; conf = conf->next ) -+ { -+ if ( strcmp(conference, conf->name) == 0 ) -+ { -+ if ( member_id < 0 ) -+ { -+ conf->default_video_source_id = -1; -+ manager_event(EVENT_FLAG_CALL, "ConferenceDefault", "ConferenceName: %s\r\nChannel: empty\r\n", conf->name); -+ res = 1; -+ break; -+ } else -+ { -+ // Search member list for our member -+ // acquire conference mutex -+ ast_mutex_lock( &conf->lock ); -+ -+ for ( member = conf->memberlist ; member != NULL ; member = member->next ) -+ { -+ // We do not allow video muted members or members that do not support -+ // VAD switching to become defaults -+ if ( member->id == member_id && -+ !member->mute_video && -+ member->vad_switch -+ ) -+ { -+ conf->default_video_source_id = member_id; -+ res = 1; -+ -+ manager_event(EVENT_FLAG_CALL, "ConferenceDefault", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name); -+ break; -+ } -+ } -+ -+ // Release conference mutex -+ ast_mutex_unlock( &conf->lock ); -+ break; -+ } -+ } -+ } -+ -+ // release conference list mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return res; -+ -+} -+ -+int set_default_channel(const char *conference, const char *channel) -+{ -+ struct ast_conference *conf; -+ struct ast_conf_member *member; -+ int res; -+ -+ if ( conference == NULL || channel == NULL ) -+ return -1 ; -+ -+ // acquire conference list mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ // Look for conference -+ res = 0; -+ for ( conf = conflist ; conf != NULL ; conf = conf->next ) -+ { -+ if ( strcmp(conference, conf->name) == 0 ) -+ { -+ // Search member list for our member -+ // acquire conference mutex -+ ast_mutex_lock( &conf->lock ); -+ -+ for ( member = conf->memberlist ; member != NULL ; member = member->next ) -+ { -+ // We do not allow video muted members or members that do not support -+ // VAD switching to become defaults -+ if ( strcmp(channel, member->channel_name) == 0 && -+ !member->mute_video && -+ member->vad_switch -+ ) -+ { -+ conf->default_video_source_id = member->id; -+ res = 1; -+ -+ manager_event(EVENT_FLAG_CALL, "ConferenceDefault", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name); -+ break; -+ } -+ } -+ -+ // Release conference mutex -+ ast_mutex_unlock( &conf->lock ); -+ break; -+ } -+ } -+ -+ // release conference list mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return res; -+} -+ -+int video_mute_member(const char *conference, int member_id) -+{ -+ struct ast_conference *conf; -+ struct ast_conf_member *member; -+ int res; -+ -+ if ( conference == NULL || member_id < 0 ) -+ return -1; -+ -+ res = 0; -+ -+ // acquire conference list mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ for ( conf = conflist ; conf != NULL ; conf = conf->next ) -+ { -+ if ( strcmp(conference, conf->name) == 0 ) -+ { -+ // acquire conference mutex -+ ast_mutex_lock( &conf->lock ); -+ -+ for ( member = conf->memberlist ; member != NULL ; member = member->next ) -+ { -+ if ( member->id == member_id ) -+ { -+ // acquire member mutex -+ ast_mutex_lock( &member->lock ); -+ -+ member->mute_video = 1; -+ -+ // release member mutex -+ ast_mutex_unlock( &member->lock ); -+ -+ manager_event(EVENT_FLAG_CALL, "ConferenceVideoMute", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name); -+ -+ if ( member->id == conf->current_video_source_id ) -+ { -+ do_video_switching(conf, conf->default_video_source_id, 0); -+ } -+ -+ res = 1; -+ break; -+ } -+ } -+ -+ // release conference mutex -+ ast_mutex_unlock( &conf->lock ); -+ -+ break; -+ } -+ } -+ -+ // release conference list mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return res; -+} -+ -+int video_unmute_member(const char *conference, int member_id) -+{ -+ struct ast_conference *conf; -+ struct ast_conf_member *member; -+ int res; -+ -+ if ( conference == NULL || member_id < 0 ) -+ return -1; -+ -+ res = 0; -+ -+ // acquire conference list mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ for ( conf = conflist ; conf != NULL ; conf = conf->next ) -+ { -+ if ( strcmp(conference, conf->name) == 0 ) -+ { -+ // acquire conference mutex -+ ast_mutex_lock( &conf->lock ); -+ -+ for ( member = conf->memberlist ; member != NULL ; member = member->next ) -+ { -+ if ( member->id == member_id ) -+ { -+ // acquire member mutex -+ ast_mutex_lock( &member->lock ); -+ -+ member->mute_video = 0; -+ -+ // release member mutex -+ ast_mutex_unlock( &member->lock ); -+ -+ manager_event(EVENT_FLAG_CALL, "ConferenceVideoUnmute", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name); -+ -+ res = 1; -+ break; -+ } -+ } -+ -+ // release conference mutex -+ ast_mutex_unlock( &conf->lock ); -+ -+ break; -+ } -+ } -+ -+ // release conference list mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return res; -+} -+ -+int video_mute_channel(const char *conference, const char *channel) -+{ -+ struct ast_conference *conf; -+ struct ast_conf_member *member; -+ int res; -+ -+ if ( conference == NULL || channel == NULL ) -+ return -1; -+ -+ res = 0; -+ -+ // acquire conference list mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ for ( conf = conflist ; conf != NULL ; conf = conf->next ) -+ { -+ if ( strcmp(conference, conf->name) == 0 ) -+ { -+ // acquire conference mutex -+ ast_mutex_lock( &conf->lock ); -+ -+ for ( member = conf->memberlist ; member != NULL ; member = member->next ) -+ { -+ if ( strcmp(channel, member->channel_name) == 0 ) -+ { -+ // acquire member mutex -+ ast_mutex_lock( &member->lock ); -+ -+ member->mute_video = 1; -+ -+ // release member mutex -+ ast_mutex_unlock( &member->lock ); -+ -+ manager_event(EVENT_FLAG_CALL, "ConferenceVideoMute", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name); -+ -+ if ( member->id == conf->current_video_source_id ) -+ { -+ do_video_switching(conf, conf->default_video_source_id, 0); -+ } -+ -+ res = 1; -+ break; -+ } -+ } -+ -+ // release conference mutex -+ ast_mutex_unlock( &conf->lock ); -+ -+ break; -+ } -+ } -+ -+ // release conference list mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return res; -+} -+ -+int video_unmute_channel(const char *conference, const char *channel) -+{ -+ struct ast_conference *conf; -+ struct ast_conf_member *member; -+ int res; -+ -+ if ( conference == NULL || channel == NULL ) -+ return -1; -+ -+ res = 0; -+ -+ // acquire conference list mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ for ( conf = conflist ; conf != NULL ; conf = conf->next ) -+ { -+ if ( strcmp(conference, conf->name) == 0 ) -+ { -+ // acquire conference mutex -+ ast_mutex_lock( &conf->lock ); -+ -+ for ( member = conf->memberlist ; member != NULL ; member = member->next ) -+ { -+ if ( strcmp(channel, member->channel_name) == 0 ) -+ { -+ // acquire member mutex -+ ast_mutex_lock( &member->lock ); -+ -+ member->mute_video = 0; -+ -+ // release member mutex -+ ast_mutex_unlock( &member->lock ); -+ -+ manager_event(EVENT_FLAG_CALL, "ConferenceVideoUnmute", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name); -+ -+ res = 1; -+ break; -+ } -+ } -+ -+ // release conference mutex -+ ast_mutex_unlock( &conf->lock ); -+ -+ break; -+ } -+ } -+ -+ // release conference list mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return res; -+} -+ -+// -+// Text message functions -+// -+int send_text(const char *conference, int member_id, const char *text) -+{ -+ struct ast_conference *conf; -+ struct ast_conf_member *member; -+ int res; -+ -+ if ( conference == NULL || member_id < 0 || text == NULL ) -+ return -1; -+ -+ res = 0; -+ -+ // acquire conference list mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ for ( conf = conflist ; conf != NULL ; conf = conf->next ) -+ { -+ if ( strcmp(conference, conf->name) == 0 ) -+ { -+ // acquire conference mutex -+ ast_mutex_lock( &conf->lock ); -+ -+ for ( member = conf->memberlist ; member != NULL ; member = member->next ) -+ { -+ if ( member->id == member_id ) -+ { -+ res = send_text_message_to_member(member, text) == 0; -+ break; -+ } -+ } -+ -+ // release conference mutex -+ ast_mutex_unlock( &conf->lock ); -+ -+ break; -+ } -+ } -+ -+ // release conference list mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ return res; -+} -+ -+int send_text_channel(const char *conference, const char *channel, const char *text) -+{ -+ struct ast_conference *conf; -+ struct ast_conf_member *member; -+ int res; -+ -+ if ( conference == NULL || channel == NULL || text == NULL ) -+ return -1; -+ -+ res = 0; -+ -+ // acquire conference list mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ for ( conf = conflist ; conf != NULL ; conf = conf->next ) -+ { -+ if ( strcmp(conference, conf->name) == 0 ) -+ { -+ // acquire conference mutex -+ ast_mutex_lock( &conf->lock ); -+ -+ for ( member = conf->memberlist ; member != NULL ; member = member->next ) -+ { -+ if ( strcmp(member->channel_name, channel) == 0 ) -+ { -+ res = send_text_message_to_member(member, text) == 0; -+ break; -+ } -+ } -+ -+ // release conference mutex -+ ast_mutex_unlock( &conf->lock ); -+ -+ break; -+ } -+ } -+ -+ // release conference list mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return res; -+} -+ -+int send_text_broadcast(const char *conference, const char *text) -+{ -+ struct ast_conference *conf; -+ struct ast_conf_member *member; -+ int res; -+ -+ if ( conference == NULL || text == NULL ) -+ return -1; -+ -+ res = 0; -+ -+ // acquire conference list mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ for ( conf = conflist ; conf != NULL ; conf = conf->next ) -+ { -+ if ( strcmp(conference, conf->name) == 0 ) -+ { -+ // acquire conference mutex -+ ast_mutex_lock( &conf->lock ); -+ -+ for ( member = conf->memberlist ; member != NULL ; member = member->next ) -+ { -+ if ( send_text_message_to_member(member, text) == 0 ) -+ res = res || 1; -+ } -+ -+ // release conference mutex -+ ast_mutex_unlock( &conf->lock ); -+ -+ break; -+ } -+ } -+ -+ // release conference list mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return res; -+} -+ -+// Creates a text frame and sends it to a given member -+// Returns 0 on success, -1 on failure -+int send_text_message_to_member(struct ast_conf_member *member, const char *text) -+{ -+ struct ast_frame *f; -+ -+ if ( member == NULL || text == NULL ) return -1; -+ -+ if ( member->does_text ) -+ { -+ f = create_text_frame(text, 1); -+ if ( f == NULL || queue_outgoing_text_frame(member, f) != 0) return -1; -+ ast_frfree(f); -+ } -+ -+ return 0; -+} -+ -+// Associates two members -+// Drives VAD-based video switching of dst_member from audio from src_member -+// This can be used when a member participates in a video conference but -+// talks using a telephone (simulcast) connection -+int drive(const char *conference, int src_member_id, int dst_member_id) -+{ -+ int res; -+ struct ast_conference *conf; -+ struct ast_conf_member *member; -+ struct ast_conf_member *src; -+ struct ast_conf_member *dst; -+ -+ if ( conference == NULL || src_member_id < 0 ) -+ return -1; -+ -+ res = 0; -+ -+ // acquire conference list mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ for ( conf = conflist ; conf != NULL ; conf = conf->next ) -+ { -+ if ( strcmp(conference, conf->name) == 0 ) -+ { -+ // acquire conference mutex -+ ast_mutex_lock( &conf->lock ); -+ -+ src = NULL; -+ dst = NULL; -+ for ( member = conf->memberlist ; member != NULL ; member = member->next ) -+ { -+ if ( member->id == src_member_id ) -+ src = member; -+ if ( member->id == dst_member_id ) -+ dst = member; -+ } -+ if ( src != NULL ) -+ { -+ // acquire member mutex -+ ast_mutex_lock(&src->lock); -+ -+ if ( dst != NULL ) -+ { -+ src->driven_member = dst; -+ // Make sure the driven member's speaker count is correct -+ if ( src->speaking_state == 1 ) -+ increment_speaker_count(src->driven_member, 1); -+ res = 1; -+ } else -+ { -+ if ( dst_member_id < 0 ) -+ { -+ // Make sure the driven member's speaker count is correct -+ if ( src->speaking_state == 1 ) -+ decrement_speaker_count(src->driven_member, 1); -+ src->driven_member = NULL; -+ res = 1; -+ } -+ } -+ -+ // release member mutex -+ ast_mutex_unlock(&src->lock); -+ } -+ -+ // release conference mutex -+ ast_mutex_unlock( &conf->lock ); -+ -+ break; -+ } -+ } -+ -+ // release conference list mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return res; -+} -+ -+// Associates two channels -+// Drives VAD-based video switching of dst_channel from audio from src_channel -+// This can be used when a member participates in a video conference but -+// talks using a telephone (simulcast) connection -+int drive_channel(const char *conference, const char *src_channel, const char *dst_channel) -+{ -+ int res; -+ struct ast_conference *conf; -+ struct ast_conf_member *member; -+ struct ast_conf_member *src; -+ struct ast_conf_member *dst; -+ -+ if ( conference == NULL || src_channel == NULL ) -+ return -1; -+ -+ res = 0; -+ -+ // acquire conference list mutex -+ ast_mutex_lock( &conflist_lock ) ; -+ -+ for ( conf = conflist ; conf != NULL ; conf = conf->next ) -+ { -+ if ( strcmp(conference, conf->name) == 0 ) -+ { -+ // acquire conference mutex -+ ast_mutex_lock( &conf->lock ); -+ -+ src = NULL; -+ dst = NULL; -+ for ( member = conf->memberlist ; member != NULL ; member = member->next ) -+ { -+ if ( strcmp(src_channel, member->channel_name) == 0 ) -+ src = member; -+ if ( dst_channel != NULL && strcmp(dst_channel, member->channel_name) == 0 ) -+ dst = member; -+ } -+ if ( src != NULL ) -+ { -+ // acquire member mutex -+ ast_mutex_lock(&src->lock); -+ -+ if ( dst != NULL ) -+ { -+ src->driven_member = dst; -+ // Make sure the driven member's speaker count is correct -+ if ( src->speaking_state == 1 ) -+ increment_speaker_count(src->driven_member, 1); -+ res = 1; -+ } else -+ { -+ if ( dst_channel == NULL ) -+ { -+ // Make sure the driven member's speaker count is correct -+ if ( src->speaking_state == 1 ) -+ decrement_speaker_count(src->driven_member, 1); -+ src->driven_member = NULL; -+ res = 1; -+ } -+ } -+ -+ // release member mutex -+ ast_mutex_unlock(&src->lock); -+ } -+ -+ // release conference mutex -+ ast_mutex_unlock( &conf->lock ); -+ -+ break; -+ } -+ } -+ -+ // release conference list mutex -+ ast_mutex_unlock( &conflist_lock ) ; -+ -+ return res; -+} -+ -+// Switches video source -+// Sends a manager event as well as -+// a text message notifying members of a video switch -+// The notification is sent to the current member and to the new member -+// The function locks the conference mutex as required -+void do_video_switching(struct ast_conference *conf, int new_id, int lock) -+{ -+ struct ast_conf_member *member; -+ struct ast_conf_member *new_member = NULL; -+ -+ if ( conf == NULL ) return; -+ -+ if ( lock ) -+ { -+ // acquire conference mutex -+ ast_mutex_lock( &conf->lock ); -+ } -+ -+ //fprintf(stderr, "Mihai: video switch from %d to %d\n", conf->current_video_source_id, new_id); -+ -+ // No need to do anything if the current member is the same as the new member -+ if ( new_id != conf->current_video_source_id ) -+ { -+ for ( member = conf->memberlist ; member != NULL ; member = member->next ) -+ { -+ if ( member->id == conf->current_video_source_id ) -+ { -+ send_text_message_to_member(member, AST_CONF_CONTROL_STOP_VIDEO); -+ } -+ if ( member->id == new_id ) -+ { -+ send_text_message_to_member(member, AST_CONF_CONTROL_START_VIDEO); -+ new_member = member; -+ } -+ } -+ -+ conf->current_video_source_id = new_id; -+ -+ if ( new_member != NULL ) -+ { -+ manager_event(EVENT_FLAG_CALL, -+ "ConferenceVideoSwitch", -+ "ConferenceName: %s\r\nChannel: %s\r\n", -+ conf->name, -+ new_member->channel_name); -+ } else -+ { -+ manager_event(EVENT_FLAG_CALL, -+ "ConferenceVideoSwitch", -+ "ConferenceName: %s\r\nChannel: empty\r\n", -+ conf->name); -+ } -+ } -+ -+ if ( lock ) -+ { -+ // release conference mutex -+ ast_mutex_unlock( &conf->lock ); -+ } -+} -+ -+int basic_play_sound(struct ast_conf_member *member, const char *file, int mute) -+{ -+ struct ast_conf_soundq *newsound; -+ struct ast_conf_soundq **q; -+ -+ newsound = calloc(1, sizeof(struct ast_conf_soundq)); -+ newsound->stream = ast_openstream(member->chan, file, NULL); -+ if( !newsound->stream ) -+ { -+ free(newsound); -+ ast_mutex_unlock(&member->lock); -+ return 0; -+ } -+ member->chan->stream = NULL; -+ -+ newsound->muted = mute; -+ ast_copy_string(newsound->name, file, sizeof(newsound->name)); -+ -+ // append sound to the end of the list. -+ for ( q=&member->soundq; *q; q = &((*q)->next) ) ; -+ *q = newsound; -+ -+ ast_mutex_unlock(&member->lock); -+ -+ return 1 ; -+} -+ -+int play_sound_channel(int fd, const char *channel, const char *file, int mute) -+{ -+ struct ast_conf_member *member; -+ -+ member = find_member(channel, 1); -+ if( !member ) -+ { -+ ast_cli(fd, "Member %s not found\n", channel); -+ return 0; -+ } -+ -+ if (! basic_play_sound(member, file, mute)) -+ { -+ ast_cli(fd, "Sound %s not found\n", file); -+ return 0; -+ } -+ -+ ast_cli(fd, "Playing sound %s to member %s %s\n", -+ file, channel, mute ? "with mute" : ""); -+ -+ return 1 ; -+} -+ -+int stop_sound_channel(int fd, const char *channel) -+{ -+ struct ast_conf_member *member; -+ struct ast_conf_soundq *sound; -+ struct ast_conf_soundq *next; -+ -+ member = find_member(channel, 1); -+ if ( !member ) -+ { -+ ast_cli(fd, "Member %s not found\n", channel); -+ return 0; -+ } -+ -+ // clear all sounds -+ sound = member->soundq; -+ member->soundq = NULL; -+ -+ while ( sound ) -+ { -+ next = sound->next; -+ ast_closestream(sound->stream); -+ free(sound); -+ sound = next; -+ } -+ -+ // reset write format, since we're done playing the sound -+ if ( ast_set_write_format( member->chan, member->write_format ) < 0 ) -+ { -+ ast_log( LOG_ERROR, "unable to set write format to %d\n", -+ member->write_format ) ; -+ } -+ -+ ast_mutex_unlock(&member->lock); -+ -+ ast_cli( fd, "Stopped sounds to member %s\n", channel); -+ return 1; -+} -diff --git a/apps/conference/conference.h b/apps/conference/conference.h -new file mode 100644 -index 0000000..0f7e248 ---- /dev/null -+++ b/apps/conference/conference.h -@@ -0,0 +1,194 @@ -+ -+// $Id: conference.h 884 2007-06-27 14:56:21Z sbalea $ -+ -+/* -+ * app_conference -+ * -+ * A channel independent conference application for Asterisk -+ * -+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH -+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc. -+ * Copyright (C) 2005, 2006 HorizonWimba, Inc. -+ * Copyright (C) 2007 Wimba, Inc. -+ * -+ * Klaus-Peter Junghanns -+ * -+ * Video Conferencing support added by -+ * Neil Stratford -+ * Copyright (C) 2005, 2005 Vipadia Limited -+ * -+ * VAD driven video conferencing, text message support -+ * and miscellaneous enhancements added by -+ * Mihai Balea -+ * -+ * This program may be modified and distributed under the -+ * terms of the GNU General Public License. You should have received -+ * a copy of the GNU General Public License along with this -+ * program; if not, write to the Free Software Foundation, Inc. -+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef _APP_CONF_CONFERENCE_H -+#define _APP_CONF_CONFERENCE_H -+ -+// -+// includes -+// -+ -+#include "app_conference.h" -+#include "common.h" -+ -+// -+// struct declarations -+// -+ -+typedef struct ast_conference_stats -+{ -+ // conference name ( copied for ease of use ) -+ char name[128] ; -+ -+ // type of connection -+ unsigned short phone ; -+ unsigned short iaxclient ; -+ unsigned short sip ; -+ -+ // type of users -+ unsigned short moderators ; -+ unsigned short listeners ; -+ -+ // accounting data -+ unsigned long frames_in ; -+ unsigned long frames_out ; -+ unsigned long frames_mixed ; -+ -+ struct timeval time_entered ; -+ -+} ast_conference_stats ; -+ -+ -+struct ast_conference -+{ -+ // conference name -+ char name[128] ; -+ -+ // single-linked list of members in conference -+ struct ast_conf_member* memberlist ; -+ int membercount ; -+ int id_count; -+ -+ // id of the default video source -+ // If nobody is talking and video is unlocked, we use this source -+ int default_video_source_id; -+ -+ // id of the current video source -+ // this changes according to VAD rules and lock requests -+ int current_video_source_id; -+ -+ // timestamp of when the current source has started talking -+ // TODO: do we really need this? -+ //struct timeval current_video_source_timestamp; -+ -+ // Video source locked flag, 1 -> locked, 0 -> unlocked -+ short video_locked; -+ -+ // conference thread id -+ pthread_t conference_thread ; -+ -+ // conference data mutex -+ ast_mutex_t lock ; -+ -+ // pointer to next conference in single-linked list -+ struct ast_conference* next ; -+ -+ // pointer to translation paths -+ struct ast_trans_pvt* from_slinear_paths[ AC_SUPPORTED_FORMATS ] ; -+ -+ // conference stats -+ ast_conference_stats stats ; -+ -+ // keep track of current delivery time -+ struct timeval delivery_time ; -+ -+ // 1 => on, 0 => off -+ short debug_flag ; -+} ; -+ -+ -+#include "member.h" -+ -+// -+// function declarations -+// -+ -+struct ast_conference* start_conference( struct ast_conf_member* member ) ; -+ -+void conference_exec( struct ast_conference* conf ) ; -+ -+struct ast_conference* find_conf( const char* name ) ; -+struct ast_conference* create_conf( char* name, struct ast_conf_member* member ) ; -+void remove_conf( struct ast_conference* conf ) ; -+int end_conference( const char *name, int hangup ) ; -+ -+// find a particular member, locking if requested. -+struct ast_conf_member *find_member ( const char *chan, int lock) ; -+ -+int queue_frame_for_listener( struct ast_conference* conf, struct ast_conf_member* member, conf_frame* frame ) ; -+int queue_frame_for_speaker( struct ast_conference* conf, struct ast_conf_member* member, conf_frame* frame ) ; -+int queue_silent_frame( struct ast_conference* conf, struct ast_conf_member* member ) ; -+ -+int get_new_id( struct ast_conference *conf ); -+void add_member( struct ast_conf_member* member, struct ast_conference* conf ) ; -+int remove_member( struct ast_conf_member* member, struct ast_conference* conf ) ; -+int count_member( struct ast_conf_member* member, struct ast_conference* conf, short add_member ) ; -+ -+void do_VAD_switching(struct ast_conference *conf); -+int send_text_message_to_member(struct ast_conf_member *member, const char *text); -+void do_video_switching(struct ast_conference *conf, int new_id, int lock); -+ -+// called by app_confernce.c:load_module() -+void init_conference( void ) ; -+ -+int get_conference_count( void ) ; -+ -+int show_conference_list ( int fd, const char* name ); -+int manager_conference_list( struct mansession *s, const struct message *m); -+int show_conference_stats ( int fd ); -+int kick_member ( const char* confname, int user_id); -+int kick_channel ( const char *confname, const char *channel); -+int kick_all ( void ); -+int mute_member ( const char* confname, int user_id); -+int unmute_member ( const char* confname, int user_id); -+int mute_channel ( const char* confname, const char* user_chan); -+int unmute_channel ( const char* confname, const char* user_chan); -+int viewstream_switch ( const char* confname, int user_id, int stream_id); -+int viewchannel_switch ( const char* confname, const char* user_chan, const char* stream_chan); -+ -+int get_conference_stats( ast_conference_stats* stats, int requested ) ; -+int get_conference_stats_by_name( ast_conference_stats* stats, const char* name ) ; -+ -+int lock_conference(const char *conference, int member_id); -+int lock_conference_channel(const char *conference, const char *channel); -+int unlock_conference(const char *conference); -+ -+int set_default_id(const char *conference, int member_id); -+int set_default_channel(const char *conference, const char *channel); -+ -+int video_mute_member(const char *conference, int member_id); -+int video_unmute_member(const char *conference, int member_id); -+int video_mute_channel(const char *conference, const char *channel); -+int video_unmute_channel(const char *conference, const char *channel); -+ -+int send_text(const char *conference, int member, const char *text); -+int send_text_channel(const char *conference, const char *channel, const char *text); -+int send_text_broadcast(const char *conference, const char *text); -+ -+int drive(const char *conference, int src_member_id, int dst_member_id); -+int drive_channel(const char *conference, const char *src_channel, const char *dst_channel); -+ -+int basic_play_sound(struct ast_conf_member *member, const char *file, int mute); -+int play_sound_channel(int fd, const char *channel, const char *file, int mute); -+int stop_sound_channel(int fd, const char *channel); -+ -+int set_conference_debugging( const char* name, int state ) ; -+ -+#endif -diff --git a/apps/conference/frame.c b/apps/conference/frame.c -new file mode 100644 -index 0000000..24e04bd ---- /dev/null -+++ b/apps/conference/frame.c -@@ -0,0 +1,679 @@ -+ -+// $Id: frame.c 751 2006-12-11 22:08:45Z sbalea $ -+ -+/* -+ * app_conference -+ * -+ * A channel independent conference application for Asterisk -+ * -+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH -+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc. -+ * Copyright (C) 2005, 2006 HorizonWimba, Inc. -+ * Copyright (C) 2007 Wimba, Inc. -+ * -+ * Klaus-Peter Junghanns -+ * -+ * Video Conferencing support added by -+ * Neil Stratford -+ * Copyright (C) 2005, 2005 Vipadia Limited -+ * -+ * VAD driven video conferencing, text message support -+ * and miscellaneous enhancements added by -+ * Mihai Balea -+ * -+ * This program may be modified and distributed under the -+ * terms of the GNU General Public License. You should have received -+ * a copy of the GNU General Public License along with this -+ * program; if not, write to the Free Software Foundation, Inc. -+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#include "asterisk/autoconfig.h" -+#include "frame.h" -+ -+conf_frame* mix_frames( conf_frame* frames_in, int speaker_count, int listener_count ) -+{ -+ if ( frames_in == NULL ) -+ return NULL ; -+ -+ conf_frame* frames_out = NULL ; -+ -+ if ( speaker_count > 1 ) -+ { -+ if ( speaker_count == 2 && listener_count == 0 ) -+ { -+ // optimize here also? -+ frames_out = mix_multiple_speakers( frames_in, speaker_count, listener_count ) ; -+ } -+ else -+ { -+ // mix spoken frames for sending -+ // ( note: this call also releases us from free'ing spoken_frames ) -+ frames_out = mix_multiple_speakers( frames_in, speaker_count, listener_count ) ; -+ } -+ } -+ else if ( speaker_count == 1 ) -+ { -+ // pass-through frames -+ frames_out = mix_single_speaker( frames_in ) ; -+ //printf("mix single speaker\n"); -+ } -+ else -+ { -+ // no frames to send, leave frames_out null -+ } -+ -+ return frames_out ; -+} -+ -+conf_frame* mix_single_speaker( conf_frame* frames_in ) -+{ -+#ifdef APP_CONFERENCE_DEBUG -+ // ast_log( AST_CONF_DEBUG, "returning single spoken frame\n" ) ; -+ -+ // -+ // check input -+ // -+ -+ if ( frames_in == NULL ) -+ { -+ ast_log( AST_CONF_DEBUG, "unable to mix single spoken frame with null frame\n" ) ; -+ return NULL ; -+ } -+ -+ if ( frames_in->fr == NULL ) -+ { -+ ast_log( AST_CONF_DEBUG, "unable to mix single spoken frame with null data\n" ) ; -+ return NULL ; -+ } -+ -+ if ( frames_in->member == NULL ) -+ { -+ ast_log( AST_CONF_DEBUG, "unable to mix single spoken frame with null member\n" ) ; -+ return NULL ; -+ } -+#endif // APP_CONFERENCE_DEBUG -+ -+ // -+ // 'mix' the frame -+ // -+ -+ // copy orignal frame to converted array so listeners don't need to re-encode it -+ frames_in->converted[ frames_in->member->read_format_index ] = ast_frdup( frames_in->fr ) ; -+ -+ // convert frame to slinear, if we have a path -+ frames_in->fr = convert_frame_to_slinear( -+ frames_in->member->to_slinear, -+ frames_in->fr -+ ) ; -+ -+ // set the frame's member to null ( i.e. all listeners ) -+ frames_in->member = NULL ; -+ -+ return frames_in ; -+} -+ -+// { -+ // -+ // a little optimization for testing only: -+ // when two speakers ( of the same type ) and no listeners -+ // are in a conference, we just swamp the frame's member pointers -+ // -+/* -+ if ( -+ listeners == 0 -+ && speakers == 2 -+ && cf_spokenFrames->member->read_format == cf_spokenFrames->next->member->write_format -+ && cf_spokenFrames->member->write_format == cf_spokenFrames->next->member->read_format -+ ) -+ { -+ struct ast_conf_member* m = NULL ; -+ m = cf_spokenFrames->member ; -+ cf_spokenFrames->member = cf_spokenFrames->next->member ; -+ cf_spokenFrames->next->member = m ; -+ return cf_spokenFrames ; -+ } -+*/ -+// } -+ -+void set_conf_frame_delivery( conf_frame* frame, struct timeval time ) -+{ -+ for ( ; frame != NULL ; frame = frame->next ) -+ { -+ if ( frame->fr != NULL ) -+ { -+ // copy passed timeval to frame's delivery timeval -+ frame->fr->delivery = time ; -+ } -+ } -+ -+ return ; -+} -+ -+conf_frame* mix_multiple_speakers( -+ conf_frame* frames_in, -+ int speakers, -+ int listeners -+) -+{ -+#ifdef APP_CONFERENCE_DEBUG -+ // -+ // check input -+ // -+ -+ // no frames to mix -+ if ( ( frames_in == NULL ) || ( frames_in->fr == NULL ) ) -+ { -+ ast_log( AST_CONF_DEBUG, "passed spoken frame list was NULL\n" ) ; -+ return NULL ; -+ } -+ -+ // if less than two speakers, then no frames to mix -+ if ( speakers < 2 ) -+ { -+ ast_log( AST_CONF_DEBUG, "mix_multiple_speakers() called with less than two speakers\n" ) ; -+ return NULL ; -+ } -+#endif // APP_CONFERENCE_DEBUG -+ -+ // -+ // at this point we know that there is more than one frame, -+ // and that the frames need to be converted to pcm to be mixed -+ // -+ // now, if there are only two frames and two members, -+ // we can swap them. ( but we'll get to that later. ) -+ // -+ -+ // -+ // loop through the spoken frames, making a list of spoken members, -+ // and converting gsm frames to slinear frames so we can mix them. -+ // -+ -+ // pointer to the spoken frames list -+ conf_frame* cf_spoken = frames_in ; -+ -+ // pointer to the new list of mixed frames -+ conf_frame* cf_sendFrames = NULL ; -+ -+ while ( cf_spoken != NULL ) -+ { -+ // -+ // while we're looping through the spoken frames, we'll -+ // convert the frame to a format suitable for mixing -+ // -+ // if the frame fails to convert, drop it and treat -+ // the speaking member like a listener by not adding -+ // them to the cf_sendFrames list -+ // -+ -+ if ( cf_spoken->member == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to determine frame member\n" ) ; -+ } -+ else -+ { -+ // ast_log( AST_CONF_DEBUG, "converting frame to slinear, channel => %s\n", cf_spoken->member->channel_name ) ; -+ cf_spoken->fr = convert_frame_to_slinear( -+ cf_spoken->member->to_slinear, -+ cf_spoken->fr -+ ) ; -+ -+ if ( cf_spoken->fr == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to convert frame to slinear\n" ) ; -+ } -+ else -+ { -+ // create new conf frame with last frame as 'next' -+ cf_sendFrames = create_conf_frame( cf_spoken->member, cf_sendFrames, NULL ) ; -+ } -+ } -+ -+ // point to the next spoken frame -+ cf_spoken = cf_spoken->next ; -+ } -+ -+ // if necessary, add a frame with a null member pointer. -+ // this frame will hold the audio mixed for all listeners -+ if ( listeners > 0 ) -+ { -+ cf_sendFrames = create_conf_frame( NULL, cf_sendFrames, NULL ) ; -+ } -+ -+ // -+ // mix the audio -+ // -+ -+ // convenience pointer that skips over the friendly offset -+ char* cp_listenerData ; -+ -+ // pointer to the send frames list -+ conf_frame* cf_send = NULL ; -+ -+ for ( cf_send = cf_sendFrames ; cf_send != NULL ; cf_send = cf_send->next ) -+ { -+ // allocate a mix buffer which fill large enough memory to -+ // hold a frame, and reset it's memory so we don't get noise -+ char* cp_listenerBuffer = malloc( AST_CONF_BUFFER_SIZE ) ; -+ memset( cp_listenerBuffer, 0x0, AST_CONF_BUFFER_SIZE ) ; -+ -+ // point past the friendly offset right to the data -+ cp_listenerData = cp_listenerBuffer + AST_FRIENDLY_OFFSET ; -+ -+ // reset the spoken list pointer -+ cf_spoken = frames_in ; -+ -+ // really mix the audio -+ for ( ; cf_spoken != NULL ; cf_spoken = cf_spoken->next ) -+ { -+ // -+ // if the members are equal, and they -+ // are not null, do not mix them. -+ // -+ if ( -+ ( cf_send->member == cf_spoken->member ) -+ && ( cf_send->member != NULL ) -+ ) -+ { -+ // don't mix this frame -+ } -+ else if ( cf_spoken->fr == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to mix conf_frame with null ast_frame\n" ) ; -+ } -+ else -+ { -+ // mix the new frame in with the existing buffer -+ mix_slinear_frames( cp_listenerData, (char*)( cf_spoken->fr->data ), AST_CONF_BLOCK_SAMPLES);//XXX NAS cf_spoken->fr->samples ) ; -+ } -+ } -+ -+ // copy a pointer to the frame data to the conf_frame -+ cf_send->mixed_buffer = cp_listenerData ; -+ } -+ -+ // -+ // copy the mixed buffer to a new frame -+ // -+ -+ // reset the send list pointer -+ cf_send = cf_sendFrames ; -+ -+ while ( cf_send != NULL ) -+ { -+ cf_send->fr = create_slinear_frame( cf_send->mixed_buffer ) ; -+ cf_send = cf_send->next ; -+ } -+ -+ // -+ // clean up the spoken frames we were passed -+ // ( caller will only be responsible for free'ing returns frames ) -+ // -+ -+ // reset the spoken list pointer -+ cf_spoken = frames_in ; -+ -+ while ( cf_spoken != NULL ) -+ { -+ // delete the frame -+ cf_spoken = delete_conf_frame( cf_spoken ) ; -+ } -+ -+ // return the list of frames for sending -+ return cf_sendFrames ; -+} -+ -+ -+struct ast_frame* convert_frame_to_slinear( struct ast_trans_pvt* trans, struct ast_frame* fr ) -+{ -+ // check for null frame -+ if ( fr == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to translate null frame to slinear\n" ) ; -+ return NULL ; -+ } -+ -+ // we don't need to duplicate this frame since -+ // the normal translation would free it anyway, so -+ // we'll just pretend we free'd and malloc'd a new one. -+ if ( fr->subclass == AST_FORMAT_SLINEAR ) -+ return fr ; -+ -+ // check for null translator ( after we've checked that we need to translate ) -+ if ( trans == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to translate frame with null translation path\n" ) ; -+ return fr ; -+ } -+ -+ // return the converted frame -+ return convert_frame( trans, fr ) ; -+} -+ -+struct ast_frame* convert_frame_from_slinear( struct ast_trans_pvt* trans, struct ast_frame* fr ) -+{ -+ // check for null translator ( after we've checked that we need to translate ) -+ if ( trans == NULL ) -+ { -+ //ast_log( LOG_ERROR, "unable to translate frame with null translation path\n" ) ; -+ return fr ; -+ } -+ -+ // check for null frame -+ if ( fr == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to translate null slinear frame\n" ) ; -+ return NULL ; -+ } -+ -+ // if the frame is not slinear, return an error -+ if ( fr->subclass != AST_FORMAT_SLINEAR ) -+ { -+ ast_log( LOG_ERROR, "unable to translate non-slinear frame\n" ) ; -+ return NULL ; -+ } -+ -+ // return the converted frame -+ return convert_frame( trans, fr ) ; -+} -+ -+struct ast_frame* convert_frame( struct ast_trans_pvt* trans, struct ast_frame* fr ) -+{ -+ if ( trans == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to convert frame with null translator\n" ) ; -+ return NULL ; -+ } -+ -+ if ( fr == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to convert null frame\n" ) ; -+ return NULL ; -+ } -+ -+ // convert the frame -+ struct ast_frame* translated_frame = ast_translate( trans, fr, 1 ) ; -+ -+ // check for errors -+ if ( translated_frame == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to translate frame\n" ) ; -+ return NULL ; -+ } -+ -+ // return the translated frame -+ return translated_frame ; -+} -+ -+conf_frame* delete_conf_frame( conf_frame* cf ) -+{ -+ int c; -+ // check for null frames -+ if ( cf == NULL ) -+ { -+ ast_log( AST_CONF_DEBUG, "unable to delete null conf frame\n" ) ; -+ return NULL ; -+ } -+ -+ // check for frame marked as static -+ if ( cf->static_frame == 1 ) -+ return NULL ; -+ -+ if ( cf->fr != NULL ) -+ { -+ ast_frfree( cf->fr ) ; -+ cf->fr = NULL ; -+ } -+ -+ // make sure converted frames are set to null -+ for ( c = 0 ; c < AC_SUPPORTED_FORMATS ; ++c ) -+ { -+ if ( cf->converted[ c ] != NULL ) -+ { -+ ast_frfree( cf->converted[ c ] ) ; -+ cf->converted[ c ] = NULL ; -+ } -+ } -+ -+ // get a pointer to the next frame -+ // in the list so we can return it -+ conf_frame* nf = cf->next ; -+ -+ free( cf ) ; -+ cf = NULL ; -+ -+ return nf ; -+} -+ -+conf_frame* create_conf_frame( struct ast_conf_member* member, conf_frame* next, const struct ast_frame* fr ) -+{ -+ // pointer to list of mixed frames -+ conf_frame* cf = malloc( sizeof( struct conf_frame ) ) ; -+ -+ if ( cf == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to allocate memory for conf frame\n" ) ; -+ return NULL ; -+ } -+ -+ // -+ // init with some defaults -+ // -+ -+ // make sure converted frames are set to null -+// for ( int c = 0 ; c < AC_SUPPORTED_FORMATS ; ++c ) -+// { -+// cf->converted[ c ] = NULL ; -+// } -+ -+ memset( (struct ast_frame*)( cf->converted ), 0x0, ( sizeof( struct ast_frame* ) * AC_SUPPORTED_FORMATS ) ) ; -+ -+ cf->member = member ; -+ // cf->priority = 0 ; -+ -+ cf->prev = NULL ; -+ cf->next = next ; -+ -+ cf->static_frame = 0 ; -+ -+ // establish relationship to 'next' -+ if ( next != NULL ) next->prev = cf ; -+ -+ // this holds the ast_frame pointer -+ cf->fr = ( fr == NULL ) ? NULL : ast_frdup( ( struct ast_frame* )( fr ) ) ; -+ -+ // this holds the temporu mix buffer -+ cf->mixed_buffer = NULL ; -+ -+ return cf ; -+} -+ -+conf_frame* copy_conf_frame( conf_frame* src ) -+{ -+ // -+ // check inputs -+ // -+ -+ if ( src == NULL ) -+ { -+ ast_log( AST_CONF_DEBUG, "unable to copy null conf frame\n" ) ; -+ return NULL ; -+ } -+ -+ // -+ // copy the frame -+ // -+ -+ struct conf_frame *cfr = NULL ; -+ -+ // create a new conf frame -+ cfr = create_conf_frame( src->member, NULL, src->fr ) ; -+ -+ if ( cfr == NULL ) -+ { -+ ast_log( AST_CONF_DEBUG, "unable to create new conf frame for copy\n" ) ; -+ return NULL ; -+ } -+ -+ return cfr ; -+} -+ -+// -+// Create a TEXT frame based on a given string -+// -+struct ast_frame* create_text_frame(const char *text, int copy) -+{ -+ struct ast_frame *f; -+ char *t; -+ -+ f = calloc(1, sizeof(struct ast_frame)); -+ if ( f == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to allocate memory for text frame\n" ) ; -+ return NULL ; -+ } -+ if ( copy ) -+ { -+ t = calloc(strlen(text) + 1, 1); -+ if ( t == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to allocate memory for text data\n" ) ; -+ free(f); -+ return NULL ; -+ } -+ strncpy(t, text, strlen(text)); -+ } else -+ { -+ t = (char *)text; -+ } -+ -+ f->frametype = AST_FRAME_TEXT; -+ f->offset = 0; -+ f->mallocd = AST_MALLOCD_HDR; -+ if ( copy ) f->mallocd |= AST_MALLOCD_DATA; -+ f->datalen = strlen(t) + 1; -+ f->data = t; -+ f->src = NULL; -+ -+ return f; -+} -+ -+// -+// slinear frame functions -+// -+ -+struct ast_frame* create_slinear_frame( char* data ) -+{ -+ struct ast_frame* f ; -+ -+ f = calloc( 1, sizeof( struct ast_frame ) ) ; -+ if ( f == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to allocate memory for slinear frame\n" ) ; -+ return NULL ; -+ } -+ -+ f->frametype = AST_FRAME_VOICE ; -+ f->subclass = AST_FORMAT_SLINEAR ; -+ f->samples = AST_CONF_BLOCK_SAMPLES ; -+ f->offset = AST_FRIENDLY_OFFSET ; -+ f->mallocd = AST_MALLOCD_HDR | AST_MALLOCD_DATA ; -+ -+ f->datalen = AST_CONF_FRAME_DATA_SIZE ; -+ f->data = data ; -+ -+ f->src = NULL ; -+ -+ return f ; -+} -+ -+void mix_slinear_frames( char *dst, const char *src, int samples ) -+{ -+ if ( dst == NULL ) return ; -+ if ( src == NULL ) return ; -+ -+ int i, val ; -+ -+ for ( i = 0 ; i < samples ; ++i ) -+ { -+ val = ( (short*)dst )[i] + ( (short*)src )[i] ; -+ -+ if ( val > 0x7fff ) -+ { -+ ( (short*)dst )[i] = 0x7fff - 1 ; -+ continue ; -+ } -+ else if ( val < -0x7fff ) -+ { -+ ( (short*)dst )[i] = -0x7fff + 1 ; -+ continue ; -+ } -+ else -+ { -+ ( (short*)dst )[i] = val ; -+ continue ; -+ } -+ } -+ -+ return ; -+} -+ -+// -+// silent frame functions -+// -+ -+conf_frame* get_silent_frame( void ) -+{ -+ static conf_frame* static_silent_frame = NULL ; -+ -+ // we'll let this leak until the application terminates -+ if ( static_silent_frame == NULL ) -+ { -+ // ast_log( AST_CONF_DEBUG, "creating cached silent frame\n" ) ; -+ struct ast_frame* fr = get_silent_slinear_frame() ; -+ -+ static_silent_frame = create_conf_frame( NULL, NULL, fr ) ; -+ -+ if ( static_silent_frame == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to create cached silent frame\n" ) ; -+ return NULL ; -+ } -+ -+ // init the 'converted' slinear silent frame -+ static_silent_frame->converted[ AC_SLINEAR_INDEX ] = get_silent_slinear_frame() ; -+ -+ // mark frame as static so it's not deleted -+ static_silent_frame->static_frame = 1 ; -+ } -+ -+ return static_silent_frame ; -+} -+ -+struct ast_frame* get_silent_slinear_frame( void ) -+{ -+ static struct ast_frame* f = NULL ; -+ -+ // we'll let this leak until the application terminates -+ if ( f == NULL ) -+ { -+ char* data = malloc( AST_CONF_BUFFER_SIZE ) ; -+ memset( data, 0x0, AST_CONF_BUFFER_SIZE ) ; -+ f = create_slinear_frame( data ) ; -+ } -+ -+ return f; -+} -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/apps/conference/frame.h b/apps/conference/frame.h -new file mode 100644 -index 0000000..2b77805 ---- /dev/null -+++ b/apps/conference/frame.h -@@ -0,0 +1,75 @@ -+ -+// $Id: frame.h 746 2006-12-11 20:12:12Z sbalea $ -+ -+/* -+ * app_conference -+ * -+ * A channel independent conference application for Asterisk -+ * -+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH -+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc. -+ * Copyright (C) 2005, 2006 HorizonWimba, Inc. -+ * Copyright (C) 2007 Wimba, Inc. -+ * -+ * Klaus-Peter Junghanns -+ * -+ * Video Conferencing support added by -+ * Neil Stratford -+ * Copyright (C) 2005, 2005 Vipadia Limited -+ * -+ * VAD driven video conferencing, text message support -+ * and miscellaneous enhancements added by -+ * Mihai Balea -+ * -+ * This program may be modified and distributed under the -+ * terms of the GNU General Public License. You should have received -+ * a copy of the GNU General Public License along with this -+ * program; if not, write to the Free Software Foundation, Inc. -+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef _APP_CONF_FRAME_H -+#define _APP_CONF_FRAME_H -+ -+// -+// includes -+// -+ -+#include "app_conference.h" -+#include "common.h" -+ -+// -+// function declarations -+// -+ -+// mixing -+conf_frame* mix_frames( conf_frame* frames_in, int speaker_count, int listener_count ) ; -+ -+conf_frame* mix_multiple_speakers( conf_frame* frames_in, int speakers, int listeners ) ; -+conf_frame* mix_single_speaker( conf_frame* frames_in ) ; -+ -+// frame creation and deletion -+conf_frame* create_conf_frame( struct ast_conf_member* member, conf_frame* next, const struct ast_frame* fr ) ; -+conf_frame* delete_conf_frame( conf_frame* cf ) ; -+conf_frame* copy_conf_frame( conf_frame* src ) ; -+ -+// convert frame functions -+struct ast_frame* convert_frame_to_slinear( struct ast_trans_pvt* trans, struct ast_frame* fr ) ; -+struct ast_frame* convert_frame_from_slinear( struct ast_trans_pvt* trans, struct ast_frame* fr ) ; -+struct ast_frame* convert_frame( struct ast_trans_pvt* trans, struct ast_frame* fr ) ; -+ -+// text frame function(s) -+struct ast_frame* create_text_frame(const char *text, int copy); -+ -+// slinear frame functions -+struct ast_frame* create_slinear_frame( char* data ) ; -+void mix_slinear_frames( char* dst, const char* src, int samples ) ; -+ -+// silent frame functions -+conf_frame* get_silent_frame( void ) ; -+struct ast_frame* get_silent_slinear_frame( void ) ; -+ -+// set delivery timestamp for frames -+void set_conf_frame_delivery( conf_frame* frame, struct timeval time ) ; -+ -+#endif -diff --git a/apps/conference/member.c b/apps/conference/member.c -new file mode 100644 -index 0000000..d7f98af ---- /dev/null -+++ b/apps/conference/member.c -@@ -0,0 +1,3332 @@ -+ -+// $Id: member.c 885 2007-06-27 15:41:18Z sbalea $ -+ -+/* -+ * app_conference -+ * -+ * A channel independent conference application for Asterisk -+ * -+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH -+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc. -+ * Copyright (C) 2005, 2006 HorizonWimba, Inc. -+ * Copyright (C) 2007 Wimba, Inc. -+ * -+ * Klaus-Peter Junghanns -+ * -+ * Video Conferencing support added by -+ * Neil Stratford -+ * Copyright (C) 2005, 2005 Vipadia Limited -+ * -+ * VAD driven video conferencing, text message support -+ * and miscellaneous enhancements added by -+ * Mihai Balea -+ * -+ * This program may be modified and distributed under the -+ * terms of the GNU General Public License. You should have received -+ * a copy of the GNU General Public License along with this -+ * program; if not, write to the Free Software Foundation, Inc. -+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#include -+#include "asterisk/channel.h" -+#include "asterisk/app.h" -+#include "asterisk/autoconfig.h" -+#include "member.h" -+ -+ -+// process an incoming frame. Returns 0 normally, 1 if hangup was received. -+static int process_incoming(struct ast_conf_member *member, struct ast_conference *conf, struct ast_frame *f) -+{ -+ int silent_frame = 0; -+ struct ast_conf_member *src_member ; -+ -+ // In Asterisk 1.4 AST_FRAME_DTMF is equivalent to AST_FRAME_DTMF_END -+ if (f->frametype == AST_FRAME_DTMF) -+ { -+ if (member->dtmf_switch) -+ { -+ ast_mutex_lock( &member->lock ) ; -+ switch (f->subclass) { -+ case '0' :member->req_id=0; -+ break; -+ case '1' :member->req_id=1; -+ break; -+ case '2' :member->req_id=2; -+ break; -+ case '3' :member->req_id=3; -+ break; -+ case '4' :member->req_id=4; -+ break; -+ case '5' :member->req_id=5; -+ break; -+ case '6' :member->req_id=6; -+ break; -+ case '7' :member->req_id=7; -+ break; -+ case '8' :member->req_id=8; -+ break; -+ case '9' :member->req_id=9; -+ break; -+ case '*' : -+ if (member->mute_video == 0 && member->mute_audio == 0) -+ { -+ member->mute_video = 1; -+ member->mute_audio = 1; -+ } -+ else if (member->mute_video == 1 && member->mute_audio == 1) -+ { -+ member->mute_video = 0; -+ member->mute_audio = 0; -+ } -+ break; -+ } -+ member->conference = 1; // switch me -+ ast_mutex_unlock( &member->lock ) ; -+ } -+ if (member->dtmf_relay) -+ { -+ // output to manager... -+ manager_event( -+ EVENT_FLAG_CALL, -+ "ConferenceDTMF", -+ "ConferenceName: %s\r\n" -+ "Channel: %s\r\n" -+ "CallerID: %s\r\n" -+ "CallerIDName: %s\r\n" -+ "Key: %c\r\n", -+ conf->name, -+ member->channel_name, -+ member->chan->cid.cid_num ? member->chan->cid.cid_num : "unknown", -+ member->chan->cid.cid_name ? member->chan->cid.cid_name : "unknown", -+ f->subclass -+ ) ; -+ -+ } -+ if (!member->dtmf_switch && !member->dtmf_relay) -+ { -+ // relay this to the listening channels -+ queue_incoming_dtmf_frame( member, f ); -+ } -+ } else if (f->frametype == AST_FRAME_DTMF_BEGIN) -+ { -+ if (!member->dtmf_switch && !member->dtmf_relay) -+ { -+ // relay this to the listening channels -+ queue_incoming_dtmf_frame( member, f ); -+ } -+ } -+ -+ ast_mutex_lock( &member->lock ) ; -+ // Handle a local or remote conference -+ if (member->conference) -+ { -+ int req_id = member->req_id; -+ ast_mutex_unlock( &member->lock ); -+ // this will return NULL or a locked member -+ src_member = check_active_video(req_id,conf); -+ // Stream a picture to the recipient if no active video -+ if (!src_member) -+ { -+ // Mihai: we don't want to send video here, we cannot negotiate codec -+ // and we don't know what codec the conference is using -+ //if (member->norecv_video == 0) -+ //{ -+ // if(!ast_streamfile(member->chan,"novideo",member->chan->language)) -+ // { -+ // ast_waitstream(member->chan,""); -+ // } -+ //} -+ } -+ else -+ { -+ // Send a FIR to the new sender -+ ast_indicate(src_member->chan,AST_CONTROL_VIDUPDATE); -+ // we will have locked in check_active_video() -+ ast_mutex_unlock( &src_member->lock); -+ } -+ ast_mutex_lock( &member->lock ); -+ member->conference = 0; -+ } -+ ast_mutex_unlock( &member->lock ); -+ -+ -+ if ((f->frametype == AST_FRAME_VOICE && member->mute_audio == 1) || (f->frametype == AST_FRAME_VIDEO && member->mute_video == 1)) -+ { -+ // this is a listen-only user, ignore the frame -+ //ast_log( AST_CONF_DEBUG, "Listen only user frame"); -+ ast_frfree( f ) ; -+ f = NULL ; -+ } -+ else if ( f->frametype == AST_FRAME_VOICE ) -+ { //ast_log( AST_CONF_DEBUG, "Got voice frame"); -+ // reset silence detection flag -+ silent_frame = 0 ; -+ -+ // accounting: count the incoming frame -+ member->frames_in++ ; -+ -+#if ( SILDET == 2 ) -+ // -+ // make sure we have a valid dsp and frame type -+ // -+ if ( -+ member->dsp != NULL -+ && f->subclass == AST_FORMAT_SLINEAR -+ && f->datalen == AST_CONF_FRAME_DATA_SIZE -+ ) -+ { -+ // send the frame to the preprocessor -+ int spx_ret; -+ spx_ret = speex_preprocess( member->dsp, f->data, NULL ); -+#ifdef DEBUG_USE_TIMELOG -+ TIMELOG(spx_ret, 3, "speex_preprocess"); -+#endif -+ if ( spx_ret == 0 ) -+ { -+ // -+ // we ignore the preprocessor's outcome if we've seen voice frames -+ // in within the last AST_CONF_SKIP_SPEEX_PREPROCESS frames -+ // -+ if ( member->ignore_speex_count > 0 ) -+ { -+ // ast_log( AST_CONF_DEBUG, "ignore_speex_count => %d\n", ignore_speex_count ) ; -+ -+ // skip speex_preprocess(), and decrement counter -+ --member->ignore_speex_count ; -+ } -+ else -+ { -+ // set silent_frame flag -+ silent_frame = 1 ; -+ } -+ } -+ else -+ { -+ // voice detected, reset skip count -+ member->ignore_speex_count = AST_CONF_SKIP_SPEEX_PREPROCESS ; -+ } -+ } -+#endif -+ if ( !silent_frame ) -+ queue_incoming_frame( member, f ); -+ -+ // free the original frame -+ ast_frfree( f ) ; -+ f = NULL ; -+ -+ } -+ else if (f->frametype == AST_FRAME_VIDEO) -+ { -+ queue_incoming_video_frame( member, f ); -+ -+ // free the original frame -+ ast_frfree( f ) ; -+ f = NULL ; -+ -+ } -+ else if ( -+ f->frametype == AST_FRAME_CONTROL -+ && f->subclass == AST_CONTROL_HANGUP -+ ) -+ { -+ // hangup received -+ -+ // free the frame -+ ast_frfree( f ) ; -+ f = NULL ; -+ -+ // break out of the while ( 42 == 42 ) -+ return 1; -+ } -+ else if ( -+ f->frametype == AST_FRAME_CONTROL -+ && f->subclass == AST_CONTROL_VIDUPDATE -+ ) -+ { -+ // say we have switched to cause a FIR to -+ // be sent to the sender -+ ast_mutex_lock( &member->lock ) ; -+ member->conference = 1; -+ ast_mutex_unlock( &member->lock ) ; -+ -+ // free the original frame -+ ast_frfree( f ) ; -+ f = NULL ; -+ } -+ else if ( f->frametype == AST_FRAME_TEXT && member->does_text ) -+ { -+ if ( strncmp(f->data, AST_CONF_CONTROL_CAMERA_DISABLED, strlen(AST_CONF_CONTROL_CAMERA_DISABLED)) == 0 ) -+ { -+ ast_mutex_lock(&member->lock); -+ manager_event(EVENT_FLAG_CALL, -+ "ConferenceCameraDisabled", -+ "ConferenceName: %s\r\nChannel: %s\r\n", -+ conf->name, -+ member->channel_name); -+ member->no_camera = 1; -+ ast_mutex_unlock(&member->lock); -+ } else if ( strncmp(f->data, AST_CONF_CONTROL_CAMERA_ENABLED, strlen(AST_CONF_CONTROL_CAMERA_ENABLED)) == 0 ) -+ { -+ ast_mutex_lock(&member->lock); -+ manager_event(EVENT_FLAG_CALL, -+ "ConferenceCameraEnabled", -+ "ConferenceName: %s\r\nChannel: %s\r\n", -+ conf->name, -+ member->channel_name); -+ member->no_camera = 0; -+ ast_mutex_unlock(&member->lock); -+ } else if ( strncmp(f->data, AST_CONF_CONTROL_STOP_VIDEO_TRANSMIT, strlen(AST_CONF_CONTROL_STOP_VIDEO_TRANSMIT)) == 0 ) -+ { -+ ast_mutex_lock(&member->lock); -+ manager_event(EVENT_FLAG_CALL, -+ "ConferenceStopVideoTransmit", -+ "ConferenceName: %s\r\nChannel: %s\r\n", -+ conf->name, -+ member->channel_name); -+ member->norecv_video = 1; -+ ast_mutex_unlock(&member->lock); -+ } else if ( strncmp(f->data, AST_CONF_CONTROL_START_VIDEO_TRANSMIT, strlen(AST_CONF_CONTROL_START_VIDEO_TRANSMIT)) == 0 ) -+ { -+ ast_mutex_lock(&member->lock); -+ manager_event(EVENT_FLAG_CALL, -+ "ConferenceStartVideoTransmit", -+ "ConferenceName: %s\r\nChannel: %s\r\n", -+ conf->name, -+ member->channel_name); -+ member->norecv_video = 0; -+ ast_mutex_unlock(&member->lock); -+ } -+ ast_frfree(f); -+ f = NULL; -+ } else -+ { -+ // undesirables -+ ast_frfree( f ) ; -+ f = NULL ; -+ } -+ -+ return 0; -+} -+ -+// get the next frame from the soundq; must be called with member locked. -+static struct ast_frame *get_next_soundframe(struct ast_conf_member *member, struct ast_frame -+ *exampleframe) { -+ struct ast_frame *f; -+ -+again: -+ f=ast_readframe(member->soundq->stream); -+ -+ if(!f) { // we're done with this sound; remove it from the queue, and try again -+ struct ast_conf_soundq *toboot = member->soundq; -+ -+ ast_closestream(toboot->stream); -+ member->soundq = toboot->next; -+ -+ //ast_log( LOG_WARNING, "finished playing a sound, next = %x\n", member->soundq); -+ // notify applications via mgr interface that this sound has been played -+ manager_event( -+ EVENT_FLAG_CALL, -+ "ConferenceSoundComplete", -+ "Channel: %s\r\n" -+ "Sound: %s\r\n", -+ member->channel_name, -+ toboot->name -+ ); -+ -+ free(toboot); -+ if(member->soundq) goto again; -+ -+ // if we get here, we've gotten to the end of the queue; reset write format -+ if ( ast_set_write_format( member->chan, member->write_format ) < 0 ) -+ { -+ ast_log( LOG_ERROR, "unable to set write format to %d\n", -+ member->write_format ) ; -+ } -+ } else { -+ // copy delivery from exampleframe -+ f->delivery = exampleframe->delivery; -+ } -+ -+ return f; -+} -+ -+ -+// process outgoing frames for the channel, playing either normal conference audio, -+// or requested sounds -+static int process_outgoing(struct ast_conf_member *member) -+{ -+ conf_frame* cf ; // frame read from the output queue -+ struct ast_frame *f; -+ struct ast_frame *realframe = NULL; -+ -+ for(;;) -+ { -+ // acquire member mutex and grab a frame. -+ ast_mutex_lock( &member->lock ) ; -+ cf = get_outgoing_frame( member ) ; -+ -+ // if there's no frames exit the loop. -+ if ( !cf ) -+ { -+ ast_mutex_unlock( &member->lock ) ; -+ break; -+ } -+ -+ -+ f = cf->fr; -+ -+ // if we're playing sounds, we can just replace the frame with the -+ // next sound frame, and send it instead -+ if ( member->soundq ) -+ { -+ realframe = f; -+ f = get_next_soundframe(member, f); -+ if ( !f ) -+ { -+ // if we didn't get anything, just revert to "normal" -+ f = realframe; -+ realframe = NULL; -+ } else -+ { -+ // We have a sound frame now, but we need to make sure it's the same -+ // format as our channel write format -+ int wf = member->chan->writeformat & AST_FORMAT_AUDIO_MASK; -+ if ( f->frametype == AST_FRAME_VOICE && !(wf & f->subclass) ) -+ { -+ // We need to change our channel's write format -+ ast_set_write_format(member->chan, f->subclass); -+ } -+ } -+ } -+ -+ ast_mutex_unlock(&member->lock); -+ -+ -+#ifdef DEBUG_FRAME_TIMESTAMPS -+ // !!! TESTING !!! -+ int delivery_diff = usecdiff( &f->delivery, &member->lastsent_timeval ) ; -+ if ( delivery_diff != AST_CONF_FRAME_INTERVAL ) -+ { -+ ast_log( AST_CONF_DEBUG, "unanticipated delivery time, delivery_diff => %d, delivery.tv_usec => %ld\n", -+ delivery_diff, f->delivery.tv_usec ) ; -+ } -+ -+ // !!! TESTING !!! -+ if ( -+ f->delivery.tv_sec < member->lastsent_timeval.tv_sec -+ || ( -+ f->delivery.tv_sec == member->lastsent_timeval.tv_sec -+ && f->delivery.tv_usec <= member->lastsent_timeval.tv_usec -+ ) -+ ) -+ { -+ ast_log( LOG_WARNING, "queued frame timestamped in the past, %ld.%ld <= %ld.%ld\n", -+ f->delivery.tv_sec, f->delivery.tv_usec, -+ member->lastsent_timeval.tv_sec, member->lastsent_timeval.tv_usec ) ; -+ } -+ member->lastsent_timeval = f->delivery ; -+#endif -+ -+#ifdef DEBUG_USE_TIMELOG -+ TIMELOG( ast_write( member->chan, f ), 10, "member: ast_write"); -+#else -+ -+ // send the voice frame -+ if ( ast_write( member->chan, f ) == 0 ) -+ { -+ struct timeval tv = ast_tvnow(); -+ ast_log( AST_CONF_DEBUG, "SENT VOICE FRAME, channel => %s, frames_out => %ld, s => %ld, ms => %ld\n", -+ member->channel_name, member->frames_out, tv.tv_sec, tv.tv_usec ) ; -+ } -+ else -+ { -+ // log 'dropped' outgoing frame -+ ast_log( LOG_ERROR, "unable to write voice frame to channel, channel => %s\n", member->channel_name ) ; -+ -+ // accounting: count dropped outgoing frames -+ member->frames_out_dropped++ ; -+ } -+#endif -+ // clean up frame -+ delete_conf_frame( cf ) ; -+ -+ } -+ -+ // Do the same for video, suck it dry -+ for(;;) -+ { -+ // grab a frame. -+ cf = get_outgoing_video_frame( member ) ; -+ -+ // if there's no frames exit the loop. -+ if(!cf){ -+ break; -+ } -+ -+ f = cf->fr; -+ -+ // send the video frame -+ if ( ast_write_video( member->chan, f ) == 1 ) -+ { -+ struct timeval tv = ast_tvnow(); -+ ast_log( AST_CONF_DEBUG, "SENT VIDEO FRAME, channel => %s, frames_out => %ld, s => %ld, ms => %ld\n", -+ member->channel_name, member->frames_out, tv.tv_sec, tv.tv_usec ) ; -+ } -+ else -+ { -+ // log 'dropped' outgoing frame -+ ast_log( AST_CONF_DEBUG, "unable to write video frame to channel, channel => %s\n", member->channel_name ) ; -+ -+ // accounting: count dropped outgoing frames -+ member->video_frames_out_dropped++ ; -+ } -+ -+ // clean up frame -+ delete_conf_frame( cf ) ; -+ -+ } -+ -+ // Do the same for dtmf, suck it dry -+ for(;;) -+ { -+ // acquire member mutex and grab a frame. -+ cf = get_outgoing_dtmf_frame( member ) ; -+ -+ // if there's no frames exit the loop. -+ if(!cf) break; -+ -+ // send the dtmf frame -+ if ( ast_write( member->chan, cf->fr ) == 0 ) -+ { -+ struct timeval tv = ast_tvnow(); -+ ast_log( AST_CONF_DEBUG, "SENT DTMF FRAME, channel => %s, frames_out => %ld, s => %ld, ms => %ld\n", -+ member->channel_name, member->frames_out, tv.tv_sec, tv.tv_usec ) ; -+ -+ } -+ else -+ { -+ // log 'dropped' outgoing frame -+ ast_log( AST_CONF_DEBUG, "unable to write dtmf frame to channel, channel => %s\n", member->channel_name ) ; -+ -+ // accounting: count dropped outgoing frames -+ member->dtmf_frames_out_dropped++ ; -+ } -+ -+ // clean up frame -+ delete_conf_frame( cf ) ; -+ } -+ -+ // Do the same for text, hell, why not? -+ for(;;) -+ { -+ // acquire member mutex and grab a frame. -+ cf = get_outgoing_text_frame( member ) ; -+ -+ // if there's no frames exit the loop. -+ if(!cf) break; -+ -+ // send the text frame -+ if ( ast_write( member->chan, cf->fr ) == 0 ) -+ { -+ struct timeval tv = ast_tvnow(); -+ ast_log( AST_CONF_DEBUG, "SENT TEXT FRAME, channel => %s, frames_out => %ld, s => %ld, ms => %ld\n", -+ member->channel_name, member->frames_out, tv.tv_sec, tv.tv_usec ) ; -+ -+ } -+ else -+ { -+ // log 'dropped' outgoing frame -+ ast_log( AST_CONF_DEBUG, "unable to write text frame to channel, channel => %s\n", member->channel_name ) ; -+ -+ // accounting: count dropped outgoing frames -+ member->text_frames_out_dropped++ ; -+ } -+ -+ // clean up frame -+ delete_conf_frame( cf ) ; -+ } -+ -+ -+ return 0; -+} -+ -+static int member_checkkick( struct ast_conf_member *member ) -+{ -+ int kick; -+ ast_mutex_lock( &member->lock ) ; -+ kick = member->kick_flag; -+ ast_mutex_unlock( &member->lock ) ; -+ return kick; -+} -+ -+// -+// main member thread function -+// -+ -+int member_exec( struct ast_channel* chan, void* data ) -+{ -+// struct timeval start, end ; -+// start = ast_tvnow(); -+ -+ struct ast_conference *conf ; -+ struct ast_conf_member *member ; -+ -+ struct ast_frame *f ; // frame received from ast_read() -+ -+ int left = 0 ; -+ int res; -+ -+ ast_log( AST_CONF_DEBUG, "Begin processing member thread, channel => %s\n", chan->name ) ; -+ -+ // -+ // If the call has not yet been answered, answer the call -+ // Note: asterisk apps seem to check _state, but it seems like it's safe -+ // to just call ast_answer. It will just do nothing if it is up. -+ // it will also return -1 if the channel is a zombie, or has hung up. -+ // -+ -+ res = ast_answer( chan ) ; -+ if ( res ) -+ { -+ ast_log( LOG_ERROR, "unable to answer call\n" ) ; -+ return -1 ; -+ } -+ -+ // -+ // create a new member for the conference -+ // -+ -+// ast_log( AST_CONF_DEBUG, "creating new member, id => %s, flags => %s, p => %s\n", -+// id, flags, priority ) ; -+ -+ member = create_member( chan, (const char*)( data ) ) ; // flags, atoi( priority ) ) ; -+ -+ // unable to create member, return an error -+ if ( member == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to create member\n" ) ; -+ return -1 ; -+ } -+ -+ // -+ // setup asterisk read/write formats -+ // -+#if 0 -+ ast_log( AST_CONF_DEBUG, "CHANNEL INFO, CHANNEL => %s, DNID => %s, CALLER_ID => %s, ANI => %s\n", -+ chan->name, chan->dnid, chan->callerid, chan->ani ) ; -+ -+ ast_log( AST_CONF_DEBUG, "CHANNEL CODECS, CHANNEL => %s, NATIVE => %d, READ => %d, WRITE => %d\n", -+ chan->name, chan->nativeformats, member->read_format, member->write_format ) ; -+#endif -+ if ( ast_set_read_format( chan, member->read_format ) < 0 ) -+ { -+ ast_log( LOG_ERROR, "unable to set read format to signed linear\n" ) ; -+ delete_member( member ) ; -+ return -1 ; -+ } -+ -+ if ( ast_set_write_format( chan, member->write_format ) < 0 ) // AST_FORMAT_SLINEAR, chan->nativeformats -+ { -+ ast_log( LOG_ERROR, "unable to set write format to signed linear\n" ) ; -+ delete_member( member ) ; -+ return -1 ; -+ } -+ -+ // -+ // setup a conference for the new member -+ // -+ -+ conf = start_conference( member ) ; -+ -+ if ( conf == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to setup member conference\n" ) ; -+ delete_member( member) ; -+ return -1 ; -+ } -+ -+ -+ manager_event( -+ EVENT_FLAG_CALL, -+ "ConferenceJoin", -+ "ConferenceName: %s\r\n" -+ "Member: %d\r\n" -+ "Channel: %s\r\n" -+ "CallerID: %s\r\n" -+ "CallerIDName: %s\r\n" -+ "Count: %d\r\n", -+ conf->name, -+ member->id, -+ member->channel_name, -+ member->chan->cid.cid_num ? member->chan->cid.cid_num : "unknown", -+ member->chan->cid.cid_name ? member->chan->cid.cid_name: "unknown", -+ conf->membercount -+ ) ; -+ -+ // Store the CID information -+ if ( member->chan->cid.cid_num ) -+ { -+ if ( (member->callerid = malloc(strlen(member->chan->cid.cid_num)+1)) ) -+ memcpy(member->callerid,member->chan->cid.cid_num, strlen(member->chan->cid.cid_num)+1); -+ } else -+ member->callerid = NULL; -+ -+ if ( member->chan->cid.cid_name ) -+ { -+ if ( (member->callername = malloc(strlen(member->chan->cid.cid_name)+1)) ) -+ memcpy(member->callername, member->chan->cid.cid_name, strlen(member->chan->cid.cid_name)+1); -+ } else -+ member->callername = NULL; -+ -+ -+ // -+ // process loop for new member ( this runs in it's own thread ) -+ // -+ -+ ast_log( AST_CONF_DEBUG, "begin member event loop, channel => %s\n", chan->name ) ; -+ -+ // timer timestamps -+ struct timeval base, curr ; -+ base = ast_tvnow(); -+ -+ // tell conference_exec we're ready for frames -+ member->ready_for_outgoing = 1 ; -+ -+ // notify others about enter -+ char * enter_snd = member->enter_snd; -+ if (strcmp(enter_snd, "-")!=0) -+ { -+ // lock conference -+ ast_mutex_lock( &conf->lock ); -+ -+ /*if (conf->membercount < 2) -+ { -+ enter_snd = "conf-onlyperson"; -+ }*/ -+ -+ struct ast_conf_member *membertest = conf->memberlist; -+ while (membertest != NULL) -+ { -+ // lock member for basic_play_sound -+ ast_mutex_lock(&membertest->lock); -+ -+ // basic_play_sound unlock member automatically. do not mute on enter message -+ if (!basic_play_sound ( membertest, enter_snd, 0 )) -+ { -+ ast_log(LOG_ERROR, "playing conference[%d] entry message <%s> FAILED on <%s>\n", conf->membercount, enter_snd, membertest->channel_name); -+ } -+ else -+ { -+ ast_log(LOG_NOTICE, "playing conference[%d] entry message <%s> on <%s>\n", conf->membercount, enter_snd, membertest->channel_name); -+ } -+ -+ membertest = membertest->next; -+ } -+ -+ // unlock conference -+ ast_mutex_unlock( &conf->lock ); -+ } -+ -+ while ( 42 == 42 ) -+ { -+ // make sure we have a channel to process -+ if ( chan == NULL ) -+ { -+ ast_log( LOG_NOTICE, "member channel has closed\n" ) ; -+ break ; -+ } -+ -+ //-----------------// -+ // INCOMING FRAMES // -+ //-----------------// -+ -+ // wait for an event on this channel -+ left = ast_waitfor( chan, AST_CONF_WAITFOR_LATENCY ) ; -+ -+ //ast_log( AST_CONF_DEBUG, "received event on channel, name => %s, left => %d\n", chan->name, left ) ; -+ -+ if ( left < 0 ) -+ { -+ // an error occured -+ ast_log( -+ LOG_NOTICE, -+ "an error occured waiting for a frame, channel => %s, error => %d\n", -+ chan->name, left -+ ) ; -+ break; // out of the 42==42 -+ } -+ else if ( left == 0 ) -+ { -+ // no frame has arrived yet -+ // ast_log( LOG_NOTICE, "no frame available from channel, channel => %s\n", chan->name ) ; -+ } -+ else if ( left > 0 ) -+ { -+ // a frame has come in before the latency timeout -+ // was reached, so we process the frame -+ -+ f = ast_read( chan ) ; -+ -+ if ( f == NULL ) -+ { -+ if (conf->debug_flag) -+ { -+ ast_log( LOG_NOTICE, "unable to read from channel, channel => %s\n", chan->name ) ; -+ // They probably want to hangup... -+ } -+ break ; -+ } -+ -+ // actually process the frame: break if we got hangup. -+ if(process_incoming(member, conf, f)) break; -+ -+ } -+ -+ if (member_checkkick(member)) break; -+ -+ //-----------------// -+ // OUTGOING FRAMES // -+ //-----------------// -+ -+ // update the current timestamps -+ curr = ast_tvnow(); -+ -+ process_outgoing(member); -+ // back to process incoming frames -+ continue ; -+ } -+ -+ ast_log( AST_CONF_DEBUG, "end member event loop, time_entered => %ld\n", member->time_entered.tv_sec ) ; -+ -+ // -+ // clean up -+ // -+ -+#ifdef DEBUG_OUTPUT_PCM -+ // !!! TESTING !!! -+ if ( incoming_fh != NULL ) -+ fclose( incoming_fh ) ; -+#endif -+ -+ // If we're driving another member, make sure its speaker count is correct -+ if ( member != NULL ) member->remove_flag = 1 ; -+ -+// end = ast_tvnow(); -+// int expected_frames = ( int )( floor( (double)( msecdiff( &end, &start ) / AST_CONF_FRAME_INTERVAL ) ) ) ; -+// ast_log( AST_CONF_DEBUG, "expected_frames => %d\n", expected_frames ) ; -+ -+ return 0 ; -+} -+ -+ -+ -+struct ast_conf_member *check_active_video( int id, struct ast_conference *conf ) -+{ -+ struct ast_conf_member *member; -+ -+ // acquire the conference lock -+ ast_mutex_lock( &conf->lock ) ; -+ -+ member = conf->memberlist; -+ while (member) -+ { -+ if (member->id == id) -+ { -+ // lock this member -+ ast_mutex_lock( &member->lock ) ; -+ ast_mutex_unlock( &conf->lock ) ; -+ return member; -+ } -+ member = member->next; -+ } -+ ast_mutex_unlock( &conf->lock ) ; -+ return NULL; -+} -+ -+// -+// manange member functions -+// -+ -+struct ast_conf_member* create_member( struct ast_channel *chan, const char* data ) -+{ -+ char *parse; -+ AST_DECLARE_APP_ARGS(args, -+ AST_APP_ARG(conf_name); -+ AST_APP_ARG(flags); -+ AST_APP_ARG(enter_snd); -+ AST_APP_ARG(leave_snd); -+ AST_APP_ARG(priority); -+ AST_APP_ARG(vad_prob_start); -+ AST_APP_ARG(vad_prob_continue); -+ ); -+ -+ // -+ // check input -+ // -+ -+ if ( chan == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to create member with null channel\n" ) ; -+ return NULL ; -+ } -+ -+ if ( chan->name == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to create member with null channel name\n" ) ; -+ return NULL ; -+ } -+ -+ if (ast_strlen_zero(data)) { -+ ast_log(LOG_ERROR, "Conference requires an argument (conf_name[|flags[|enter_snd[|leave_snd[|priority[vad_prob_start[|vad_prob_continue]]]]]]))\n"); -+ return NULL; -+ } -+ -+ // -+ // allocate memory for new conference member -+ // -+ -+ struct ast_conf_member *member = calloc( 1, sizeof( struct ast_conf_member ) ) ; -+ -+ if ( member == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to malloc ast_conf_member\n" ) ; -+ return NULL ; -+ } -+ -+ // initialize mutex -+ ast_mutex_init( &member->lock ) ; -+ -+ // -+ // initialize member with passed data values -+ // -+ -+ parse = ast_strdupa(data); -+ -+ AST_STANDARD_APP_ARGS(args, parse); -+ -+ // parse the id -+ if (ast_strlen_zero(args.conf_name)) -+ { -+ ast_log( LOG_ERROR, "unable to parse member id\n" ) ; -+ free( member ) ; -+ return NULL ; -+ } -+ else -+ member->conf_name = ast_strdup(args.conf_name); -+ -+ // parse the flags -+ if (!ast_strlen_zero(args.flags)) -+ member->flags = ast_strdup(args.flags); -+ else -+ member->flags = ast_strdup(""); -+ -+ // parse enter sound -+ if (!ast_strlen_zero(args.enter_snd)) -+ member->enter_snd = ast_strdup(args.enter_snd); -+ else -+ member->enter_snd = ast_strdup("enter"); -+ -+ // parse leave sound -+ if (!ast_strlen_zero(args.leave_snd)) -+ member->leave_snd = ast_strdup(args.leave_snd); -+ else -+ member->leave_snd = ast_strdup("leave"); -+ -+ if (!ast_strlen_zero(args.priority)) -+ // parse the priority -+ member->priority = atoi(args.priority); -+ else -+ member->priority = 0; -+ -+ if (!ast_strlen_zero(args.vad_prob_start)) -+ member->vad_prob_start = atof(args.vad_prob_start); -+ else -+ member->vad_prob_start = AST_CONF_PROB_START; -+ -+ if (!ast_strlen_zero(args.vad_prob_continue)) -+ member->vad_prob_continue = atof(args.vad_prob_continue); -+ else -+ member->vad_prob_continue = AST_CONF_PROB_CONTINUE; -+ -+ // debugging -+ ast_log( -+ AST_CONF_DEBUG, -+ "parsed data params, id => %s, flags => %s, priority => %d, vad_prob_start => %f, vad_prob_continue => %f\n", -+ member->conf_name, member->flags, member->priority, member->vad_prob_start, member->vad_prob_continue -+ ) ; -+ -+ // -+ // initialize member with default values -+ // -+ -+ // keep pointer to member's channel -+ member->chan = chan ; -+ -+ // copy the channel name -+ member->channel_name = malloc( strlen( chan->name ) + 1 ) ; -+ strcpy( member->channel_name, chan->name ) ; -+ -+ // ( default can be overridden by passed flags ) -+ member->mute_audio = 0; -+ member->mute_video = 0; -+ member->norecv_audio = 0; -+ member->norecv_video = 0; -+ member->no_camera = 0; -+ -+ // moderator? -+ member->ismoderator = 0; -+ -+ // ready flag -+ member->ready_for_outgoing = 0 ; -+ -+ // incoming frame queue -+ member->inFrames = NULL ; -+ member->inFramesTail = NULL ; -+ member->inFramesCount = 0 ; -+ -+ member->inVideoFrames = NULL ; -+ member->inVideoFramesTail = NULL ; -+ member->inVideoFramesCount = 0 ; -+ -+ member->inDTMFFrames = NULL ; -+ member->inDTMFFramesTail = NULL ; -+ member->inDTMFFramesCount = 0 ; -+ -+ member->inTextFrames = NULL ; -+ member->inTextFramesTail = NULL ; -+ member->inTextFramesCount = 0 ; -+ -+ member->conference = 1; // we have switched req_id -+ member->dtmf_switch = 0; // no dtmf switch by default -+ member->dtmf_relay = 0; // no dtmf relay by default -+ -+ // start of day video ids -+ member->req_id = -1; -+ member->id = -1; -+ -+ member->first_frame_received = 0; // cause a FIR after NAT delay -+ -+ // last frame caching -+ member->inFramesRepeatLast = 0 ; -+ member->inFramesLast = NULL ; -+ member->okayToCacheLast = 0 ; -+ -+ // outgoing frame queue -+ member->outFrames = NULL ; -+ member->outFramesTail = NULL ; -+ member->outFramesCount = 0 ; -+ -+ member->outVideoFrames = NULL ; -+ member->outVideoFramesTail = NULL ; -+ member->outVideoFramesCount = 0 ; -+ -+ member->outDTMFFrames = NULL ; -+ member->outDTMFFramesTail = NULL ; -+ member->outDTMFFramesCount = 0 ; -+ -+ member->outTextFrames = NULL ; -+ member->outTextFramesTail = NULL ; -+ member->outTextFramesCount = 0 ; -+ -+ // ( not currently used ) -+ // member->samplesperframe = AST_CONF_BLOCK_SAMPLES ; -+ -+ // used for determining need to mix frames -+ // and for management interface notification -+ // and for VAD based video switching -+ member->speaking_state_notify = 0 ; -+ member->speaking_state = 0 ; -+ member->local_speaking_state = 0; -+ member->speaker_count = 0; -+ member->driven_member = NULL; -+ -+ // linked-list pointer -+ member->next = NULL ; -+ -+ // account data -+ member->frames_in = 0 ; -+ member->frames_in_dropped = 0 ; -+ member->frames_out = 0 ; -+ member->frames_out_dropped = 0 ; -+ member->video_frames_in = 0 ; -+ member->video_frames_in_dropped = 0 ; -+ member->video_frames_out = 0 ; -+ member->video_frames_out_dropped = 0 ; -+ member->dtmf_frames_in = 0 ; -+ member->dtmf_frames_in_dropped = 0 ; -+ member->dtmf_frames_out = 0 ; -+ member->dtmf_frames_out_dropped = 0 ; -+ member->text_frames_in = 0 ; -+ member->text_frames_in_dropped = 0 ; -+ member->text_frames_out = 0 ; -+ member->text_frames_out_dropped = 0 ; -+ -+ // for counting sequentially dropped frames -+ member->sequential_drops = 0 ; -+ member->since_dropped = 0 ; -+ -+ // flags -+ member->remove_flag = 0 ; -+ member->kick_flag = 0; -+ -+ // record start time -+ // init dropped frame timestamps -+ // init state change timestamp -+ member->time_entered = -+ member->last_in_dropped = -+ member->last_out_dropped = -+ member->last_state_change = ast_tvnow(); -+ -+ // -+ // parse passed flags -+ // -+ -+ // silence detection flags w/ defaults -+ member->vad_flag = 0 ; -+ member->denoise_flag = 0 ; -+ member->agc_flag = 0 ; -+ -+ // is this member using the telephone? -+ member->via_telephone = 0 ; -+ -+ // temp pointer to flags string -+ char* flags = member->flags ; -+ -+ int i; -+ -+ for ( i = 0 ; i < strlen( flags ) ; ++i ) -+ { -+ -+ if (flags[i] >= (int)'0' && flags[i] <= (int)'9') -+ { -+ if (member->req_id < 0) -+ { -+ member->req_id = flags[i] - (int)'0'; -+ } -+ else -+ { -+ int newid = flags[i] - (int)'0'; -+ // need to boot anyone with this id already -+ // will happen in add_member -+ member->id = newid; -+ } -+ } -+ else -+ { -+ // allowed flags are C, c, L, l, V, D, A, C, X, R, T, t, M, S -+ // mute/no_recv options -+ switch ( flags[i] ) -+ { -+ case 'C': -+ member->mute_video = 1; -+ break ; -+ case 'c': -+ member->norecv_video = 1; -+ break ; -+ case 'L': -+ member->mute_audio = 1; -+ break ; -+ case 'l': -+ member->norecv_audio = 1; -+ break; -+ -+ // speex preprocessing options -+ case 'V': -+ member->vad_flag = 1 ; -+ break ; -+ case 'D': -+ member->denoise_flag = 1 ; -+ break ; -+ case 'A': -+ member->agc_flag = 1 ; -+ break ; -+ -+ // dtmf/moderator/video switching options -+ case 'X': -+ member->dtmf_switch = 1; -+ break; -+ case 'R': -+ member->dtmf_relay = 1; -+ break; -+ case 'S': -+ member->vad_switch = 1; -+ break; -+ case 'M': -+ member->ismoderator = 1; -+ break; -+ case 'N': -+ member->no_camera = 1; -+ break; -+ case 't': -+ member->does_text = 1; -+ break; -+ -+ //Telephone connection -+ case 'T': -+ member->via_telephone = 1; -+ break; -+ -+ default: -+ ast_log( LOG_WARNING, "received invalid flag, chan => %s, flag => %c\n", -+ chan->name, flags[i] ); -+ break ; -+ } -+ } -+ } -+ -+ // set the dsp to null so silence detection is disabled by default -+ member->dsp = NULL ; -+ -+#if ( SILDET == 2 ) -+ // -+ // configure silence detection and preprocessing -+ // if the user is coming in via the telephone, -+ // and is not listen-only -+ // -+ if ( -+ member->via_telephone == 1 -+ && member->type != 'L' -+ ) -+ { -+ // create a speex preprocessor -+ member->dsp = speex_preprocess_state_init( AST_CONF_BLOCK_SAMPLES, AST_CONF_SAMPLE_RATE ) ; -+ -+ if ( member->dsp == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to initialize member dsp, channel => %s\n", chan->name ) ; -+ } -+ else -+ { -+ ast_log( LOG_NOTICE, "member dsp initialized, channel => %s, v => %d, d => %d, a => %d\n", -+ chan->name, member->vad_flag, member->denoise_flag, member->agc_flag ) ; -+ -+ // set speex preprocessor options -+ speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_VAD, &(member->vad_flag) ) ; -+ speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_DENOISE, &(member->denoise_flag) ) ; -+ speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_AGC, &(member->agc_flag) ) ; -+ -+ speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_PROB_START, &member->vad_prob_start ) ; -+ speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_PROB_CONTINUE, &member->vad_prob_continue ) ; -+ -+ speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_GET_PROB_START, &member->vad_prob_start ) ; -+ speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_GET_PROB_CONTINUE, &member->vad_prob_continue ) ; -+ -+ ast_log( AST_CONF_DEBUG, "speech_prob_start => %f, speech_prob_continue => %f\n", -+ member->vad_prob_start, member->vad_prob_continue ) ; -+ } -+ } -+#endif -+ -+ // -+ // set connection type -+ // -+ -+ if ( member->via_telephone == 1 ) -+ { -+ member->connection_type = 'T' ; -+ } -+ else if ( strncmp( member->channel_name, "SIP", 3 ) == 0 ) -+ { -+ member->connection_type = 'S' ; -+ } -+ else // default to iax -+ { -+ member->connection_type = 'X' ; -+ } -+ -+ // -+ // read, write, and translation options -+ // -+ -+ // set member's audio formats, taking dsp preprocessing into account -+ // ( chan->nativeformats, AST_FORMAT_SLINEAR, AST_FORMAT_ULAW, AST_FORMAT_GSM ) -+ member->read_format = ( member->dsp == NULL ) ? chan->nativeformats : AST_FORMAT_SLINEAR ; -+ -+ member->write_format = chan->nativeformats; -+ -+ // 1.2 or 1.3+ -+#ifdef AST_FORMAT_AUDIO_MASK -+ -+ member->read_format &= AST_FORMAT_AUDIO_MASK; -+ member->write_format &= AST_FORMAT_AUDIO_MASK; -+#endif -+ -+ // translation paths ( ast_translator_build_path() returns null if formats match ) -+ member->to_slinear = ast_translator_build_path( AST_FORMAT_SLINEAR, member->read_format ) ; -+ member->from_slinear = ast_translator_build_path( member->write_format, AST_FORMAT_SLINEAR ) ; -+ -+ ast_log( AST_CONF_DEBUG, "AST_FORMAT_SLINEAR => %d\n", AST_FORMAT_SLINEAR ) ; -+ -+ // index for converted_frames array -+ switch ( member->write_format ) -+ { -+ case AST_FORMAT_SLINEAR: -+ member->write_format_index = AC_SLINEAR_INDEX ; -+ break ; -+ -+ case AST_FORMAT_ULAW: -+ member->write_format_index = AC_ULAW_INDEX ; -+ break ; -+ -+ case AST_FORMAT_ALAW: -+ member->write_format_index = AC_ALAW_INDEX ; -+ break ; -+ -+ case AST_FORMAT_GSM: -+ member->write_format_index = AC_GSM_INDEX ; -+ break ; -+ -+ case AST_FORMAT_SPEEX: -+ member->write_format_index = AC_SPEEX_INDEX; -+ break; -+ -+#ifdef AC_USE_G729A -+ case AST_FORMAT_G729A: -+ member->write_format_index = AC_G729A_INDEX; -+ break; -+#endif -+ -+ default: -+ member->write_format_index = 0 ; -+ } -+ -+ // index for converted_frames array -+ switch ( member->read_format ) -+ { -+ case AST_FORMAT_SLINEAR: -+ member->read_format_index = AC_SLINEAR_INDEX ; -+ break ; -+ -+ case AST_FORMAT_ULAW: -+ member->read_format_index = AC_ULAW_INDEX ; -+ break ; -+ -+ case AST_FORMAT_ALAW: -+ member->read_format_index = AC_ALAW_INDEX ; -+ break ; -+ -+ case AST_FORMAT_GSM: -+ member->read_format_index = AC_GSM_INDEX ; -+ break ; -+ -+ case AST_FORMAT_SPEEX: -+ member->read_format_index = AC_SPEEX_INDEX; -+ break; -+ -+#ifdef AC_USE_G729A -+ case AST_FORMAT_G729A: -+ member->read_format_index = AC_G729A_INDEX; -+ break; -+#endif -+ -+ default: -+ member->read_format_index = 0 ; -+ } -+ -+ // smoother defaults. -+ member->smooth_multiple =1; -+ member->smooth_size_in = -1; -+ member->smooth_size_out = -1; -+ member->inSmoother= NULL; -+ member->outPacker= NULL; -+ -+ switch (member->read_format){ -+ /* these assumptions may be incorrect */ -+ case AST_FORMAT_ULAW: -+ case AST_FORMAT_ALAW: -+ member->smooth_size_in = 160; //bytes -+ member->smooth_size_out = 160; //samples -+ break; -+ case AST_FORMAT_GSM: -+ /* -+ member->smooth_size_in = 33; //bytes -+ member->smooth_size_out = 160;//samples -+ */ -+ break; -+ case AST_FORMAT_SPEEX: -+ case AST_FORMAT_G729A: -+ /* this assumptions are wrong -+ member->smooth_multiple = 2 ; // for testing, force to dual frame -+ member->smooth_size_in = 39; // bytes -+ member->smooth_size_out = 160; // samples -+ */ -+ break; -+ case AST_FORMAT_SLINEAR: -+ member->smooth_size_in = 320; //bytes -+ member->smooth_size_out = 160; //samples -+ break; -+ default: -+ member->inSmoother = NULL; //don't use smoother for this type. -+ //ast_log( AST_CONF_DEBUG, "smoother is NULL for member->read_format => %d\n", member->read_format); -+ } -+ -+ if (member->smooth_size_in > 0){ -+ member->inSmoother = ast_smoother_new(member->smooth_size_in); -+ ast_log( AST_CONF_DEBUG, "created smoother(%d) for %d\n", member->smooth_size_in , member->read_format); -+ } -+ -+ // -+ // finish up -+ // -+ -+ ast_log( AST_CONF_DEBUG, "created member, type => %c, priority => %d, readformat => %d\n", -+ member->type, member->priority, chan->readformat ) ; -+ -+ return member ; -+} -+ -+struct ast_conf_member* delete_member( struct ast_conf_member* member ) -+{ -+ // !!! NO RETURN TEST !!! -+ // do { sleep(1) ; } while (1) ; -+ -+ // !!! CRASH TEST !!! -+ // *((int *)0) = 0; -+ -+ if ( member == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to the delete null member\n" ) ; -+ return NULL ; -+ } -+ -+ ast_mutex_lock(&member->lock); -+ -+ // If member is driving another member, make sure its speaker count is correct -+ if ( member->driven_member != NULL && member->speaking_state == 1 ) -+ decrement_speaker_count(member->driven_member, 1); -+ -+ // -+ // clean up member flags -+ // -+ -+ if ( member->flags != NULL ) -+ { -+ // !!! DEBUGING !!! -+ ast_log( AST_CONF_DEBUG, "freeing member flags, name => %s\n", -+ member->channel_name ) ; -+ free( member->flags ) ; -+ } -+ -+ // -+ // delete the members frames -+ // -+ -+ conf_frame* cf ; -+ -+ // !!! DEBUGING !!! -+ ast_log( AST_CONF_DEBUG, "deleting member input frames, name => %s\n", -+ member->channel_name ) ; -+ -+ // incoming frames -+ cf = member->inFrames ; -+ -+ while ( cf != NULL ) -+ { -+ cf = delete_conf_frame( cf ) ; -+ } -+ -+ if (member->inSmoother != NULL) -+ ast_smoother_free(member->inSmoother); -+ -+ cf = member->inVideoFrames ; -+ -+ while ( cf != NULL ) -+ { -+ cf = delete_conf_frame( cf ) ; -+ } -+ -+ // !!! DEBUGING !!! -+ ast_log( AST_CONF_DEBUG, "deleting member output frames, name => %s\n", -+ member->channel_name ) ; -+ -+ // outgoing frames -+ cf = member->outFrames ; -+ -+ while ( cf != NULL ) -+ { -+ cf = delete_conf_frame( cf ) ; -+ } -+ -+ cf = member->outVideoFrames ; -+ -+ while ( cf != NULL ) -+ { -+ cf = delete_conf_frame( cf ) ; -+ } -+ -+#if ( SILDET == 2 ) -+ if ( member->dsp != NULL ) -+ { -+ // !!! DEBUGING !!! -+ ast_log( AST_CONF_DEBUG, "destroying member preprocessor, name => %s\n", -+ member->channel_name ) ; -+ speex_preprocess_state_destroy( member->dsp ) ; -+ } -+#endif -+ -+ // !!! DEBUGING !!! -+ ast_log( AST_CONF_DEBUG, "freeing member translator paths, name => %s\n", -+ member->channel_name ) ; -+ -+ // free the mixing translators -+ ast_translator_free_path( member->to_slinear ) ; -+ ast_translator_free_path( member->from_slinear ) ; -+ -+ // get a pointer to the next -+ // member so we can return it -+ struct ast_conf_member* nm = member->next ; -+ -+ ast_mutex_unlock(&member->lock); -+ -+ // !!! DEBUGING !!! -+ ast_log( AST_CONF_DEBUG, "freeing member channel name, name => %s\n", -+ member->channel_name ) ; -+ -+ // free the member's copy for the channel name -+ free( member->channel_name ) ; -+ -+ // free the member's copy of the conference name -+ free(member->conf_name); -+ -+ // !!! DEBUGING !!! -+ ast_log( AST_CONF_DEBUG, "freeing member\n" ) ; -+ -+ // free the member's memory -+ free(member->callerid); -+ free(member->callername); -+ -+ // free enter/leave sounds -+ free(member->enter_snd); -+ free(member->leave_snd); -+ -+ free( member ) ; -+ member = NULL ; -+ -+ return nm ; -+} -+ -+// -+// incoming frame functions -+// -+ -+conf_frame* get_incoming_video_frame( struct ast_conf_member *member ) -+{ -+ if ( member == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ; -+ return NULL ; -+ } -+ -+ ast_mutex_lock(&member->lock); -+ -+ if ( member->inVideoFramesCount == 0 ) -+ { -+ ast_mutex_unlock(&member->lock); -+ return NULL ; -+ } -+ -+ // -+ // return the next frame in the queue -+ // -+ -+ conf_frame* cfr = NULL ; -+ -+ // get first frame in line -+ cfr = member->inVideoFramesTail ; -+ -+ // if it's the only frame, reset the queue, -+ // else, move the second frame to the front -+ if ( member->inVideoFramesTail == member->inVideoFrames ) -+ { -+ member->inVideoFramesTail = NULL ; -+ member->inVideoFrames = NULL ; -+ } -+ else -+ { -+ // move the pointer to the next frame -+ member->inVideoFramesTail = member->inVideoFramesTail->prev ; -+ -+ // reset it's 'next' pointer -+ if ( member->inVideoFramesTail != NULL ) -+ member->inVideoFramesTail->next = NULL ; -+ } -+ -+ // separate the conf frame from the list -+ cfr->next = NULL ; -+ cfr->prev = NULL ; -+ -+ // decrement frame count -+ member->inVideoFramesCount-- ; -+ -+ ast_mutex_unlock(&member->lock); -+ return cfr ; -+ -+} -+conf_frame* get_incoming_dtmf_frame( struct ast_conf_member *member ) -+{ -+ if ( member == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ; -+ return NULL ; -+ } -+ -+ ast_mutex_lock(&member->lock); -+ -+ if ( member->inDTMFFramesCount == 0 ) -+ { -+ ast_mutex_unlock(&member->lock); -+ return NULL ; -+ } -+ -+ // -+ // return the next frame in the queue -+ // -+ -+ conf_frame* cfr = NULL ; -+ -+ // get first frame in line -+ cfr = member->inDTMFFramesTail ; -+ -+ // if it's the only frame, reset the queue, -+ // else, move the second frame to the front -+ if ( member->inDTMFFramesTail == member->inDTMFFrames ) -+ { -+ member->inDTMFFramesTail = NULL ; -+ member->inDTMFFrames = NULL ; -+ } -+ else -+ { -+ // move the pointer to the next frame -+ member->inDTMFFramesTail = member->inDTMFFramesTail->prev ; -+ -+ // reset it's 'next' pointer -+ if ( member->inDTMFFramesTail != NULL ) -+ member->inDTMFFramesTail->next = NULL ; -+ } -+ -+ // separate the conf frame from the list -+ cfr->next = NULL ; -+ cfr->prev = NULL ; -+ -+ // decriment frame count -+ member->inDTMFFramesCount-- ; -+ -+ ast_mutex_unlock(&member->lock); -+ return cfr ; -+ -+} -+ -+ -+conf_frame* get_incoming_frame( struct ast_conf_member *member ) -+{ -+ conf_frame *cf_result; -+ // -+ // sanity checks -+ // -+ -+ if ( member == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ; -+ return NULL ; -+ } -+ -+ ast_mutex_lock(&member->lock); -+ -+ // -+ // repeat last frame a couple times to smooth transition -+ // -+ -+#ifdef AST_CONF_CACHE_LAST_FRAME -+ if ( member->inFramesCount == 0 ) -+ { -+ // nothing to do if there's no cached frame -+ if ( member->inFramesLast == NULL ) { -+ ast_mutex_unlock(&member->lock); -+ return NULL ; -+ } -+ -+ // turn off 'okay to cache' flag -+ member->okayToCacheLast = 0 ; -+ -+ if ( member->inFramesRepeatLast >= AST_CONF_CACHE_LAST_FRAME ) -+ { -+ // already used this frame AST_CONF_CACHE_LAST_FRAME times -+ -+ // reset repeat count -+ member->inFramesRepeatLast = 0 ; -+ -+ // clear the cached frame -+ delete_conf_frame( member->inFramesLast ) ; -+ member->inFramesLast = NULL ; -+ -+ // return null -+ ast_mutex_unlock(&member->lock); -+ return NULL ; -+ } -+ else -+ { -+ ast_log( AST_CONF_DEBUG, "repeating cached frame, channel => %s, inFramesRepeatLast => %d\n", -+ member->channel_name, member->inFramesRepeatLast ) ; -+ -+ // increment counter -+ member->inFramesRepeatLast++ ; -+ -+ // return a copy of the cached frame -+ cf_result = copy_conf_frame( member->inFramesLast ) ; -+ ast_mutex_unlock(&member->lock); -+ return cf_result; -+ } -+ } -+ else if ( member->okayToCacheLast == 0 && member->inFramesCount >= 3 ) -+ { -+ ast_log( AST_CONF_DEBUG, "enabling cached frame, channel => %s, incoming => %d, outgoing => %d\n", -+ member->channel_name, member->inFramesCount, member->outFramesCount ) ; -+ -+ // turn on 'okay to cache' flag -+ member->okayToCacheLast = 1 ; -+ } -+#else -+ if ( member->inFramesCount == 0 ) { -+ ast_mutex_unlock(&member->lock); -+ return NULL ; -+ } -+#endif // AST_CONF_CACHE_LAST_FRAME -+ -+ // -+ // return the next frame in the queue -+ // -+ -+ conf_frame* cfr = NULL ; -+ -+ // get first frame in line -+ cfr = member->inFramesTail ; -+ -+ // if it's the only frame, reset the queue, -+ // else, move the second frame to the front -+ if ( member->inFramesTail == member->inFrames ) -+ { -+ member->inFramesTail = NULL ; -+ member->inFrames = NULL ; -+ } -+ else -+ { -+ // move the pointer to the next frame -+ member->inFramesTail = member->inFramesTail->prev ; -+ -+ // reset it's 'next' pointer -+ if ( member->inFramesTail != NULL ) -+ member->inFramesTail->next = NULL ; -+ } -+ -+ // separate the conf frame from the list -+ cfr->next = NULL ; -+ cfr->prev = NULL ; -+ -+ // decriment frame count -+ member->inFramesCount-- ; -+ -+#ifdef AST_CONF_CACHE_LAST_FRAME -+ // copy frame if queue is now empty -+ if ( -+ member->inFramesCount == 0 -+ && member->okayToCacheLast == 1 -+ ) -+ { -+ // reset repeat count -+ member->inFramesRepeatLast = 0 ; -+ -+ // clear cached frame -+ if ( member->inFramesLast != NULL ) -+ { -+ delete_conf_frame( member->inFramesLast ) ; -+ member->inFramesLast = NULL ; -+ } -+ -+ // cache new frame -+ member->inFramesLast = copy_conf_frame( cfr ) ; -+ } -+#endif // AST_CONF_CACHE_LAST_FRAME -+ -+ ast_mutex_unlock(&member->lock); -+ return cfr ; -+} -+ -+int queue_incoming_video_frame( struct ast_conf_member* member, const struct ast_frame* fr ) -+{ -+ // check on frame -+ if ( fr == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to queue null frame\n" ) ; -+ return -1 ; -+ } -+ -+ // check on member -+ if ( member == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ; -+ return -1 ; -+ } -+ -+ // lock the member -+ ast_mutex_lock(&member->lock); -+ -+ if (!member->first_frame_received) -+ { -+ // nat=yes will be correct now -+ member->first_frame_received = 1; -+ member->conference = 1; -+ } -+ -+ // We have to drop if the queue is full! -+ if ( member->inVideoFramesCount >= AST_CONF_MAX_VIDEO_QUEUE ) -+ { -+ ast_log( -+ AST_CONF_DEBUG, -+ "unable to queue incoming VIDEO frame, channel => %s, incoming => %d, outgoing => %d\n", -+ member->channel_name, member->inVideoFramesCount, member->outVideoFramesCount -+ ) ; -+ ast_mutex_unlock(&member->lock); -+ return -1 ; -+ } -+ -+ // -+ // create new conf frame from passed data frame -+ // -+ -+ // ( member->inFrames may be null at this point ) -+ conf_frame* cfr = create_conf_frame( member, member->inVideoFrames, fr ) ; -+ -+ if ( cfr == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to malloc conf_frame\n" ) ; -+ ast_mutex_unlock(&member->lock); -+ return -1 ; -+ } -+ -+ // copy frame data pointer to conf frame -+ // cfr->fr = fr ; -+ -+ // -+ // add new frame to speaking members incoming frame queue -+ // ( i.e. save this frame data, so we can distribute it in conference_exec later ) -+ // -+ -+ if ( member->inVideoFrames == NULL ) -+ { -+ // this is the first frame in the buffer -+ member->inVideoFramesTail = cfr ; -+ member->inVideoFrames = cfr ; -+ } -+ else -+ { -+ // put the new frame at the head of the list -+ member->inVideoFrames = cfr ; -+ } -+ -+ // increment member frame count -+ member->inVideoFramesCount++ ; -+ -+ ast_mutex_unlock(&member->lock); -+ -+ // Everything has gone okay! -+ return 0; -+} -+ -+int queue_incoming_dtmf_frame( struct ast_conf_member* member, const struct ast_frame* fr ) -+{ -+ //ast_log( AST_CONF_DEBUG, "queue incoming video frame\n"); -+ -+ // check on frame -+ if ( fr == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to queue null frame\n" ) ; -+ return -1 ; -+ } -+ -+ // check on member -+ if ( member == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ; -+ return -1 ; -+ } -+ -+ ast_mutex_lock(&member->lock); -+ -+ // We have to drop if the queue is full! -+ if ( member->inDTMFFramesCount >= AST_CONF_MAX_DTMF_QUEUE ) -+ { -+ ast_log( -+ AST_CONF_DEBUG, -+ "unable to queue incoming DTMF frame, channel => %s, incoming => %d, outgoing => %d\n", -+ member->channel_name, member->inDTMFFramesCount, member->outDTMFFramesCount -+ ) ; -+ ast_mutex_unlock(&member->lock); -+ return -1 ; -+ } -+ -+ // -+ // create new conf frame from passed data frame -+ // -+ -+ // ( member->inFrames may be null at this point ) -+ conf_frame* cfr = create_conf_frame( member, member->inDTMFFrames, fr ) ; -+ -+ if ( cfr == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to malloc conf_frame\n" ) ; -+ ast_mutex_unlock(&member->lock); -+ return -1 ; -+ } -+ -+ // copy frame data pointer to conf frame -+ // cfr->fr = fr ; -+ -+ // -+ // add new frame to speaking members incoming frame queue -+ // ( i.e. save this frame data, so we can distribute it in conference_exec later ) -+ // -+ -+ if ( member->inDTMFFrames == NULL ) -+ { -+ // this is the first frame in the buffer -+ member->inDTMFFramesTail = cfr ; -+ member->inDTMFFrames = cfr ; -+ } -+ else -+ { -+ // put the new frame at the head of the list -+ member->inDTMFFrames = cfr ; -+ } -+ -+ // increment member frame count -+ member->inDTMFFramesCount++ ; -+ -+ ast_mutex_unlock(&member->lock); -+ -+ // Everything has gone okay! -+ return 0; -+} -+ -+int queue_incoming_frame( struct ast_conf_member* member, struct ast_frame* fr ) -+{ -+ // -+ // sanity checks -+ // -+ -+ // check on frame -+ if ( fr == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to queue null frame\n" ) ; -+ return -1 ; -+ } -+ -+ // check on member -+ if ( member == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ; -+ return -1 ; -+ } -+ -+ ast_mutex_lock(&member->lock); -+ -+ if ( member->inFramesCount > member->inFramesNeeded ) -+ { -+ if ( member->inFramesCount > AST_CONF_QUEUE_DROP_THRESHOLD ) -+ { -+ struct timeval curr = ast_tvnow(); -+ -+ // time since last dropped frame -+ long diff = ast_tvdiff_ms(curr, member->last_in_dropped); -+ -+ // number of milliseconds which must pass between frame drops -+ // ( 15 frames => -100ms, 10 frames => 400ms, 5 frames => 900ms, 0 frames => 1400ms, etc. ) -+ long time_limit = 1000 - ( ( member->inFramesCount - AST_CONF_QUEUE_DROP_THRESHOLD ) * 100 ) ; -+ -+ if ( diff >= time_limit ) -+ { -+ // count sequential drops -+ member->sequential_drops++ ; -+ -+ ast_log( -+ AST_CONF_DEBUG, -+ "dropping frame from input buffer, channel => %s, incoming => %d, outgoing => %d\n", -+ member->channel_name, member->inFramesCount, member->outFramesCount -+ ) ; -+ -+ // accounting: count dropped incoming frames -+ member->frames_in_dropped++ ; -+ -+ // reset frames since dropped -+ member->since_dropped = 0 ; -+ -+ // delete the frame -+ delete_conf_frame( get_incoming_frame( member ) ) ; -+ -+ member->last_in_dropped = ast_tvnow(); -+ } -+ else -+ { -+/* -+ ast_log( -+ AST_CONF_DEBUG, -+ "input buffer larger than drop threshold, channel => %s, incoming => %d, outgoing => %d\n", -+ member->channel_name, member->inFramesCount, member->outFramesCount -+ ) ; -+*/ -+ } -+ } -+ } -+ -+ // -+ // if we have to drop frames, we'll drop new frames -+ // because it's easier ( and doesn't matter much anyway ). -+ // -+ -+ if ( member->inFramesCount >= AST_CONF_MAX_QUEUE ) -+ { -+ // count sequential drops -+ member->sequential_drops++ ; -+ -+ ast_log( -+ AST_CONF_DEBUG, -+ "unable to queue incoming frame, channel => %s, incoming => %d, outgoing => %d\n", -+ member->channel_name, member->inFramesCount, member->outFramesCount -+ ) ; -+ -+ // accounting: count dropped incoming frames -+ member->frames_in_dropped++ ; -+ -+ // reset frames since dropped -+ member->since_dropped = 0 ; -+ -+ ast_mutex_unlock(&member->lock); -+ return -1 ; -+ } -+ -+ // reset sequential drops -+ member->sequential_drops = 0 ; -+ -+ // increment frames since dropped -+ member->since_dropped++ ; -+ -+ // -+ // create new conf frame from passed data frame -+ // -+ -+ // ( member->inFrames may be null at this point ) -+ if (member->inSmoother == NULL ){ -+ conf_frame* cfr = create_conf_frame( member, member->inFrames, fr ) ; -+ if ( cfr == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to malloc conf_frame\n" ) ; -+ ast_mutex_unlock(&member->lock); -+ return -1 ; -+ } -+ -+ // -+ // add new frame to speaking members incoming frame queue -+ // ( i.e. save this frame data, so we can distribute it in conference_exec later ) -+ // -+ -+ if ( member->inFrames == NULL ) { -+ member->inFramesTail = cfr ; -+ } -+ member->inFrames = cfr ; -+ member->inFramesCount++ ; -+ } else { -+ //feed frame(fr) into the smoother -+ -+ // smoother tmp frame -+ struct ast_frame *sfr; -+ int multiple = 1; -+ int i=0; -+ -+#if 0 -+ if ( (member->smooth_size_in > 0 ) && (member->smooth_size_in * member->smooth_multiple != fr->datalen) ) -+ { -+ ast_log( AST_CONF_DEBUG, "resetting smooth_size_in. old size=> %d, multiple =>%d, datalen=> %d\n", member->smooth_size_in, member->smooth_multiple, fr->datalen ); -+ if ( fr->datalen % member->smooth_multiple != 0) { -+ // if datalen not divisible by smooth_multiple, assume we're just getting normal encoding. -+ // ast_log(AST_CONF_DEBUG,"smooth_multiple does not divide datalen. changing smooth size from %d to %d, multiple => 1\n", member->smooth_size_in, fr->datalen); -+ member->smooth_size_in = fr->datalen; -+ member->smooth_multiple = 1; -+ } else { -+ // assume a fixed multiple, so divide into datalen. -+ int newsmooth = fr->datalen / member->smooth_multiple ; -+ // ast_log(AST_CONF_DEBUG,"datalen is divisible by smooth_multiple, changing smooth size from %d to %d\n", member->smooth_size_in, newsmooth); -+ member->smooth_size_in = newsmooth; -+ } -+ -+ //free input smoother. -+ if (member->inSmoother != NULL) -+ ast_smoother_free(member->inSmoother); -+ -+ //make new input smoother. -+ member->inSmoother = ast_smoother_new(member->smooth_size_in); -+ } -+#endif -+ -+ ast_smoother_feed( member->inSmoother, fr ); -+ast_log (AST_CONF_DEBUG, "SMOOTH:Feeding frame into inSmoother, timestamp => %ld.%ld\n", fr->delivery.tv_sec, fr->delivery.tv_usec); -+ -+ if ( multiple > 1 ) -+ fr->samples /= multiple; -+ -+ // read smoothed version of frames, add to queue -+ while( ( sfr = ast_smoother_read( member->inSmoother ) ) ){ -+ -+ ++i; -+ast_log( AST_CONF_DEBUG , "\treading new frame [%d] from smoother, inFramesCount[%d], \n\tsfr->frametype -> %d , sfr->subclass -> %d , sfr->datalen => %d sfr->samples => %d\n", i , member->inFramesCount , sfr->frametype, sfr->subclass, sfr->datalen, sfr->samples); -+ast_log (AST_CONF_DEBUG, "SMOOTH:Reading frame from inSmoother, i=>%d, timestamp => %ld.%ld\n",i, sfr->delivery.tv_sec, sfr->delivery.tv_usec); -+ conf_frame* cfr = create_conf_frame( member, member->inFrames, sfr ) ; -+ if ( cfr == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to malloc conf_frame\n" ) ; -+ ast_mutex_unlock(&member->lock); -+ return -1 ; -+ } -+ -+ // -+ // add new frame to speaking members incoming frame queue -+ // ( i.e. save this frame data, so we can distribute it in conference_exec later ) -+ // -+ -+ if ( member->inFrames == NULL ) { -+ member->inFramesTail = cfr ; -+ } -+ member->inFrames = cfr ; -+ member->inFramesCount++ ; -+ } -+ } -+ ast_mutex_unlock(&member->lock); -+ return 0 ; -+} -+ -+// -+// outgoing frame functions -+// -+ -+conf_frame* get_outgoing_frame( struct ast_conf_member *member ) -+{ -+ if ( member == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ; -+ return NULL ; -+ } -+ -+ conf_frame* cfr ; -+ -+ // ast_log( AST_CONF_DEBUG, "getting member frames, count => %d\n", member->outFramesCount ) ; -+ -+ ast_mutex_lock(&member->lock); -+ -+ if ( member->outFramesCount > AST_CONF_MIN_QUEUE ) -+ { -+ cfr = member->outFramesTail ; -+ -+ // if it's the only frame, reset the queu, -+ // else, move the second frame to the front -+ if ( member->outFramesTail == member->outFrames ) -+ { -+ member->outFrames = NULL ; -+ member->outFramesTail = NULL ; -+ } -+ else -+ { -+ // move the pointer to the next frame -+ member->outFramesTail = member->outFramesTail->prev ; -+ -+ // reset it's 'next' pointer -+ if ( member->outFramesTail != NULL ) -+ member->outFramesTail->next = NULL ; -+ } -+ -+ // separate the conf frame from the list -+ cfr->next = NULL ; -+ cfr->prev = NULL ; -+ -+ // decriment frame count -+ member->outFramesCount-- ; -+ ast_mutex_unlock(&member->lock); -+ return cfr ; -+ } -+ ast_mutex_unlock(&member->lock); -+ return NULL ; -+} -+ -+int __queue_outgoing_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery ) -+{ -+ // accounting: count the number of outgoing frames for this member -+ member->frames_out++ ; -+ -+ // -+ // we have to drop frames, so we'll drop new frames -+ // because it's easier ( and doesn't matter much anyway ). -+ // -+ if ( member->outFramesCount >= AST_CONF_MAX_QUEUE ) -+ { -+ ast_log( -+ AST_CONF_DEBUG, -+ "unable to queue outgoing frame, channel => %s, incoming => %d, outgoing => %d\n", -+ member->channel_name, member->inFramesCount, member->outFramesCount -+ ) ; -+ -+ // accounting: count dropped outgoing frames -+ member->frames_out_dropped++ ; -+ return -1 ; -+ } -+ -+ // -+ // create new conf frame from passed data frame -+ // -+ -+ conf_frame* cfr = create_conf_frame( member, member->outFrames, fr ) ; -+ -+ if ( cfr == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to create new conf frame\n" ) ; -+ -+ // accounting: count dropped outgoing frames -+ member->frames_out_dropped++ ; -+ return -1 ; -+ } -+ -+ // set delivery timestamp -+ cfr->fr->delivery = delivery ; -+ -+ // -+ // add new frame to speaking members incoming frame queue -+ // ( i.e. save this frame data, so we can distribute it in conference_exec later ) -+ // -+ -+ if ( member->outFrames == NULL ) { -+ member->outFramesTail = cfr ; -+ } -+ member->outFrames = cfr ; -+ member->outFramesCount++ ; -+ -+ // return success -+ return 0 ; -+} -+ -+int queue_outgoing_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery ) -+{ -+ // check on frame -+ if ( fr == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to queue null frame\n" ) ; -+ return -1 ; -+ } -+ -+ // check on member -+ if ( member == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ; -+ return -1 ; -+ } -+ -+ if ( ( member->outPacker == NULL ) && ( member->smooth_multiple > 1 ) && ( member->smooth_size_out > 0 ) ){ -+ //ast_log (AST_CONF_DEBUG, "creating outPacker with size => %d \n\t( multiple => %d ) * ( size => %d )\n", member->smooth_multiple * member-> smooth_size_out, member->smooth_multiple , member->smooth_size_out); -+ member->outPacker = ast_packer_new( member->smooth_multiple * member->smooth_size_out); -+ } -+ -+ if (member->outPacker == NULL ){ -+ return __queue_outgoing_frame( member, fr, delivery ) ; -+ } -+ else -+ { -+ struct ast_frame *sfr; -+ int exitval = 0; -+//ast_log (AST_CONF_DEBUG, "sending fr into outPacker, datalen=>%d, samples=>%d\n",fr->datalen, fr->samples); -+ ast_packer_feed( member->outPacker , fr ); -+ while( (sfr = ast_packer_read( member->outPacker ) ) ) -+ { -+//ast_log (AST_CONF_DEBUG, "read sfr from outPacker, datalen=>%d, samples=>%d\n",sfr->datalen, sfr->samples); -+ if ( __queue_outgoing_frame( member, sfr, delivery ) == -1 ) { -+ exitval = -1; -+ } -+ } -+ -+ return exitval; -+ } -+} -+ -+// -+// outgoing frame functions -+// -+ -+conf_frame* get_outgoing_video_frame( struct ast_conf_member *member ) -+{ -+ if ( member == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ; -+ return NULL ; -+ } -+ -+ conf_frame* cfr ; -+ -+ ast_mutex_lock(&member->lock); -+ -+ // ast_log( AST_CONF_DEBUG, "getting member frames, count => %d\n", member->outFramesCount ) ; -+ -+ if ( member->outVideoFramesCount > AST_CONF_MIN_QUEUE ) -+ { -+ cfr = member->outVideoFramesTail ; -+ -+ // if it's the only frame, reset the queu, -+ // else, move the second frame to the front -+ if ( member->outVideoFramesTail == member->outVideoFrames ) -+ { -+ member->outVideoFrames = NULL ; -+ member->outVideoFramesTail = NULL ; -+ } -+ else -+ { -+ // move the pointer to the next frame -+ member->outVideoFramesTail = member->outVideoFramesTail->prev ; -+ -+ // reset it's 'next' pointer -+ if ( member->outVideoFramesTail != NULL ) -+ member->outVideoFramesTail->next = NULL ; -+ } -+ -+ // separate the conf frame from the list -+ cfr->next = NULL ; -+ cfr->prev = NULL ; -+ -+ // decriment frame count -+ member->outVideoFramesCount-- ; -+ ast_mutex_unlock(&member->lock); -+ return cfr ; -+ } -+ -+ ast_mutex_unlock(&member->lock); -+ return NULL ; -+} -+ -+ -+ -+int queue_outgoing_video_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery ) -+{ -+ // check on frame -+ if ( fr == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to queue null frame\n" ) ; -+ return -1 ; -+ } -+ -+ // check on member -+ if ( member == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ; -+ return -1 ; -+ } -+ -+ ast_mutex_lock(&member->lock); -+ -+ // accounting: count the number of outgoing frames for this member -+ member->video_frames_out++ ; -+ -+ // -+ // we have to drop frames, so we'll drop new frames -+ // because it's easier ( and doesn't matter much anyway ). -+ // -+ if ( member->outVideoFramesCount >= AST_CONF_MAX_VIDEO_QUEUE) -+ { -+ ast_log( -+ AST_CONF_DEBUG, -+ "unable to queue outgoing VIDEO frame, channel => %s, incoming => %d, outgoing => %d\n", -+ member->channel_name, member->inVideoFramesCount, member->outVideoFramesCount -+ ) ; -+ -+ // accounting: count dropped outgoing frames -+ member->video_frames_out_dropped++ ; -+ ast_mutex_unlock(&member->lock); -+ return -1 ; -+ } -+ -+ // -+ // create new conf frame from passed data frame -+ // -+ -+ conf_frame* cfr = create_conf_frame( member, member->outVideoFrames, fr ) ; -+ -+ if ( cfr == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to create new conf frame\n" ) ; -+ -+ // accounting: count dropped outgoing frames -+ member->video_frames_out_dropped++ ; -+ ast_mutex_unlock(&member->lock); -+ return -1 ; -+ } -+ -+ // set delivery timestamp -+#ifdef VIDEO_SETTIMESTAMP -+ cfr->fr->delivery = delivery ; -+#else -+ cfr->fr->delivery.tv_sec = 0; -+ cfr->fr->delivery.tv_usec = 0; -+#endif -+ //ast_log (LOG_WARNING,"%d\n",cfr->fr->seqno); -+ -+#ifdef RTP_SEQNO_ZERO -+ cfr->fr->seqno = 0; -+#endif -+ -+ if ( member->outVideoFrames == NULL ) -+ { -+ // this is the first frame in the buffer -+ member->outVideoFramesTail = cfr ; -+ member->outVideoFrames = cfr ; -+ } -+ else -+ { -+ // put the new frame at the head of the list -+ member->outVideoFrames = cfr ; -+ } -+ -+ // increment member frame count -+ member->outVideoFramesCount++ ; -+ -+ ast_mutex_unlock(&member->lock); -+ -+ // return success -+ return 0 ; -+} -+ -+conf_frame* get_outgoing_dtmf_frame( struct ast_conf_member *member ) -+{ -+ if ( member == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ; -+ return NULL ; -+ } -+ -+ conf_frame* cfr ; -+ -+ // ast_log( AST_CONF_DEBUG, "getting member frames, count => %d\n", member->outFramesCount ) ; -+ -+ ast_mutex_lock(&member->lock); -+ -+ if ( member->outDTMFFramesCount > AST_CONF_MIN_QUEUE ) -+ { -+ cfr = member->outDTMFFramesTail ; -+ -+ // if it's the only frame, reset the queu, -+ // else, move the second frame to the front -+ if ( member->outDTMFFramesTail == member->outDTMFFrames ) -+ { -+ member->outDTMFFrames = NULL ; -+ member->outDTMFFramesTail = NULL ; -+ } -+ else -+ { -+ // move the pointer to the next frame -+ member->outDTMFFramesTail = member->outDTMFFramesTail->prev ; -+ -+ // reset it's 'next' pointer -+ if ( member->outDTMFFramesTail != NULL ) -+ member->outDTMFFramesTail->next = NULL ; -+ } -+ -+ // separate the conf frame from the list -+ cfr->next = NULL ; -+ cfr->prev = NULL ; -+ -+ // decriment frame count -+ member->outDTMFFramesCount-- ; -+ ast_mutex_unlock(&member->lock); -+ return cfr ; -+ } -+ ast_mutex_unlock(&member->lock); -+ return NULL ; -+} -+ -+conf_frame* get_outgoing_text_frame( struct ast_conf_member *member ) -+{ -+ if ( member == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ; -+ return NULL ; -+ } -+ -+ conf_frame* cfr ; -+ -+ // ast_log( AST_CONF_DEBUG, "getting member frames, count => %d\n", member->outFramesCount ) ; -+ -+ ast_mutex_lock(&member->lock); -+ -+ if ( member->outTextFramesCount > AST_CONF_MIN_QUEUE ) -+ { -+ cfr = member->outTextFramesTail ; -+ -+ // if it's the only frame, reset the queu, -+ // else, move the second frame to the front -+ if ( member->outTextFramesTail == member->outTextFrames ) -+ { -+ member->outTextFrames = NULL ; -+ member->outTextFramesTail = NULL ; -+ } -+ else -+ { -+ // move the pointer to the next frame -+ member->outTextFramesTail = member->outTextFramesTail->prev ; -+ -+ // reset it's 'next' pointer -+ if ( member->outTextFramesTail != NULL ) -+ member->outTextFramesTail->next = NULL ; -+ } -+ -+ // separate the conf frame from the list -+ cfr->next = NULL ; -+ cfr->prev = NULL ; -+ -+ // decriment frame count -+ member->outTextFramesCount-- ; -+ ast_mutex_unlock(&member->lock); -+ return cfr ; -+ } -+ ast_mutex_unlock(&member->lock); -+ return NULL ; -+} -+ -+ -+int queue_outgoing_dtmf_frame( struct ast_conf_member* member, const struct ast_frame* fr ) -+{ -+ // check on frame -+ if ( fr == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to queue null frame\n" ) ; -+ return -1 ; -+ } -+ -+ // check on member -+ if ( member == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ; -+ return -1 ; -+ } -+ -+ ast_mutex_lock(&member->lock); -+ -+ // accounting: count the number of outgoing frames for this member -+ member->dtmf_frames_out++ ; -+ -+ // -+ // we have to drop frames, so we'll drop new frames -+ // because it's easier ( and doesn't matter much anyway ). -+ // -+ if ( member->outDTMFFramesCount >= AST_CONF_MAX_DTMF_QUEUE) -+ { -+ ast_log( -+ AST_CONF_DEBUG, -+ "unable to queue outgoing DTMF frame, channel => %s, incoming => %d, outgoing => %d\n", -+ member->channel_name, member->inDTMFFramesCount, member->outDTMFFramesCount -+ ) ; -+ -+ // accounting: count dropped outgoing frames -+ member->dtmf_frames_out_dropped++ ; -+ ast_mutex_unlock(&member->lock); -+ return -1 ; -+ } -+ -+ // -+ // create new conf frame from passed data frame -+ // -+ -+ conf_frame* cfr = create_conf_frame( member, member->outDTMFFrames, fr ) ; -+ -+ if ( cfr == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to create new conf frame\n" ) ; -+ -+ // accounting: count dropped outgoing frames -+ member->dtmf_frames_out_dropped++ ; -+ ast_mutex_unlock(&member->lock); -+ return -1 ; -+ } -+ -+#ifdef RTP_SEQNO_ZERO -+ cfr->fr->seqno = 0; -+#endif -+ -+ if ( member->outDTMFFrames == NULL ) -+ { -+ // this is the first frame in the buffer -+ member->outDTMFFramesTail = cfr ; -+ member->outDTMFFrames = cfr ; -+ } -+ else -+ { -+ // put the new frame at the head of the list -+ member->outDTMFFrames = cfr ; -+ } -+ -+ // increment member frame count -+ member->outDTMFFramesCount++ ; -+ -+ ast_mutex_unlock(&member->lock); -+ // return success -+ return 0 ; -+} -+ -+int queue_outgoing_text_frame( struct ast_conf_member* member, const struct ast_frame* fr) -+{ -+ // check on frame -+ if ( fr == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to queue null frame\n" ) ; -+ return -1 ; -+ } -+ -+ // check on member -+ if ( member == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ; -+ return -1 ; -+ } -+ -+ ast_mutex_lock(&member->lock); -+ -+ // accounting: count the number of outgoing frames for this member -+ member->text_frames_out++ ; -+ -+ // -+ // we have to drop frames, so we'll drop new frames -+ // because it's easier ( and doesn't matter much anyway ). -+ // -+ if ( member->outTextFramesCount >= AST_CONF_MAX_TEXT_QUEUE) -+ { -+ ast_log( -+ AST_CONF_DEBUG, -+ "unable to queue outgoing text frame, channel => %s, incoming => %d, outgoing => %d\n", -+ member->channel_name, member->inTextFramesCount, member->outTextFramesCount -+ ) ; -+ -+ // accounting: count dropped outgoing frames -+ member->text_frames_out_dropped++ ; -+ ast_mutex_unlock(&member->lock); -+ return -1 ; -+ } -+ -+ // -+ // create new conf frame from passed data frame -+ // -+ -+ conf_frame* cfr = create_conf_frame( member, member->outTextFrames, fr ) ; -+ -+ if ( cfr == NULL ) -+ { -+ ast_log( LOG_ERROR, "unable to create new conf frame\n" ) ; -+ -+ // accounting: count dropped outgoing frames -+ member->text_frames_out_dropped++ ; -+ ast_mutex_unlock(&member->lock); -+ return -1 ; -+ } -+ -+#ifdef RTP_SEQNO_ZERO -+ cfr->fr->seqno = 0; -+#endif -+ -+ if ( member->outTextFrames == NULL ) -+ { -+ // this is the first frame in the buffer -+ member->outTextFramesTail = cfr ; -+ member->outTextFrames = cfr ; -+ } -+ else -+ { -+ // put the new frame at the head of the list -+ member->outTextFrames = cfr ; -+ } -+ -+ // increment member frame count -+ member->outTextFramesCount++ ; -+ -+ ast_mutex_unlock(&member->lock); -+ // return success -+ return 0 ; -+} -+ -+ -+// -+// manager functions -+// -+ -+void send_state_change_notifications( struct ast_conf_member* member ) -+{ -+ // ast_log( AST_CONF_DEBUG, "sending state change notification\n" ) ; -+ -+ // loop through list of members, sending state changes -+ while ( member != NULL ) -+ { -+ // has the state changed since last time through this loop? -+ if ( member->speaking_state_notify ) -+ { -+ manager_event( -+ EVENT_FLAG_CALL, -+ "ConferenceState", -+ "Channel: %s\r\n" -+ "State: %s\r\n", -+ member->channel_name, -+ ( ( member->speaking_state == 1 ) ? "speaking" : "silent" ) -+ ) ; -+ -+ ast_log( AST_CONF_DEBUG, "member state changed, channel => %s, state => %d, incoming => %d, outgoing => %d\n", -+ member->channel_name, member->speaking_state, member->inFramesCount, member->outFramesCount ) ; -+ -+ member->speaking_state_notify = 0; -+ } -+ -+ // move the pointer to the next member -+ member = member->next ; -+ } -+ -+ return ; -+} -+ -+// -+// ast_packer, adapted from ast_smoother -+// pack multiple frames together into one packet on the wire. -+// -+ -+#define PACKER_SIZE 8000 -+#define PACKER_QUEUE 10 // store at most 10 complete packets in the queue -+ -+struct ast_packer { -+ int framesize; // number of frames per packet on the wire. -+ int size; -+ int packet_index; -+ int format; -+ int readdata; -+ int optimizablestream; -+ int flags; -+ float samplesperbyte; -+ struct ast_frame f; -+ struct timeval delivery; -+ char data[PACKER_SIZE]; -+ char framedata[PACKER_SIZE + AST_FRIENDLY_OFFSET]; -+ int samples; -+ int sample_queue[PACKER_QUEUE]; -+ int len_queue[PACKER_QUEUE]; -+ struct ast_frame *opt; -+ int len; -+}; -+ -+void ast_packer_reset(struct ast_packer *s, int framesize) -+{ -+ memset(s, 0, sizeof(struct ast_packer)); -+ s->framesize = framesize; -+ s->packet_index=0; -+ s->len=0; -+} -+ -+struct ast_packer *ast_packer_new(int framesize) -+{ -+ struct ast_packer *s; -+ if (framesize < 1) -+ return NULL; -+ s = malloc(sizeof(struct ast_packer)); -+ if (s) -+ ast_packer_reset(s, framesize); -+ return s; -+} -+ -+int ast_packer_get_flags(struct ast_packer *s) -+{ -+ return s->flags; -+} -+ -+void ast_packer_set_flags(struct ast_packer *s, int flags) -+{ -+ s->flags = flags; -+} -+ -+int ast_packer_feed(struct ast_packer *s, const struct ast_frame *f) -+{ -+ if (f->frametype != AST_FRAME_VOICE) { -+ ast_log(LOG_WARNING, "Huh? Can't pack a non-voice frame!\n"); -+ return -1; -+ } -+ if (!s->format) { -+ s->format = f->subclass; -+ s->samples=0; -+ } else if (s->format != f->subclass) { -+ ast_log(LOG_WARNING, "Packer was working on %d format frames, now trying to feed %d?\n", s->format, f->subclass); -+ return -1; -+ } -+ if (s->len + f->datalen > PACKER_SIZE) { -+ ast_log(LOG_WARNING, "Out of packer space\n"); -+ return -1; -+ } -+ if (s->packet_index >= PACKER_QUEUE ){ -+ ast_log(LOG_WARNING, "Out of packer queue space\n"); -+ return -1; -+ } -+ -+ memcpy(s->data + s->len, f->data, f->datalen); -+ /* If either side is empty, reset the delivery time */ -+ if (!s->len || (!f->delivery.tv_sec && !f->delivery.tv_usec) || -+ (!s->delivery.tv_sec && !s->delivery.tv_usec)) -+ s->delivery = f->delivery; -+ s->len += f->datalen; -+//packer stuff -+ s->len_queue[s->packet_index] += f->datalen; -+ s->sample_queue[s->packet_index] += f->samples; -+ s->samples += f->samples; -+ -+ if (s->samples > s->framesize ) -+ ++s->packet_index; -+ -+ return 0; -+} -+ -+struct ast_frame *ast_packer_read(struct ast_packer *s) -+{ -+ struct ast_frame *opt; -+ int len; -+ /* IF we have an optimization frame, send it */ -+ if (s->opt) { -+ opt = s->opt; -+ s->opt = NULL; -+ return opt; -+ } -+ -+ /* Make sure we have enough data */ -+ if (s->samples < s->framesize ){ -+ return NULL; -+ } -+ len = s->len_queue[0]; -+ if (len > s->len) -+ len = s->len; -+ /* Make frame */ -+ s->f.frametype = AST_FRAME_VOICE; -+ s->f.subclass = s->format; -+ s->f.data = s->framedata + AST_FRIENDLY_OFFSET; -+ s->f.offset = AST_FRIENDLY_OFFSET; -+ s->f.datalen = len; -+ s->f.samples = s->sample_queue[0]; -+ s->f.delivery = s->delivery; -+ /* Fill Data */ -+ memcpy(s->f.data, s->data, len); -+ s->len -= len; -+ /* Move remaining data to the front if applicable */ -+ if (s->len) { -+ /* In principle this should all be fine because if we are sending -+ G.729 VAD, the next timestamp will take over anyawy */ -+ memmove(s->data, s->data + len, s->len); -+ if (s->delivery.tv_sec || s->delivery.tv_usec) { -+ /* If we have delivery time, increment it, otherwise, leave it at 0 */ -+ s->delivery.tv_sec += s->sample_queue[0] / 8000.0; -+ s->delivery.tv_usec += (((int)(s->sample_queue[0])) % 8000) * 125; -+ if (s->delivery.tv_usec > 1000000) { -+ s->delivery.tv_usec -= 1000000; -+ s->delivery.tv_sec += 1; -+ } -+ } -+ } -+ int j; -+ s->samples -= s->sample_queue[0]; -+ if( s->packet_index > 0 ){ -+ for (j=0; jpacket_index -1 ; j++){ -+ s->len_queue[j]=s->len_queue[j+1]; -+ s->sample_queue[j]=s->sample_queue[j+1]; -+ } -+ s->len_queue[s->packet_index]=0; -+ s->sample_queue[s->packet_index]=0; -+ s->packet_index--; -+ } else { -+ s->len_queue[0]=0; -+ s->sample_queue[0]=0; -+ } -+ -+ -+ /* Return frame */ -+ return &s->f; -+} -+ -+void ast_packer_free(struct ast_packer *s) -+{ -+ free(s); -+} -+ -+int queue_frame_for_listener( -+ struct ast_conference* conf, -+ struct ast_conf_member* member, -+ conf_frame* frame -+) -+{ -+ // -+ // check inputs -+ // -+ -+ if ( conf == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to queue listener frame with null conference\n" ) ; -+ return -1 ; -+ } -+ -+ if ( member == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to queue listener frame with null member\n" ) ; -+ return -1 ; -+ } -+ -+ // -+ // loop over spoken frames looking for member's appropriate match -+ // -+ -+ short found_flag = 0 ; -+ struct ast_frame* qf ; -+ -+ for ( ; frame != NULL ; frame = frame->next ) -+ { -+ // we're looking for a null or matching member -+ if ( frame->member != NULL && frame->member != member ) -+ continue ; -+ -+ if ( frame->fr == NULL ) -+ { -+ ast_log( LOG_WARNING, "unknown error queueing frame for listener, frame->fr == NULL\n" ) ; -+ continue ; -+ } -+ -+ // first, try for a pre-converted frame -+ qf = frame->converted[ member->write_format_index ] ; -+ -+ // convert ( and store ) the frame -+ if ( qf == NULL ) -+ { -+ // make a copy of the slinear version of the frame -+ qf = ast_frdup( frame->fr ) ; -+ -+ if ( qf == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to duplicate frame\n" ) ; -+ continue ; -+ } -+ -+ // convert using the conference's translation path -+ qf = convert_frame_from_slinear( conf->from_slinear_paths[ member->write_format_index ], qf ) ; -+ -+ // store the converted frame -+ // ( the frame will be free'd next time through the loop ) -+ frame->converted[ member->write_format_index ] = qf ; -+ } -+ -+ if ( qf != NULL ) -+ { -+ // duplicate the frame before queue'ing it -+ // ( since this member doesn't own this _shared_ frame ) -+ // qf = ast_frdup( qf ) ; -+ -+ -+ -+ if ( queue_outgoing_frame( member, qf, conf->delivery_time ) != 0 ) -+ { -+ // free the new frame if it couldn't be queue'd -+ // XXX NEILS - WOULD BE FREED IN CLEANUPast_frfree( qf ) ; -+ //qf = NULL ; -+ } -+ } -+ else -+ { -+ ast_log( LOG_WARNING, "unable to translate outgoing listener frame, channel => %s\n", member->channel_name ) ; -+ } -+ -+ // set found flag -+ found_flag = 1 ; -+ -+ // break from for loop -+ break ; -+ } -+ -+ // queue a silent frame -+ if ( found_flag == 0 ) -+ queue_silent_frame( conf, member ) ; -+ -+ return 0 ; -+} -+ -+ -+int queue_frame_for_speaker( -+ struct ast_conference* conf, -+ struct ast_conf_member* member, -+ conf_frame* frame -+) -+{ -+ // -+ // check inputs -+ // -+ -+ if ( conf == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to queue speaker frame with null conference\n" ) ; -+ return -1 ; -+ } -+ -+ if ( member == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to queue speaker frame with null member\n" ) ; -+ return -1 ; -+ } -+ -+ // -+ // loop over spoken frames looking for member's appropriate match -+ // -+ -+ short found_flag = 0 ; -+ struct ast_frame* qf ; -+ -+ for ( ; frame != NULL ; frame = frame->next ) -+ { -+ if ( frame->member != member ) -+ { -+ continue ; -+ } -+ -+ if ( frame->fr == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to queue speaker frame with null data\n" ) ; -+ continue ; -+ } -+ -+ // -+ // convert and queue frame -+ // -+ -+ // short-cut pointer to the ast_frame -+ qf = frame->fr ; -+ -+ if ( qf->subclass == member->write_format ) -+ { -+ // frame is already in correct format, so just queue it -+ -+ queue_outgoing_frame( member, qf, conf->delivery_time ) ; -+ } -+ else -+ { -+ // -+ // convert frame to member's write format -+ // ( calling ast_frdup() to make sure the translator's copy sticks around ) -+ // -+ qf = convert_frame_from_slinear( member->from_slinear, ast_frdup( qf ) ) ; -+ -+ if ( qf != NULL ) -+ { -+ // queue frame -+ queue_outgoing_frame( member, qf, conf->delivery_time ) ; -+ -+ // free frame ( the translator's copy ) -+ ast_frfree( qf ) ; -+ } -+ else -+ { -+ ast_log( LOG_WARNING, "unable to translate outgoing speaker frame, channel => %s\n", member->channel_name ) ; -+ } -+ } -+ -+ // set found flag -+ found_flag = 1 ; -+ -+ // we found the frame, skip to the next member -+ break ; -+ } -+ -+ // queue a silent frame -+ if ( found_flag == 0 ) -+ queue_silent_frame( conf, member ) ; -+ -+ return 0 ; -+} -+ -+ -+int queue_silent_frame( -+ struct ast_conference* conf, -+ struct ast_conf_member* member -+) -+{ -+ int c; -+#ifdef APP_CONFERENCE_DEBUG -+ // -+ // check inputs -+ // -+ -+ if ( conf == NULL ) -+ { -+ ast_log( AST_CONF_DEBUG, "unable to queue silent frame for null conference\n" ) ; -+ return -1 ; -+ } -+ -+ if ( member == NULL ) -+ { -+ ast_log( AST_CONF_DEBUG, "unable to queue silent frame for null member\n" ) ; -+ return -1 ; -+ } -+#endif // APP_CONFERENCE_DEBUG -+ -+ // -+ // initialize static variables -+ // -+ -+ static conf_frame* silent_frame = NULL ; -+ static struct ast_frame* qf = NULL ; -+ -+ if ( silent_frame == NULL ) -+ { -+ if ( ( silent_frame = get_silent_frame() ) == NULL ) -+ { -+ ast_log( LOG_WARNING, "unable to initialize static silent frame\n" ) ; -+ return -1 ; -+ } -+ } -+ -+ -+ // get the appropriate silent frame -+ qf = silent_frame->converted[ member->write_format_index ] ; -+ -+ if ( qf == NULL ) -+ { -+ // -+ // we need to do this to avoid echo on the speaker's line. -+ // translators seem to be single-purpose, i.e. they -+ // can't be used simultaneously for multiple audio streams -+ // -+ -+ struct ast_trans_pvt* trans = ast_translator_build_path( member->write_format, AST_FORMAT_SLINEAR ) ; -+ -+ if ( trans != NULL ) -+ { -+ // attempt ( five times ) to get a silent frame -+ // to make sure we provice the translator with enough data -+ for ( c = 0 ; c < 5 ; ++c ) -+ { -+ // translate the frame -+ qf = ast_translate( trans, silent_frame->fr, 0 ) ; -+ -+ // break if we get a frame -+ if ( qf != NULL ) break ; -+ } -+ -+ if ( qf != NULL ) -+ { -+ // isolate the frame so we can keep it around after trans is free'd -+ qf = ast_frisolate( qf ) ; -+ -+ // cache the new, isolated frame -+ silent_frame->converted[ member->write_format_index ] = qf ; -+ } -+ -+ ast_translator_free_path( trans ) ; -+ } -+ } -+ -+ // -+ // queue the frame, if it's not null, -+ // otherwise there was an error -+ // -+ if ( qf != NULL ) -+ { -+ queue_outgoing_frame( member, qf, conf->delivery_time ) ; -+ } -+ else -+ { -+ ast_log( LOG_ERROR, "unable to translate outgoing silent frame, channel => %s\n", member->channel_name ) ; -+ } -+ -+ return 0 ; -+} -+ -+ -+ -+void member_process_outgoing_frames(struct ast_conference* conf, -+ struct ast_conf_member *member, -+ struct conf_frame *send_frames) -+{ -+ ast_mutex_lock(&member->lock); -+ -+ // skip members that are not ready -+ if ( member->ready_for_outgoing == 0 ) -+ { -+ ast_mutex_unlock(&member->lock); -+ return ; -+ } -+ -+ // skip no receive audio clients -+ if ( member->norecv_audio ) -+ { -+ ast_mutex_unlock(&member->lock); -+ return; -+ } -+ -+ if ( member->local_speaking_state == 0 ) -+ { -+ // queue listener frame -+ queue_frame_for_listener( conf, member, send_frames ) ; -+ } -+ else -+ { -+ // queue speaker frame -+ queue_frame_for_speaker( conf, member, send_frames ) ; -+ } -+ ast_mutex_unlock(&member->lock); -+} -+ -+// Functions that will increase and decrease speaker_count in a secure way, locking the member mutex if required -+// Will also set speaking_state flag. -+// Returns the previous speaking state -+int increment_speaker_count(struct ast_conf_member *member, int lock) -+{ -+ int old_state; -+ -+ if ( lock ) -+ ast_mutex_lock(&member->lock); -+ -+ old_state = member->speaking_state; -+ member->speaker_count++; -+ member->speaking_state = 1; -+ -+ ast_log(AST_CONF_DEBUG, "Increment speaker count: id=%d, count=%d\n", member->id, member->speaker_count); -+ -+ // If this is a state change, update the timestamp -+ if ( old_state == 0 ) -+ { -+ member->speaking_state_notify = 1; -+ member->last_state_change = ast_tvnow(); -+ } -+ -+ if ( lock ) -+ ast_mutex_unlock(&member->lock); -+ -+ return old_state; -+} -+ -+int decrement_speaker_count(struct ast_conf_member *member, int lock) -+{ -+ int old_state; -+ -+ if ( lock ) -+ ast_mutex_lock(&member->lock); -+ -+ old_state = member->speaking_state; -+ if ( member->speaker_count > 0 ) -+ member->speaker_count--; -+ if ( member->speaker_count == 0 ) -+ member->speaking_state = 0; -+ -+ ast_log(AST_CONF_DEBUG, "Decrement speaker count: id=%d, count=%d\n", member->id, member->speaker_count); -+ -+ // If this is a state change, update the timestamp -+ if ( old_state == 1 && member->speaking_state == 0 ) -+ { -+ member->speaking_state_notify = 1; -+ member->last_state_change = ast_tvnow(); -+ } -+ -+ if ( lock ) -+ ast_mutex_unlock(&member->lock); -+ -+ return old_state; -+} -+ -+void member_process_spoken_frames(struct ast_conference* conf, -+ struct ast_conf_member *member, -+ struct conf_frame **spoken_frames, -+ long time_diff, -+ int *listener_count, -+ int *speaker_count -+ ) -+{ -+ struct conf_frame *cfr; -+ -+ // acquire member mutex -+ TIMELOG(ast_mutex_lock( &member->lock ),1,"conf thread member lock") ; -+ -+ // check for dead members -+ if ( member->remove_flag == 1 ) -+ { -+ // If this member is the default video source for the conference, then change the default to -1 -+ if ( member->id == conf->default_video_source_id ) -+ conf->default_video_source_id = -1; -+ -+ if (conf->debug_flag) -+ { -+ ast_log( LOG_NOTICE, "found member slated for removal, channel => %s\n", member->channel_name ) ; -+ } -+ remove_member( member, conf ) ; -+ return; -+ } -+ -+ // tell member the number of frames we're going to need ( used to help dropping algorithm ) -+ member->inFramesNeeded = ( time_diff / AST_CONF_FRAME_INTERVAL ) - 1 ; -+ -+ // !!! TESTING !!! -+ if ( -+ conf->debug_flag == 1 -+ && member->inFramesNeeded > 0 -+ ) -+ { -+ ast_log( AST_CONF_DEBUG, "channel => %s, inFramesNeeded => %d, inFramesCount => %d\n", -+ member->channel_name, member->inFramesNeeded, member->inFramesCount ) ; -+ } -+ -+ // non-listener member should have frames, -+ // unless silence detection dropped them -+ cfr = get_incoming_frame( member ) ; -+ -+ // handle retrieved frames -+ if ( cfr == NULL || cfr->fr == NULL ) -+ { -+ // Decrement speaker count for us and for driven members -+ // This happens only for the first missed frame, since we want to -+ // decrement only on state transitions -+ if ( member->local_speaking_state == 1 ) -+ { -+ decrement_speaker_count(member, 0); -+ member->local_speaking_state = 0; -+ // If we're driving another member, decrement its speaker count as well -+ if ( member->driven_member != NULL ) -+ decrement_speaker_count(member->driven_member, 1); -+ } -+ -+ // count the listeners -+ (*listener_count)++ ; -+ } -+ else -+ { -+ // append the frame to the list of spoken frames -+ if ( *spoken_frames != NULL ) -+ { -+ // add new frame to end of list -+ cfr->next = *spoken_frames ; -+ (*spoken_frames)->prev = cfr ; -+ } -+ -+ // point the list at the new frame -+ *spoken_frames = cfr ; -+ -+ // Increment speaker count for us and for driven members -+ // This happens only on the first received frame, since we want to -+ // increment only on state transitions -+ if ( member->local_speaking_state == 0 ) -+ { -+ increment_speaker_count(member, 0); -+ member->local_speaking_state = 1; -+ -+ // If we're driving another member, increment its speaker count as well -+ if ( member->driven_member != NULL ) -+ increment_speaker_count(member->driven_member, 1); -+ } -+ -+ // count the speakers -+ (*speaker_count)++ ; -+ } -+ -+ // release member mutex -+ ast_mutex_unlock( &member->lock ) ; -+ -+ return; -+} -diff --git a/apps/conference/member.h b/apps/conference/member.h -new file mode 100644 -index 0000000..f4cae3f ---- /dev/null -+++ b/apps/conference/member.h -@@ -0,0 +1,315 @@ -+ -+// $Id: member.h 872 2007-03-05 23:43:10Z sbalea $ -+ -+/* -+ * app_conference -+ * -+ * A channel independent conference application for Asterisk -+ * -+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH -+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc. -+ * Copyright (C) 2005, 2006 HorizonWimba, Inc. -+ * Copyright (C) 2007 Wimba, Inc. -+ * -+ * Klaus-Peter Junghanns -+ * -+ * Video Conferencing support added by -+ * Neil Stratford -+ * Copyright (C) 2005, 2005 Vipadia Limited -+ * -+ * VAD driven video conferencing, text message support -+ * and miscellaneous enhancements added by -+ * Mihai Balea -+ * -+ * This program may be modified and distributed under the -+ * terms of the GNU General Public License. You should have received -+ * a copy of the GNU General Public License along with this -+ * program; if not, write to the Free Software Foundation, Inc. -+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef _APP_CONF_MEMBER_H -+#define _APP_CONF_MEMBER_H -+ -+// -+// includes -+// -+ -+#include "app_conference.h" -+#include "common.h" -+ -+// -+// struct declarations -+// -+ -+struct ast_conf_soundq -+{ -+ char name[256]; -+ struct ast_filestream *stream; // the stream -+ int muted; // should incoming audio be muted while we play? -+ struct ast_conf_soundq *next; -+}; -+ -+struct ast_conf_member -+{ -+ ast_mutex_t lock ; // member data mutex -+ -+ struct ast_channel* chan ; // member's channel -+ char* channel_name ; // member's channel name -+ -+ // values passed to create_member () via *data -+ int priority ; // highest priority gets the channel -+ char* flags ; // raw member-type flags -+ char type ; // L = ListenOnly, M = Moderator, S = Standard (Listen/Talk) -+ char* conf_name ; // name of the conference that own this member -+ -+ char *callerid; -+ char *callername; -+ -+ // voice flags -+ int vad_flag; -+ int denoise_flag; -+ int agc_flag; -+ int via_telephone; -+ -+ // video conference params -+ int id; -+ int initial_id; -+ int req_id; -+ -+ // muting options - this member will not be heard/seen -+ int mute_audio; -+ int mute_video; -+ -+ // this member will not hear/see -+ int norecv_audio; -+ int norecv_video; -+ -+ // this member does not have a camera -+ int no_camera; -+ -+ // is this person a moderator? -+ int ismoderator; -+ -+ // determine by flags and channel name -+ char connection_type ; // T = telephone, X = iaxclient, S = sip -+ -+ // vad voice probability thresholds -+ float vad_prob_start ; -+ float vad_prob_continue ; -+ -+ // ready flag -+ short ready_for_outgoing ; -+ -+ // input frame queue -+ conf_frame* inFrames ; -+ conf_frame* inFramesTail ; -+ unsigned int inFramesCount ; -+ conf_frame* inVideoFrames ; -+ conf_frame* inVideoFramesTail ; -+ unsigned int inVideoFramesCount ; -+ conf_frame* inDTMFFrames ; -+ conf_frame* inDTMFFramesTail ; -+ unsigned int inDTMFFramesCount ; -+ conf_frame* inTextFrames ; -+ conf_frame* inTextFramesTail ; -+ unsigned int inTextFramesCount ; -+ -+ -+ // input/output smoother -+ struct ast_smoother *inSmoother; -+ struct ast_packer *outPacker; -+ int smooth_size_in; -+ int smooth_size_out; -+ int smooth_multiple; -+ -+ // frames needed by conference_exec -+ unsigned int inFramesNeeded ; -+ unsigned int inVideoFramesNeeded ; -+ -+ // used when caching last frame -+ conf_frame* inFramesLast ; -+ unsigned int inFramesRepeatLast ; -+ unsigned short okayToCacheLast ; -+ -+ // LL output frame queue -+ conf_frame* outFrames ; -+ conf_frame* outFramesTail ; -+ unsigned int outFramesCount ; -+ conf_frame* outVideoFrames ; -+ conf_frame* outVideoFramesTail ; -+ unsigned int outVideoFramesCount ; -+ conf_frame* outDTMFFrames ; -+ conf_frame* outDTMFFramesTail ; -+ unsigned int outDTMFFramesCount ; -+ conf_frame* outTextFrames ; -+ conf_frame* outTextFramesTail ; -+ unsigned int outTextFramesCount ; -+ -+ // LL video switched flag -+ short conference; -+ -+ // switch video by VAD? -+ short vad_switch; -+ // switch by dtmf? -+ short dtmf_switch; -+ // relay dtmf to manager? -+ short dtmf_relay; -+ // initial nat delay flag -+ short first_frame_received; -+ // does text messages? -+ short does_text; -+ -+ -+ // time we last dropped a frame -+ struct timeval last_in_dropped ; -+ struct timeval last_out_dropped ; -+ -+ // ( not currently used ) -+ // int samplesperframe ; -+ -+ // used for determining need to mix frames -+ // and for management interface notification -+ // and for VAD based video switching -+ short speaking_state_notify ; -+ short speaking_state ; // This flag will be true if this member or any of its drivers is speaking -+ short local_speaking_state; // This flag will be true only if this member is speaking -+ struct timeval last_state_change; -+ int speaker_count; // Number of drivers (including this member) that are speaking -+ -+ // pointer to next member in single-linked list -+ struct ast_conf_member* next ; -+ -+ // accounting values -+ unsigned long frames_in ; -+ unsigned long frames_in_dropped ; -+ unsigned long frames_out ; -+ unsigned long frames_out_dropped ; -+ -+ unsigned long video_frames_in ; -+ unsigned long video_frames_in_dropped ; -+ unsigned long video_frames_out ; -+ unsigned long video_frames_out_dropped ; -+ -+ unsigned long dtmf_frames_in ; -+ unsigned long dtmf_frames_in_dropped ; -+ unsigned long dtmf_frames_out ; -+ unsigned long dtmf_frames_out_dropped ; -+ -+ unsigned long text_frames_in ; -+ unsigned long text_frames_in_dropped ; -+ unsigned long text_frames_out ; -+ unsigned long text_frames_out_dropped ; -+ -+ // for counting sequentially dropped frames -+ unsigned int sequential_drops ; -+ unsigned long since_dropped ; -+ -+ // start time -+ struct timeval time_entered ; -+ struct timeval lastsent_timeval ; -+ -+ // flag indicating we should remove this member -+ short remove_flag ; -+ short kick_flag ; -+ -+#if ( SILDET == 2 ) -+ // pointer to speex preprocessor dsp -+ SpeexPreprocessState *dsp ; -+ // number of frames to ignore speex_preprocess() -+ int ignore_speex_count; -+#else -+ // placeholder when preprocessing is not enabled -+ void* dsp ; -+#endif -+ -+ // audio format this member is using -+ int write_format ; -+ int read_format ; -+ -+ int write_format_index ; -+ int read_format_index ; -+ -+ // member frame translators -+ struct ast_trans_pvt* to_slinear ; -+ struct ast_trans_pvt* from_slinear ; -+ -+ // For playing sounds -+ struct ast_conf_soundq *soundq; -+ struct ast_conf_soundq *videoq; -+ -+ // Enter/leave sounds -+ char * enter_snd; -+ char * leave_snd; -+ -+ // Pointer to another member that will be driven from this member's audio -+ struct ast_conf_member *driven_member; -+} ; -+ -+struct conf_member -+{ -+ struct ast_conf_member* realmember ; -+ struct conf_member* next ; -+} ; -+ -+// -+// function declarations -+// -+ -+int member_exec( struct ast_channel* chan, void* data ) ; -+ -+struct ast_conf_member* check_active_video( int id, struct ast_conference *conf ); -+ -+struct ast_conf_member* create_member( struct ast_channel* chan, const char* data ) ; -+struct ast_conf_member* delete_member( struct ast_conf_member* member ) ; -+ -+// incoming queue -+int queue_incoming_frame( struct ast_conf_member* member, struct ast_frame* fr ) ; -+int queue_incoming_video_frame( struct ast_conf_member* member, const struct ast_frame* fr ) ; -+int queue_incoming_dtmf_frame( struct ast_conf_member* member, const struct ast_frame* fr ) ; -+conf_frame* get_incoming_frame( struct ast_conf_member* member ) ; -+conf_frame* get_incoming_video_frame( struct ast_conf_member* member ) ; -+conf_frame* get_incoming_dtmf_frame( struct ast_conf_member* member ) ; -+ -+// outgoing queue -+int queue_outgoing_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery ) ; -+int __queue_outgoing_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery ) ; -+conf_frame* get_outgoing_frame( struct ast_conf_member* member ) ; -+ -+int queue_outgoing_video_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery ) ; -+conf_frame* get_outgoing_video_frame( struct ast_conf_member* member ) ; -+int queue_outgoing_dtmf_frame( struct ast_conf_member* member, const struct ast_frame* fr ) ; -+int queue_outgoing_text_frame( struct ast_conf_member* member, const struct ast_frame* fr ) ; -+conf_frame* get_outgoing_dtmf_frame( struct ast_conf_member* member ) ; -+conf_frame* get_outgoing_text_frame( struct ast_conf_member* member ) ; -+ -+void send_state_change_notifications( struct ast_conf_member* member ) ; -+ -+int increment_speaker_count(struct ast_conf_member *member, int lock); -+int decrement_speaker_count(struct ast_conf_member *member, int lock); -+ -+void member_process_spoken_frames(struct ast_conference* conf, -+ struct ast_conf_member *member, -+ struct conf_frame **spoken_frames, -+ long time_diff, -+ int *listener_count, -+ int *speaker_count); -+ -+void member_process_outgoing_frames(struct ast_conference* conf, -+ struct ast_conf_member *member, -+ struct conf_frame *send_frames); -+ -+// -+// packer functions -+// -+ -+struct ast_packer; -+ -+extern struct ast_packer *ast_packer_new(int bytes); -+extern void ast_packer_set_flags(struct ast_packer *packer, int flags); -+extern int ast_packer_get_flags(struct ast_packer *packer); -+extern void ast_packer_free(struct ast_packer *s); -+extern void ast_packer_reset(struct ast_packer *s, int bytes); -+extern int ast_packer_feed(struct ast_packer *s, const struct ast_frame *f); -+extern struct ast_frame *ast_packer_read(struct ast_packer *s); -+#endif --- -1.5.3.6 - diff --git a/asterisk-1.4.16-autoconf.patch b/asterisk-1.4.16-autoconf.patch deleted file mode 100644 index ede2e30..0000000 --- a/asterisk-1.4.16-autoconf.patch +++ /dev/null @@ -1,1184 +0,0 @@ -From 3b5f7cfaadc19fdc122742b1d2ff4313d33fd0c8 Mon Sep 17 00:00:00 2001 -From: Jeffrey C. Ollie -Date: Sun, 18 Nov 2007 22:30:31 -0600 -Subject: [PATCH] Update configure script. - ---- - configure | 993 +++++++++++++++++++++++++++++++++++++- - include/asterisk/autoconfig.h.in | 6 + - 2 files changed, 974 insertions(+), 25 deletions(-) - -diff --git a/configure b/configure -index 8fcf1b9..104811a 100755 ---- a/configure -+++ b/configure -@@ -1,5 +1,5 @@ - #! /bin/sh --# From configure.ac Revision: 92463 . -+# From configure.ac Revision. - # Guess values for system-dependent variables and create Makefiles. - # Generated by GNU Autoconf 2.61 for asterisk 1.4. - # -@@ -721,10 +721,18 @@ ALSA_LIB - ALSA_INCLUDE - ALSA_DIR - PBX_ALSA -+BLUETOOTH_LIB -+BLUETOOTH_INCLUDE -+BLUETOOTH_DIR -+PBX_BLUETOOTH - CURL_LIB - CURL_INCLUDE - CURL_DIR - PBX_CURL -+CAP_LIB -+CAP_INCLUDE -+CAP_DIR -+PBX_CAP - CURSES_LIB - CURSES_INCLUDE - CURSES_DIR -@@ -1510,7 +1518,9 @@ Optional Packages: - --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) - --with-gnu-ld assume the C compiler uses GNU ld [default=no] - --with-asound=PATH use Advanced Linux Sound Architecture files in PATH -+ --with-bluetooth=PATH use Bluetooth Support files in PATH - --with-curl=PATH use cURL files in PATH -+ --with-cap=PATH use POSIX 1.e capabilities files in PATH - --with-curses=PATH use curses files in PATH - --with-gnutls=PATH use GNU TLS support (used for iksemel only) files in - PATH -@@ -7670,6 +7680,34 @@ PBX_ALSA=0 - - - -+BLUETOOTH_DESCRIP="Bluetooth Support" -+BLUETOOTH_OPTION="bluetooth" -+ -+# Check whether --with-bluetooth was given. -+if test "${with_bluetooth+set}" = set; then -+ withval=$with_bluetooth; -+case ${withval} in -+ n|no) -+ USE_BLUETOOTH=no -+ ;; -+ y|ye|yes) -+ BLUETOOTH_MANDATORY="yes" -+ ;; -+ *) -+ BLUETOOTH_DIR="${withval}" -+ BLUETOOTH_MANDATORY="yes" -+ ;; -+esac -+ -+fi -+ -+PBX_BLUETOOTH=0 -+ -+ -+ -+ -+ -+ - CURL_DESCRIP="cURL" - CURL_OPTION="curl" - -@@ -7698,6 +7736,34 @@ PBX_CURL=0 - - - -+CAP_DESCRIP="POSIX 1.e capabilities" -+CAP_OPTION="cap" -+ -+# Check whether --with-cap was given. -+if test "${with_cap+set}" = set; then -+ withval=$with_cap; -+case ${withval} in -+ n|no) -+ USE_CAP=no -+ ;; -+ y|ye|yes) -+ CAP_MANDATORY="yes" -+ ;; -+ *) -+ CAP_DIR="${withval}" -+ CAP_MANDATORY="yes" -+ ;; -+esac -+ -+fi -+ -+PBX_CAP=0 -+ -+ -+ -+ -+ -+ - CURSES_DESCRIP="curses" - CURSES_OPTION="curses" - -@@ -11988,13 +12054,11 @@ _ACEOF - cat confdefs.h >>conftest.$ac_ext - cat >>conftest.$ac_ext <<_ACEOF - /* end confdefs.h. */ --#include /* for off_t */ -- #include -+#include - int - main () - { --int (*fp) (FILE *, off_t, int) = fseeko; -- return fseeko (stdin, 0, 0) && fp (stdin, 0, 0); -+return fseeko (stdin, 0, 0) && (fseeko) (stdin, 0, 0); - ; - return 0; - } -@@ -12034,13 +12098,11 @@ cat confdefs.h >>conftest.$ac_ext - cat >>conftest.$ac_ext <<_ACEOF - /* end confdefs.h. */ - #define _LARGEFILE_SOURCE 1 --#include /* for off_t */ -- #include -+#include - int - main () - { --int (*fp) (FILE *, off_t, int) = fseeko; -- return fseeko (stdin, 0, 0) && fp (stdin, 0, 0); -+return fseeko (stdin, 0, 0) && (fseeko) (stdin, 0, 0); - ; - return 0; - } -@@ -16757,6 +16819,415 @@ fi - - - -+if test "${USE_BLUETOOTH}" != "no"; then -+ pbxlibdir="" -+ if test "x${BLUETOOTH_DIR}" != "x"; then -+ if test -d ${BLUETOOTH_DIR}/lib; then -+ pbxlibdir="-L${BLUETOOTH_DIR}/lib" -+ else -+ pbxlibdir="-L${BLUETOOTH_DIR}" -+ fi -+ fi -+ { echo "$as_me:$LINENO: checking for ba2str in -lbluetooth" >&5 -+echo $ECHO_N "checking for ba2str in -lbluetooth... $ECHO_C" >&6; } -+if test "${ac_cv_lib_bluetooth_ba2str+set}" = set; then -+ echo $ECHO_N "(cached) $ECHO_C" >&6 -+else -+ ac_check_lib_save_LIBS=$LIBS -+LIBS="-lbluetooth ${pbxlibdir} $LIBS" -+cat >conftest.$ac_ext <<_ACEOF -+/* confdefs.h. */ -+_ACEOF -+cat confdefs.h >>conftest.$ac_ext -+cat >>conftest.$ac_ext <<_ACEOF -+/* end confdefs.h. */ -+ -+/* Override any GCC internal prototype to avoid an error. -+ Use char because int might match the return type of a GCC -+ builtin and then its argument prototype would still apply. */ -+#ifdef __cplusplus -+extern "C" -+#endif -+char ba2str (); -+int -+main () -+{ -+return ba2str (); -+ ; -+ return 0; -+} -+_ACEOF -+rm -f conftest.$ac_objext conftest$ac_exeext -+if { (ac_try="$ac_link" -+case "(($ac_try" in -+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; -+ *) ac_try_echo=$ac_try;; -+esac -+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 -+ (eval "$ac_link") 2>conftest.er1 -+ ac_status=$? -+ grep -v '^ *+' conftest.er1 >conftest.err -+ rm -f conftest.er1 -+ cat conftest.err >&5 -+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); } && { -+ test -z "$ac_c_werror_flag" || -+ test ! -s conftest.err -+ } && test -s conftest$ac_exeext && -+ $as_test_x conftest$ac_exeext; then -+ ac_cv_lib_bluetooth_ba2str=yes -+else -+ echo "$as_me: failed program was:" >&5 -+sed 's/^/| /' conftest.$ac_ext >&5 -+ -+ ac_cv_lib_bluetooth_ba2str=no -+fi -+ -+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ -+ conftest$ac_exeext conftest.$ac_ext -+LIBS=$ac_check_lib_save_LIBS -+fi -+{ echo "$as_me:$LINENO: result: $ac_cv_lib_bluetooth_ba2str" >&5 -+echo "${ECHO_T}$ac_cv_lib_bluetooth_ba2str" >&6; } -+if test $ac_cv_lib_bluetooth_ba2str = yes; then -+ AST_BLUETOOTH_FOUND=yes -+else -+ AST_BLUETOOTH_FOUND=no -+fi -+ -+ -+ if test "${AST_BLUETOOTH_FOUND}" = "yes"; then -+ BLUETOOTH_LIB="-lbluetooth " -+ BLUETOOTH_HEADER_FOUND="1" -+ if test "x${BLUETOOTH_DIR}" != "x"; then -+ BLUETOOTH_LIB="${pbxlibdir} ${BLUETOOTH_LIB}" -+ BLUETOOTH_INCLUDE="-I${BLUETOOTH_DIR}/include" -+ saved_cppflags="${CPPFLAGS}" -+ CPPFLAGS="${CPPFLAGS} -I${BLUETOOTH_DIR}/include" -+ if test "xbluetooth/bluetooth.h" != "x" ; then -+ as_ac_Header=`echo "ac_cv_header_${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h" | $as_tr_sh` -+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then -+ { echo "$as_me:$LINENO: checking for ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h" >&5 -+echo $ECHO_N "checking for ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h... $ECHO_C" >&6; } -+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then -+ echo $ECHO_N "(cached) $ECHO_C" >&6 -+fi -+ac_res=`eval echo '${'$as_ac_Header'}'` -+ { echo "$as_me:$LINENO: result: $ac_res" >&5 -+echo "${ECHO_T}$ac_res" >&6; } -+else -+ # Is the header compilable? -+{ echo "$as_me:$LINENO: checking ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h usability" >&5 -+echo $ECHO_N "checking ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h usability... $ECHO_C" >&6; } -+cat >conftest.$ac_ext <<_ACEOF -+/* confdefs.h. */ -+_ACEOF -+cat confdefs.h >>conftest.$ac_ext -+cat >>conftest.$ac_ext <<_ACEOF -+/* end confdefs.h. */ -+$ac_includes_default -+#include <${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h> -+_ACEOF -+rm -f conftest.$ac_objext -+if { (ac_try="$ac_compile" -+case "(($ac_try" in -+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; -+ *) ac_try_echo=$ac_try;; -+esac -+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 -+ (eval "$ac_compile") 2>conftest.er1 -+ ac_status=$? -+ grep -v '^ *+' conftest.er1 >conftest.err -+ rm -f conftest.er1 -+ cat conftest.err >&5 -+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); } && { -+ test -z "$ac_c_werror_flag" || -+ test ! -s conftest.err -+ } && test -s conftest.$ac_objext; then -+ ac_header_compiler=yes -+else -+ echo "$as_me: failed program was:" >&5 -+sed 's/^/| /' conftest.$ac_ext >&5 -+ -+ ac_header_compiler=no -+fi -+ -+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 -+echo "${ECHO_T}$ac_header_compiler" >&6; } -+ -+# Is the header present? -+{ echo "$as_me:$LINENO: checking ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h presence" >&5 -+echo $ECHO_N "checking ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h presence... $ECHO_C" >&6; } -+cat >conftest.$ac_ext <<_ACEOF -+/* confdefs.h. */ -+_ACEOF -+cat confdefs.h >>conftest.$ac_ext -+cat >>conftest.$ac_ext <<_ACEOF -+/* end confdefs.h. */ -+#include <${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h> -+_ACEOF -+if { (ac_try="$ac_cpp conftest.$ac_ext" -+case "(($ac_try" in -+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; -+ *) ac_try_echo=$ac_try;; -+esac -+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 -+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 -+ ac_status=$? -+ grep -v '^ *+' conftest.er1 >conftest.err -+ rm -f conftest.er1 -+ cat conftest.err >&5 -+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); } >/dev/null && { -+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || -+ test ! -s conftest.err -+ }; then -+ ac_header_preproc=yes -+else -+ echo "$as_me: failed program was:" >&5 -+sed 's/^/| /' conftest.$ac_ext >&5 -+ -+ ac_header_preproc=no -+fi -+ -+rm -f conftest.err conftest.$ac_ext -+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 -+echo "${ECHO_T}$ac_header_preproc" >&6; } -+ -+# So? What about this header? -+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in -+ yes:no: ) -+ { echo "$as_me:$LINENO: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: accepted by the compiler, rejected by the preprocessor!" >&5 -+echo "$as_me: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: accepted by the compiler, rejected by the preprocessor!" >&2;} -+ { echo "$as_me:$LINENO: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: proceeding with the compiler's result" >&5 -+echo "$as_me: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: proceeding with the compiler's result" >&2;} -+ ac_header_preproc=yes -+ ;; -+ no:yes:* ) -+ { echo "$as_me:$LINENO: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: present but cannot be compiled" >&5 -+echo "$as_me: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: present but cannot be compiled" >&2;} -+ { echo "$as_me:$LINENO: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: check for missing prerequisite headers?" >&5 -+echo "$as_me: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: check for missing prerequisite headers?" >&2;} -+ { echo "$as_me:$LINENO: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: see the Autoconf documentation" >&5 -+echo "$as_me: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: see the Autoconf documentation" >&2;} -+ { echo "$as_me:$LINENO: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: section \"Present But Cannot Be Compiled\"" >&5 -+echo "$as_me: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: section \"Present But Cannot Be Compiled\"" >&2;} -+ { echo "$as_me:$LINENO: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: proceeding with the preprocessor's result" >&5 -+echo "$as_me: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: proceeding with the preprocessor's result" >&2;} -+ { echo "$as_me:$LINENO: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: in the future, the compiler will take precedence" >&5 -+echo "$as_me: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: in the future, the compiler will take precedence" >&2;} -+ ( cat <<\_ASBOX -+## ------------------------------- ## -+## Report this to www.asterisk.org ## -+## ------------------------------- ## -+_ASBOX -+ ) | sed "s/^/$as_me: WARNING: /" >&2 -+ ;; -+esac -+{ echo "$as_me:$LINENO: checking for ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h" >&5 -+echo $ECHO_N "checking for ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h... $ECHO_C" >&6; } -+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then -+ echo $ECHO_N "(cached) $ECHO_C" >&6 -+else -+ eval "$as_ac_Header=\$ac_header_preproc" -+fi -+ac_res=`eval echo '${'$as_ac_Header'}'` -+ { echo "$as_me:$LINENO: result: $ac_res" >&5 -+echo "${ECHO_T}$ac_res" >&6; } -+ -+fi -+if test `eval echo '${'$as_ac_Header'}'` = yes; then -+ BLUETOOTH_HEADER_FOUND=1 -+else -+ BLUETOOTH_HEADER_FOUND=0 -+fi -+ -+ -+ fi -+ CPPFLAGS="${saved_cppflags}" -+ else -+ if test "xbluetooth/bluetooth.h" != "x" ; then -+ if test "${ac_cv_header_bluetooth_bluetooth_h+set}" = set; then -+ { echo "$as_me:$LINENO: checking for bluetooth/bluetooth.h" >&5 -+echo $ECHO_N "checking for bluetooth/bluetooth.h... $ECHO_C" >&6; } -+if test "${ac_cv_header_bluetooth_bluetooth_h+set}" = set; then -+ echo $ECHO_N "(cached) $ECHO_C" >&6 -+fi -+{ echo "$as_me:$LINENO: result: $ac_cv_header_bluetooth_bluetooth_h" >&5 -+echo "${ECHO_T}$ac_cv_header_bluetooth_bluetooth_h" >&6; } -+else -+ # Is the header compilable? -+{ echo "$as_me:$LINENO: checking bluetooth/bluetooth.h usability" >&5 -+echo $ECHO_N "checking bluetooth/bluetooth.h usability... $ECHO_C" >&6; } -+cat >conftest.$ac_ext <<_ACEOF -+/* confdefs.h. */ -+_ACEOF -+cat confdefs.h >>conftest.$ac_ext -+cat >>conftest.$ac_ext <<_ACEOF -+/* end confdefs.h. */ -+$ac_includes_default -+#include -+_ACEOF -+rm -f conftest.$ac_objext -+if { (ac_try="$ac_compile" -+case "(($ac_try" in -+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; -+ *) ac_try_echo=$ac_try;; -+esac -+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 -+ (eval "$ac_compile") 2>conftest.er1 -+ ac_status=$? -+ grep -v '^ *+' conftest.er1 >conftest.err -+ rm -f conftest.er1 -+ cat conftest.err >&5 -+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); } && { -+ test -z "$ac_c_werror_flag" || -+ test ! -s conftest.err -+ } && test -s conftest.$ac_objext; then -+ ac_header_compiler=yes -+else -+ echo "$as_me: failed program was:" >&5 -+sed 's/^/| /' conftest.$ac_ext >&5 -+ -+ ac_header_compiler=no -+fi -+ -+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 -+echo "${ECHO_T}$ac_header_compiler" >&6; } -+ -+# Is the header present? -+{ echo "$as_me:$LINENO: checking bluetooth/bluetooth.h presence" >&5 -+echo $ECHO_N "checking bluetooth/bluetooth.h presence... $ECHO_C" >&6; } -+cat >conftest.$ac_ext <<_ACEOF -+/* confdefs.h. */ -+_ACEOF -+cat confdefs.h >>conftest.$ac_ext -+cat >>conftest.$ac_ext <<_ACEOF -+/* end confdefs.h. */ -+#include -+_ACEOF -+if { (ac_try="$ac_cpp conftest.$ac_ext" -+case "(($ac_try" in -+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; -+ *) ac_try_echo=$ac_try;; -+esac -+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 -+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 -+ ac_status=$? -+ grep -v '^ *+' conftest.er1 >conftest.err -+ rm -f conftest.er1 -+ cat conftest.err >&5 -+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); } >/dev/null && { -+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || -+ test ! -s conftest.err -+ }; then -+ ac_header_preproc=yes -+else -+ echo "$as_me: failed program was:" >&5 -+sed 's/^/| /' conftest.$ac_ext >&5 -+ -+ ac_header_preproc=no -+fi -+ -+rm -f conftest.err conftest.$ac_ext -+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 -+echo "${ECHO_T}$ac_header_preproc" >&6; } -+ -+# So? What about this header? -+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in -+ yes:no: ) -+ { echo "$as_me:$LINENO: WARNING: bluetooth/bluetooth.h: accepted by the compiler, rejected by the preprocessor!" >&5 -+echo "$as_me: WARNING: bluetooth/bluetooth.h: accepted by the compiler, rejected by the preprocessor!" >&2;} -+ { echo "$as_me:$LINENO: WARNING: bluetooth/bluetooth.h: proceeding with the compiler's result" >&5 -+echo "$as_me: WARNING: bluetooth/bluetooth.h: proceeding with the compiler's result" >&2;} -+ ac_header_preproc=yes -+ ;; -+ no:yes:* ) -+ { echo "$as_me:$LINENO: WARNING: bluetooth/bluetooth.h: present but cannot be compiled" >&5 -+echo "$as_me: WARNING: bluetooth/bluetooth.h: present but cannot be compiled" >&2;} -+ { echo "$as_me:$LINENO: WARNING: bluetooth/bluetooth.h: check for missing prerequisite headers?" >&5 -+echo "$as_me: WARNING: bluetooth/bluetooth.h: check for missing prerequisite headers?" >&2;} -+ { echo "$as_me:$LINENO: WARNING: bluetooth/bluetooth.h: see the Autoconf documentation" >&5 -+echo "$as_me: WARNING: bluetooth/bluetooth.h: see the Autoconf documentation" >&2;} -+ { echo "$as_me:$LINENO: WARNING: bluetooth/bluetooth.h: section \"Present But Cannot Be Compiled\"" >&5 -+echo "$as_me: WARNING: bluetooth/bluetooth.h: section \"Present But Cannot Be Compiled\"" >&2;} -+ { echo "$as_me:$LINENO: WARNING: bluetooth/bluetooth.h: proceeding with the preprocessor's result" >&5 -+echo "$as_me: WARNING: bluetooth/bluetooth.h: proceeding with the preprocessor's result" >&2;} -+ { echo "$as_me:$LINENO: WARNING: bluetooth/bluetooth.h: in the future, the compiler will take precedence" >&5 -+echo "$as_me: WARNING: bluetooth/bluetooth.h: in the future, the compiler will take precedence" >&2;} -+ ( cat <<\_ASBOX -+## ------------------------------- ## -+## Report this to www.asterisk.org ## -+## ------------------------------- ## -+_ASBOX -+ ) | sed "s/^/$as_me: WARNING: /" >&2 -+ ;; -+esac -+{ echo "$as_me:$LINENO: checking for bluetooth/bluetooth.h" >&5 -+echo $ECHO_N "checking for bluetooth/bluetooth.h... $ECHO_C" >&6; } -+if test "${ac_cv_header_bluetooth_bluetooth_h+set}" = set; then -+ echo $ECHO_N "(cached) $ECHO_C" >&6 -+else -+ ac_cv_header_bluetooth_bluetooth_h=$ac_header_preproc -+fi -+{ echo "$as_me:$LINENO: result: $ac_cv_header_bluetooth_bluetooth_h" >&5 -+echo "${ECHO_T}$ac_cv_header_bluetooth_bluetooth_h" >&6; } -+ -+fi -+if test $ac_cv_header_bluetooth_bluetooth_h = yes; then -+ BLUETOOTH_HEADER_FOUND=1 -+else -+ BLUETOOTH_HEADER_FOUND=0 -+fi -+ -+ -+ fi -+ fi -+ if test "x${BLUETOOTH_HEADER_FOUND}" = "x0" ; then -+ if test -n "${BLUETOOTH_MANDATORY}" ; -+ then -+ { echo "$as_me:$LINENO: ***" >&5 -+echo "$as_me: ***" >&6;} -+ { echo "$as_me:$LINENO: *** It appears that you do not have the bluetooth development package installed." >&5 -+echo "$as_me: *** It appears that you do not have the bluetooth development package installed." >&6;} -+ { echo "$as_me:$LINENO: *** Please install it to include ${BLUETOOTH_DESCRIP} support, or re-run configure" >&5 -+echo "$as_me: *** Please install it to include ${BLUETOOTH_DESCRIP} support, or re-run configure" >&6;} -+ { echo "$as_me:$LINENO: *** without explicitly specifying --with-${BLUETOOTH_OPTION}" >&5 -+echo "$as_me: *** without explicitly specifying --with-${BLUETOOTH_OPTION}" >&6;} -+ exit 1 -+ fi -+ BLUETOOTH_LIB="" -+ BLUETOOTH_INCLUDE="" -+ PBX_BLUETOOTH=0 -+ else -+ PBX_BLUETOOTH=1 -+ -+cat >>confdefs.h <<_ACEOF -+#define HAVE_BLUETOOTH 1 -+_ACEOF -+ -+ fi -+ elif test -n "${BLUETOOTH_MANDATORY}"; -+ then -+ { echo "$as_me:$LINENO: ***" >&5 -+echo "$as_me: ***" >&6;} -+ { echo "$as_me:$LINENO: *** The ${BLUETOOTH_DESCRIP} installation on this system appears to be broken." >&5 -+echo "$as_me: *** The ${BLUETOOTH_DESCRIP} installation on this system appears to be broken." >&6;} -+ { echo "$as_me:$LINENO: *** Either correct the installation, or run configure" >&5 -+echo "$as_me: *** Either correct the installation, or run configure" >&6;} -+ { echo "$as_me:$LINENO: *** without explicitly specifying --with-${BLUETOOTH_OPTION}" >&5 -+echo "$as_me: *** without explicitly specifying --with-${BLUETOOTH_OPTION}" >&6;} -+ exit 1 -+ fi -+fi -+ -+ -+ - if test "${USE_CURSES}" != "no"; then - pbxlibdir="" - if test "x${CURSES_DIR}" != "x"; then -@@ -17165,6 +17636,417 @@ echo "$as_me: *** without explicitly specifying --with-${CURSES_OPTION}" >&6;} - fi - - -+if test "x${host_os}" = "xlinux-gnu" ; then -+ -+if test "${USE_CAP}" != "no"; then -+ pbxlibdir="" -+ if test "x${CAP_DIR}" != "x"; then -+ if test -d ${CAP_DIR}/lib; then -+ pbxlibdir="-L${CAP_DIR}/lib" -+ else -+ pbxlibdir="-L${CAP_DIR}" -+ fi -+ fi -+ { echo "$as_me:$LINENO: checking for cap_from_text in -lcap" >&5 -+echo $ECHO_N "checking for cap_from_text in -lcap... $ECHO_C" >&6; } -+if test "${ac_cv_lib_cap_cap_from_text+set}" = set; then -+ echo $ECHO_N "(cached) $ECHO_C" >&6 -+else -+ ac_check_lib_save_LIBS=$LIBS -+LIBS="-lcap ${pbxlibdir} $LIBS" -+cat >conftest.$ac_ext <<_ACEOF -+/* confdefs.h. */ -+_ACEOF -+cat confdefs.h >>conftest.$ac_ext -+cat >>conftest.$ac_ext <<_ACEOF -+/* end confdefs.h. */ -+ -+/* Override any GCC internal prototype to avoid an error. -+ Use char because int might match the return type of a GCC -+ builtin and then its argument prototype would still apply. */ -+#ifdef __cplusplus -+extern "C" -+#endif -+char cap_from_text (); -+int -+main () -+{ -+return cap_from_text (); -+ ; -+ return 0; -+} -+_ACEOF -+rm -f conftest.$ac_objext conftest$ac_exeext -+if { (ac_try="$ac_link" -+case "(($ac_try" in -+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; -+ *) ac_try_echo=$ac_try;; -+esac -+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 -+ (eval "$ac_link") 2>conftest.er1 -+ ac_status=$? -+ grep -v '^ *+' conftest.er1 >conftest.err -+ rm -f conftest.er1 -+ cat conftest.err >&5 -+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); } && { -+ test -z "$ac_c_werror_flag" || -+ test ! -s conftest.err -+ } && test -s conftest$ac_exeext && -+ $as_test_x conftest$ac_exeext; then -+ ac_cv_lib_cap_cap_from_text=yes -+else -+ echo "$as_me: failed program was:" >&5 -+sed 's/^/| /' conftest.$ac_ext >&5 -+ -+ ac_cv_lib_cap_cap_from_text=no -+fi -+ -+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ -+ conftest$ac_exeext conftest.$ac_ext -+LIBS=$ac_check_lib_save_LIBS -+fi -+{ echo "$as_me:$LINENO: result: $ac_cv_lib_cap_cap_from_text" >&5 -+echo "${ECHO_T}$ac_cv_lib_cap_cap_from_text" >&6; } -+if test $ac_cv_lib_cap_cap_from_text = yes; then -+ AST_CAP_FOUND=yes -+else -+ AST_CAP_FOUND=no -+fi -+ -+ -+ if test "${AST_CAP_FOUND}" = "yes"; then -+ CAP_LIB="-lcap " -+ CAP_HEADER_FOUND="1" -+ if test "x${CAP_DIR}" != "x"; then -+ CAP_LIB="${pbxlibdir} ${CAP_LIB}" -+ CAP_INCLUDE="-I${CAP_DIR}/include" -+ saved_cppflags="${CPPFLAGS}" -+ CPPFLAGS="${CPPFLAGS} -I${CAP_DIR}/include" -+ if test "xsys/capability.h" != "x" ; then -+ as_ac_Header=`echo "ac_cv_header_${CAP_DIR}/include/sys/capability.h" | $as_tr_sh` -+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then -+ { echo "$as_me:$LINENO: checking for ${CAP_DIR}/include/sys/capability.h" >&5 -+echo $ECHO_N "checking for ${CAP_DIR}/include/sys/capability.h... $ECHO_C" >&6; } -+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then -+ echo $ECHO_N "(cached) $ECHO_C" >&6 -+fi -+ac_res=`eval echo '${'$as_ac_Header'}'` -+ { echo "$as_me:$LINENO: result: $ac_res" >&5 -+echo "${ECHO_T}$ac_res" >&6; } -+else -+ # Is the header compilable? -+{ echo "$as_me:$LINENO: checking ${CAP_DIR}/include/sys/capability.h usability" >&5 -+echo $ECHO_N "checking ${CAP_DIR}/include/sys/capability.h usability... $ECHO_C" >&6; } -+cat >conftest.$ac_ext <<_ACEOF -+/* confdefs.h. */ -+_ACEOF -+cat confdefs.h >>conftest.$ac_ext -+cat >>conftest.$ac_ext <<_ACEOF -+/* end confdefs.h. */ -+$ac_includes_default -+#include <${CAP_DIR}/include/sys/capability.h> -+_ACEOF -+rm -f conftest.$ac_objext -+if { (ac_try="$ac_compile" -+case "(($ac_try" in -+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; -+ *) ac_try_echo=$ac_try;; -+esac -+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 -+ (eval "$ac_compile") 2>conftest.er1 -+ ac_status=$? -+ grep -v '^ *+' conftest.er1 >conftest.err -+ rm -f conftest.er1 -+ cat conftest.err >&5 -+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); } && { -+ test -z "$ac_c_werror_flag" || -+ test ! -s conftest.err -+ } && test -s conftest.$ac_objext; then -+ ac_header_compiler=yes -+else -+ echo "$as_me: failed program was:" >&5 -+sed 's/^/| /' conftest.$ac_ext >&5 -+ -+ ac_header_compiler=no -+fi -+ -+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 -+echo "${ECHO_T}$ac_header_compiler" >&6; } -+ -+# Is the header present? -+{ echo "$as_me:$LINENO: checking ${CAP_DIR}/include/sys/capability.h presence" >&5 -+echo $ECHO_N "checking ${CAP_DIR}/include/sys/capability.h presence... $ECHO_C" >&6; } -+cat >conftest.$ac_ext <<_ACEOF -+/* confdefs.h. */ -+_ACEOF -+cat confdefs.h >>conftest.$ac_ext -+cat >>conftest.$ac_ext <<_ACEOF -+/* end confdefs.h. */ -+#include <${CAP_DIR}/include/sys/capability.h> -+_ACEOF -+if { (ac_try="$ac_cpp conftest.$ac_ext" -+case "(($ac_try" in -+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; -+ *) ac_try_echo=$ac_try;; -+esac -+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 -+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 -+ ac_status=$? -+ grep -v '^ *+' conftest.er1 >conftest.err -+ rm -f conftest.er1 -+ cat conftest.err >&5 -+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); } >/dev/null && { -+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || -+ test ! -s conftest.err -+ }; then -+ ac_header_preproc=yes -+else -+ echo "$as_me: failed program was:" >&5 -+sed 's/^/| /' conftest.$ac_ext >&5 -+ -+ ac_header_preproc=no -+fi -+ -+rm -f conftest.err conftest.$ac_ext -+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 -+echo "${ECHO_T}$ac_header_preproc" >&6; } -+ -+# So? What about this header? -+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in -+ yes:no: ) -+ { echo "$as_me:$LINENO: WARNING: ${CAP_DIR}/include/sys/capability.h: accepted by the compiler, rejected by the preprocessor!" >&5 -+echo "$as_me: WARNING: ${CAP_DIR}/include/sys/capability.h: accepted by the compiler, rejected by the preprocessor!" >&2;} -+ { echo "$as_me:$LINENO: WARNING: ${CAP_DIR}/include/sys/capability.h: proceeding with the compiler's result" >&5 -+echo "$as_me: WARNING: ${CAP_DIR}/include/sys/capability.h: proceeding with the compiler's result" >&2;} -+ ac_header_preproc=yes -+ ;; -+ no:yes:* ) -+ { echo "$as_me:$LINENO: WARNING: ${CAP_DIR}/include/sys/capability.h: present but cannot be compiled" >&5 -+echo "$as_me: WARNING: ${CAP_DIR}/include/sys/capability.h: present but cannot be compiled" >&2;} -+ { echo "$as_me:$LINENO: WARNING: ${CAP_DIR}/include/sys/capability.h: check for missing prerequisite headers?" >&5 -+echo "$as_me: WARNING: ${CAP_DIR}/include/sys/capability.h: check for missing prerequisite headers?" >&2;} -+ { echo "$as_me:$LINENO: WARNING: ${CAP_DIR}/include/sys/capability.h: see the Autoconf documentation" >&5 -+echo "$as_me: WARNING: ${CAP_DIR}/include/sys/capability.h: see the Autoconf documentation" >&2;} -+ { echo "$as_me:$LINENO: WARNING: ${CAP_DIR}/include/sys/capability.h: section \"Present But Cannot Be Compiled\"" >&5 -+echo "$as_me: WARNING: ${CAP_DIR}/include/sys/capability.h: section \"Present But Cannot Be Compiled\"" >&2;} -+ { echo "$as_me:$LINENO: WARNING: ${CAP_DIR}/include/sys/capability.h: proceeding with the preprocessor's result" >&5 -+echo "$as_me: WARNING: ${CAP_DIR}/include/sys/capability.h: proceeding with the preprocessor's result" >&2;} -+ { echo "$as_me:$LINENO: WARNING: ${CAP_DIR}/include/sys/capability.h: in the future, the compiler will take precedence" >&5 -+echo "$as_me: WARNING: ${CAP_DIR}/include/sys/capability.h: in the future, the compiler will take precedence" >&2;} -+ ( cat <<\_ASBOX -+## ------------------------------- ## -+## Report this to www.asterisk.org ## -+## ------------------------------- ## -+_ASBOX -+ ) | sed "s/^/$as_me: WARNING: /" >&2 -+ ;; -+esac -+{ echo "$as_me:$LINENO: checking for ${CAP_DIR}/include/sys/capability.h" >&5 -+echo $ECHO_N "checking for ${CAP_DIR}/include/sys/capability.h... $ECHO_C" >&6; } -+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then -+ echo $ECHO_N "(cached) $ECHO_C" >&6 -+else -+ eval "$as_ac_Header=\$ac_header_preproc" -+fi -+ac_res=`eval echo '${'$as_ac_Header'}'` -+ { echo "$as_me:$LINENO: result: $ac_res" >&5 -+echo "${ECHO_T}$ac_res" >&6; } -+ -+fi -+if test `eval echo '${'$as_ac_Header'}'` = yes; then -+ CAP_HEADER_FOUND=1 -+else -+ CAP_HEADER_FOUND=0 -+fi -+ -+ -+ fi -+ CPPFLAGS="${saved_cppflags}" -+ else -+ if test "xsys/capability.h" != "x" ; then -+ if test "${ac_cv_header_sys_capability_h+set}" = set; then -+ { echo "$as_me:$LINENO: checking for sys/capability.h" >&5 -+echo $ECHO_N "checking for sys/capability.h... $ECHO_C" >&6; } -+if test "${ac_cv_header_sys_capability_h+set}" = set; then -+ echo $ECHO_N "(cached) $ECHO_C" >&6 -+fi -+{ echo "$as_me:$LINENO: result: $ac_cv_header_sys_capability_h" >&5 -+echo "${ECHO_T}$ac_cv_header_sys_capability_h" >&6; } -+else -+ # Is the header compilable? -+{ echo "$as_me:$LINENO: checking sys/capability.h usability" >&5 -+echo $ECHO_N "checking sys/capability.h usability... $ECHO_C" >&6; } -+cat >conftest.$ac_ext <<_ACEOF -+/* confdefs.h. */ -+_ACEOF -+cat confdefs.h >>conftest.$ac_ext -+cat >>conftest.$ac_ext <<_ACEOF -+/* end confdefs.h. */ -+$ac_includes_default -+#include -+_ACEOF -+rm -f conftest.$ac_objext -+if { (ac_try="$ac_compile" -+case "(($ac_try" in -+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; -+ *) ac_try_echo=$ac_try;; -+esac -+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 -+ (eval "$ac_compile") 2>conftest.er1 -+ ac_status=$? -+ grep -v '^ *+' conftest.er1 >conftest.err -+ rm -f conftest.er1 -+ cat conftest.err >&5 -+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); } && { -+ test -z "$ac_c_werror_flag" || -+ test ! -s conftest.err -+ } && test -s conftest.$ac_objext; then -+ ac_header_compiler=yes -+else -+ echo "$as_me: failed program was:" >&5 -+sed 's/^/| /' conftest.$ac_ext >&5 -+ -+ ac_header_compiler=no -+fi -+ -+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 -+echo "${ECHO_T}$ac_header_compiler" >&6; } -+ -+# Is the header present? -+{ echo "$as_me:$LINENO: checking sys/capability.h presence" >&5 -+echo $ECHO_N "checking sys/capability.h presence... $ECHO_C" >&6; } -+cat >conftest.$ac_ext <<_ACEOF -+/* confdefs.h. */ -+_ACEOF -+cat confdefs.h >>conftest.$ac_ext -+cat >>conftest.$ac_ext <<_ACEOF -+/* end confdefs.h. */ -+#include -+_ACEOF -+if { (ac_try="$ac_cpp conftest.$ac_ext" -+case "(($ac_try" in -+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; -+ *) ac_try_echo=$ac_try;; -+esac -+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 -+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 -+ ac_status=$? -+ grep -v '^ *+' conftest.er1 >conftest.err -+ rm -f conftest.er1 -+ cat conftest.err >&5 -+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); } >/dev/null && { -+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || -+ test ! -s conftest.err -+ }; then -+ ac_header_preproc=yes -+else -+ echo "$as_me: failed program was:" >&5 -+sed 's/^/| /' conftest.$ac_ext >&5 -+ -+ ac_header_preproc=no -+fi -+ -+rm -f conftest.err conftest.$ac_ext -+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 -+echo "${ECHO_T}$ac_header_preproc" >&6; } -+ -+# So? What about this header? -+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in -+ yes:no: ) -+ { echo "$as_me:$LINENO: WARNING: sys/capability.h: accepted by the compiler, rejected by the preprocessor!" >&5 -+echo "$as_me: WARNING: sys/capability.h: accepted by the compiler, rejected by the preprocessor!" >&2;} -+ { echo "$as_me:$LINENO: WARNING: sys/capability.h: proceeding with the compiler's result" >&5 -+echo "$as_me: WARNING: sys/capability.h: proceeding with the compiler's result" >&2;} -+ ac_header_preproc=yes -+ ;; -+ no:yes:* ) -+ { echo "$as_me:$LINENO: WARNING: sys/capability.h: present but cannot be compiled" >&5 -+echo "$as_me: WARNING: sys/capability.h: present but cannot be compiled" >&2;} -+ { echo "$as_me:$LINENO: WARNING: sys/capability.h: check for missing prerequisite headers?" >&5 -+echo "$as_me: WARNING: sys/capability.h: check for missing prerequisite headers?" >&2;} -+ { echo "$as_me:$LINENO: WARNING: sys/capability.h: see the Autoconf documentation" >&5 -+echo "$as_me: WARNING: sys/capability.h: see the Autoconf documentation" >&2;} -+ { echo "$as_me:$LINENO: WARNING: sys/capability.h: section \"Present But Cannot Be Compiled\"" >&5 -+echo "$as_me: WARNING: sys/capability.h: section \"Present But Cannot Be Compiled\"" >&2;} -+ { echo "$as_me:$LINENO: WARNING: sys/capability.h: proceeding with the preprocessor's result" >&5 -+echo "$as_me: WARNING: sys/capability.h: proceeding with the preprocessor's result" >&2;} -+ { echo "$as_me:$LINENO: WARNING: sys/capability.h: in the future, the compiler will take precedence" >&5 -+echo "$as_me: WARNING: sys/capability.h: in the future, the compiler will take precedence" >&2;} -+ ( cat <<\_ASBOX -+## ------------------------------- ## -+## Report this to www.asterisk.org ## -+## ------------------------------- ## -+_ASBOX -+ ) | sed "s/^/$as_me: WARNING: /" >&2 -+ ;; -+esac -+{ echo "$as_me:$LINENO: checking for sys/capability.h" >&5 -+echo $ECHO_N "checking for sys/capability.h... $ECHO_C" >&6; } -+if test "${ac_cv_header_sys_capability_h+set}" = set; then -+ echo $ECHO_N "(cached) $ECHO_C" >&6 -+else -+ ac_cv_header_sys_capability_h=$ac_header_preproc -+fi -+{ echo "$as_me:$LINENO: result: $ac_cv_header_sys_capability_h" >&5 -+echo "${ECHO_T}$ac_cv_header_sys_capability_h" >&6; } -+ -+fi -+if test $ac_cv_header_sys_capability_h = yes; then -+ CAP_HEADER_FOUND=1 -+else -+ CAP_HEADER_FOUND=0 -+fi -+ -+ -+ fi -+ fi -+ if test "x${CAP_HEADER_FOUND}" = "x0" ; then -+ if test -n "${CAP_MANDATORY}" ; -+ then -+ { echo "$as_me:$LINENO: ***" >&5 -+echo "$as_me: ***" >&6;} -+ { echo "$as_me:$LINENO: *** It appears that you do not have the cap development package installed." >&5 -+echo "$as_me: *** It appears that you do not have the cap development package installed." >&6;} -+ { echo "$as_me:$LINENO: *** Please install it to include ${CAP_DESCRIP} support, or re-run configure" >&5 -+echo "$as_me: *** Please install it to include ${CAP_DESCRIP} support, or re-run configure" >&6;} -+ { echo "$as_me:$LINENO: *** without explicitly specifying --with-${CAP_OPTION}" >&5 -+echo "$as_me: *** without explicitly specifying --with-${CAP_OPTION}" >&6;} -+ exit 1 -+ fi -+ CAP_LIB="" -+ CAP_INCLUDE="" -+ PBX_CAP=0 -+ else -+ PBX_CAP=1 -+ -+cat >>confdefs.h <<_ACEOF -+#define HAVE_CAP 1 -+_ACEOF -+ -+ fi -+ elif test -n "${CAP_MANDATORY}"; -+ then -+ { echo "$as_me:$LINENO: ***" >&5 -+echo "$as_me: ***" >&6;} -+ { echo "$as_me:$LINENO: *** The ${CAP_DESCRIP} installation on this system appears to be broken." >&5 -+echo "$as_me: *** The ${CAP_DESCRIP} installation on this system appears to be broken." >&6;} -+ { echo "$as_me:$LINENO: *** Either correct the installation, or run configure" >&5 -+echo "$as_me: *** Either correct the installation, or run configure" >&6;} -+ { echo "$as_me:$LINENO: *** without explicitly specifying --with-${CAP_OPTION}" >&5 -+echo "$as_me: *** without explicitly specifying --with-${CAP_OPTION}" >&6;} -+ exit 1 -+ fi -+fi -+ -+fi -+ - GSM_INTERNAL="yes" - - GSM_SYSTEM="yes" -@@ -27922,9 +28804,9 @@ if test "${USE_SPANDSP}" != "no"; then - pbxlibdir="" - if test "x${SPANDSP_DIR}" != "x"; then - if test -d ${SPANDSP_DIR}/lib; then -- pbxlibdir="-L${SPANDSP_DIR}/lib" -+ pbxlibdir="-L${SPANDSP_DIR}/lib" - else -- pbxlibdir="-L${SPANDSP_DIR}" -+ pbxlibdir="-L${SPANDSP_DIR}" - fi - fi - { echo "$as_me:$LINENO: checking for fax_init in -lspandsp" >&5 -@@ -28117,7 +28999,12 @@ echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: section \"Present B - echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: proceeding with the preprocessor's result" >&2;} - { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: in the future, the compiler will take precedence" >&5 - echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: in the future, the compiler will take precedence" >&2;} -- -+ ( cat <<\_ASBOX -+## ------------------------------- ## -+## Report this to www.asterisk.org ## -+## ------------------------------- ## -+_ASBOX -+ ) | sed "s/^/$as_me: WARNING: /" >&2 - ;; - esac - { echo "$as_me:$LINENO: checking for ${SPANDSP_DIR}/include/spandsp.h" >&5 -@@ -28254,7 +29141,12 @@ echo "$as_me: WARNING: spandsp.h: section \"Present But Cannot Be Compiled\" - echo "$as_me: WARNING: spandsp.h: proceeding with the preprocessor's result" >&2;} - { echo "$as_me:$LINENO: WARNING: spandsp.h: in the future, the compiler will take precedence" >&5 - echo "$as_me: WARNING: spandsp.h: in the future, the compiler will take precedence" >&2;} -- -+ ( cat <<\_ASBOX -+## ------------------------------- ## -+## Report this to www.asterisk.org ## -+## ------------------------------- ## -+_ASBOX -+ ) | sed "s/^/$as_me: WARNING: /" >&2 - ;; - esac - { echo "$as_me:$LINENO: checking for spandsp.h" >&5 -@@ -34667,10 +35559,18 @@ ALSA_LIB!$ALSA_LIB$ac_delim - ALSA_INCLUDE!$ALSA_INCLUDE$ac_delim - ALSA_DIR!$ALSA_DIR$ac_delim - PBX_ALSA!$PBX_ALSA$ac_delim -+BLUETOOTH_LIB!$BLUETOOTH_LIB$ac_delim -+BLUETOOTH_INCLUDE!$BLUETOOTH_INCLUDE$ac_delim -+BLUETOOTH_DIR!$BLUETOOTH_DIR$ac_delim -+PBX_BLUETOOTH!$PBX_BLUETOOTH$ac_delim - CURL_LIB!$CURL_LIB$ac_delim - CURL_INCLUDE!$CURL_INCLUDE$ac_delim - CURL_DIR!$CURL_DIR$ac_delim - PBX_CURL!$PBX_CURL$ac_delim -+CAP_LIB!$CAP_LIB$ac_delim -+CAP_INCLUDE!$CAP_INCLUDE$ac_delim -+CAP_DIR!$CAP_DIR$ac_delim -+PBX_CAP!$PBX_CAP$ac_delim - CURSES_LIB!$CURSES_LIB$ac_delim - CURSES_INCLUDE!$CURSES_INCLUDE$ac_delim - CURSES_DIR!$CURSES_DIR$ac_delim -@@ -34752,14 +35652,6 @@ PRI_INCLUDE!$PRI_INCLUDE$ac_delim - PRI_DIR!$PRI_DIR$ac_delim - PBX_PRI!$PBX_PRI$ac_delim - PWLIB_LIB!$PWLIB_LIB$ac_delim --PWLIB_INCLUDE!$PWLIB_INCLUDE$ac_delim --PWLIB_DIR!$PWLIB_DIR$ac_delim --PBX_PWLIB!$PBX_PWLIB$ac_delim --OPENH323_LIB!$OPENH323_LIB$ac_delim --OPENH323_INCLUDE!$OPENH323_INCLUDE$ac_delim --OPENH323_DIR!$OPENH323_DIR$ac_delim --PBX_OPENH323!$PBX_OPENH323$ac_delim --QT_LIB!$QT_LIB$ac_delim - _ACEOF - - if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then -@@ -34801,6 +35693,14 @@ _ACEOF - ac_delim='%!_!# ' - for ac_last_try in false false false false false :; do - cat >conf$$subs.sed <<_ACEOF -+PWLIB_INCLUDE!$PWLIB_INCLUDE$ac_delim -+PWLIB_DIR!$PWLIB_DIR$ac_delim -+PBX_PWLIB!$PBX_PWLIB$ac_delim -+OPENH323_LIB!$OPENH323_LIB$ac_delim -+OPENH323_INCLUDE!$OPENH323_INCLUDE$ac_delim -+OPENH323_DIR!$OPENH323_DIR$ac_delim -+PBX_OPENH323!$PBX_OPENH323$ac_delim -+QT_LIB!$QT_LIB$ac_delim - QT_INCLUDE!$QT_INCLUDE$ac_delim - QT_DIR!$QT_DIR$ac_delim - PBX_QT!$PBX_QT$ac_delim -@@ -34890,6 +35790,47 @@ PBX_ZAPTEL_VLDTMF!$PBX_ZAPTEL_VLDTMF$ac_delim - PBX_ZAPTEL_TRANSCODE!$PBX_ZAPTEL_TRANSCODE$ac_delim - EDITLINE_LIB!$EDITLINE_LIB$ac_delim - PBX_H323!$PBX_H323$ac_delim -+_ACEOF -+ -+ if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then -+ break -+ elif $ac_last_try; then -+ { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 -+echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} -+ { (exit 1); exit 1; }; } -+ else -+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! " -+ fi -+done -+ -+ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed` -+if test -n "$ac_eof"; then -+ ac_eof=`echo "$ac_eof" | sort -nru | sed 1q` -+ ac_eof=`expr $ac_eof + 1` -+fi -+ -+cat >>$CONFIG_STATUS <<_ACEOF -+cat >"\$tmp/subs-3.sed" <<\CEOF$ac_eof -+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b -+_ACEOF -+sed ' -+s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g -+s/^/s,@/; s/!/@,|#_!!_#|/ -+:n -+t n -+s/'"$ac_delim"'$/,g/; t -+s/$/\\/; p -+N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n -+' >>$CONFIG_STATUS >$CONFIG_STATUS <<_ACEOF -+CEOF$ac_eof -+_ACEOF -+ -+ -+ac_delim='%!_!# ' -+for ac_last_try in false false false false false :; do -+ cat >conf$$subs.sed <<_ACEOF - PBX_IXJUSER!$PBX_IXJUSER$ac_delim - GTKCONFIG!$GTKCONFIG$ac_delim - PBX_GTK!$PBX_GTK$ac_delim -@@ -34903,7 +35844,7 @@ CURL_CONFIG!$CURL_CONFIG$ac_delim - LTLIBOBJS!$LTLIBOBJS$ac_delim - _ACEOF - -- if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 96; then -+ if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 11; then - break - elif $ac_last_try; then - { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 -@@ -34921,8 +35862,8 @@ if test -n "$ac_eof"; then - fi - - cat >>$CONFIG_STATUS <<_ACEOF --cat >"\$tmp/subs-3.sed" <<\CEOF$ac_eof --/@[a-zA-Z_][a-zA-Z_0-9]*@/!b -+cat >"\$tmp/subs-4.sed" <<\CEOF$ac_eof -+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b end - _ACEOF - sed ' - s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g -@@ -34935,6 +35876,8 @@ N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n - ' >>$CONFIG_STATUS >$CONFIG_STATUS <<_ACEOF -+:end -+s/|#_!!_#|//g - CEOF$ac_eof - _ACEOF - -@@ -35182,7 +36125,7 @@ s&@abs_builddir@&$ac_abs_builddir&;t t - s&@abs_top_builddir@&$ac_abs_top_builddir&;t t - s&@INSTALL@&$ac_INSTALL&;t t - $ac_datarootdir_hack --" $ac_file_inputs | sed -f "$tmp/subs-1.sed" | sed -f "$tmp/subs-2.sed" | sed -f "$tmp/subs-3.sed" | sed 's/|#_!!_#|//g' >$tmp/out -+" $ac_file_inputs | sed -f "$tmp/subs-1.sed" | sed -f "$tmp/subs-2.sed" | sed -f "$tmp/subs-3.sed" | sed -f "$tmp/subs-4.sed" >$tmp/out - - test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && - { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && -diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in -index 47fcad9..3c90d4c 100644 ---- a/include/asterisk/autoconfig.h.in -+++ b/include/asterisk/autoconfig.h.in -@@ -59,9 +59,15 @@ - /* Define to 1 if your GCC C compiler supports the 'unused' attribute. */ - #undef HAVE_ATTRIBUTE_unused - -+/* Define to indicate the ${BLUETOOTH_DESCRIP} library */ -+#undef HAVE_BLUETOOTH -+ - /* Define to 1 if you have the `bzero' function. */ - #undef HAVE_BZERO - -+/* Define to indicate the ${CAP_DESCRIP} library */ -+#undef HAVE_CAP -+ - /* Define to 1 if your system has a working `chown' function. */ - #undef HAVE_CHOWN - --- -1.5.3.6 - diff --git a/asterisk-1.4.16-chanmobile.patch b/asterisk-1.4.16-chanmobile.patch deleted file mode 100644 index bfd67f8..0000000 --- a/asterisk-1.4.16-chanmobile.patch +++ /dev/null @@ -1,2521 +0,0 @@ -From 90c23e041c9c4f215f62f617873766d13c5a9063 Mon Sep 17 00:00:00 2001 -From: Jeffrey C. Ollie -Date: Sun, 18 Nov 2007 22:25:43 -0600 -Subject: [PATCH] Add chan_mobile (backported from asterisk-addons trunk). - ---- - build_tools/menuselect-deps.in | 1 + - channels/Makefile | 2 + - channels/chan_mobile.c | 2126 ++++++++++++++++++++++++++++++++++++++++ - configs/mobile.conf.sample | 60 ++ - configure.ac | 3 + - doc/chan_mobile.txt | 240 +++++ - makeopts.in | 3 + - 7 files changed, 2435 insertions(+), 0 deletions(-) - create mode 100644 channels/chan_mobile.c - create mode 100644 configs/mobile.conf.sample - create mode 100644 doc/chan_mobile.txt - -diff --git a/build_tools/menuselect-deps.in b/build_tools/menuselect-deps.in -index fb10545..4f12adb 100644 ---- a/build_tools/menuselect-deps.in -+++ b/build_tools/menuselect-deps.in -@@ -1,4 +1,5 @@ - ASOUND=@PBX_ALSA@ -+BLUETOOTH=@PBX_BLUETOOTH@ - CURL=@PBX_CURL@ - FREETDS=@PBX_FREETDS@ - GSM=@PBX_GSM@ -diff --git a/channels/Makefile b/channels/Makefile -index f5e96c7..9844128 100644 ---- a/channels/Makefile -+++ b/channels/Makefile -@@ -124,4 +124,6 @@ misdn/isdn_lib.o: ASTCFLAGS+=-Wno-strict-aliasing - - $(if $(filter chan_misdn,$(EMBEDDED_MODS)),modules.link,chan_misdn.so): chan_misdn.o misdn_config.o misdn/isdn_lib.o misdn/isdn_msg_parser.o - -+chan_mobile.so: LIBS+=$(BLUETOOTH_LIB) -+ - chan_vpb.oo: ASTCFLAGS:=$(filter-out -Wdeclaration-after-statement,$(ASTCFLAGS)) -diff --git a/channels/chan_mobile.c b/channels/chan_mobile.c -new file mode 100644 -index 0000000..69c871f ---- /dev/null -+++ b/channels/chan_mobile.c -@@ -0,0 +1,2126 @@ -+/* -+ * Asterisk -- An open source telephony toolkit. -+ * -+ * Copyright (C) 1999 - 2006, Digium, Inc. -+ * -+ * Mark Spencer -+ * -+ * See http://www.asterisk.org for more information about -+ * the Asterisk project. Please do not directly contact -+ * any of the maintainers of this project for assistance; -+ * the project provides a web site, mailing lists and IRC -+ * channels for your use. -+ * -+ * This program is free software, distributed under the terms of -+ * the GNU General Public License Version 2. See the LICENSE file -+ * at the top of the source tree. -+ */ -+ -+/*! \file -+ * -+ * \brief Bluetooth Mobile Device channel driver -+ * -+ * \author Dave Bowerman -+ * -+ * \ingroup channel_drivers -+ */ -+ -+/*** MODULEINFO -+ bluetooth -+ ***/ -+ -+#include -+ -+ASTERISK_FILE_VERSION(__FILE__, "$Revision$") -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define AST_MODULE "chan_mobile" -+ -+#define MBL_CONFIG "mobile.conf" -+ -+#define DEVICE_FRAME_SIZE 48 -+#define DEVICE_FRAME_FORMAT AST_FORMAT_SLINEAR -+#define CHANNEL_FRAME_SIZE 320 -+ -+static int prefformat = DEVICE_FRAME_FORMAT; -+ -+static int discovery_interval = 60; /* The device discovery interval, default 60 seconds. */ -+static pthread_t discovery_thread = AST_PTHREADT_NULL; /* The discovery thread */ -+static sdp_session_t *sdp_session; -+ -+enum mbl_type { -+ MBL_TYPE_PHONE, -+ MBL_TYPE_HEADSET -+}; -+ -+enum mbl_state { -+ MBL_STATE_INIT = 0, -+ MBL_STATE_INIT1, -+ MBL_STATE_INIT2, -+ MBL_STATE_INIT3, -+ MBL_STATE_INIT4, -+ MBL_STATE_INIT5, -+ MBL_STATE_INIT6, -+ MBL_STATE_PREIDLE, -+ MBL_STATE_IDLE, -+ MBL_STATE_DIAL, -+ MBL_STATE_DIAL1, -+ MBL_STATE_OUTGOING, -+ MBL_STATE_RING, -+ MBL_STATE_RING2, -+ MBL_STATE_RING3, -+ MBL_STATE_INCOMING, -+ MBL_STATE_HANGUP, -+ MBL_STATE_INSMS, -+ MBL_STATE_OUTSMS, -+ MBL_STATE_OUTSMS1, -+ MBL_STATE_OUTSMS2 -+}; -+ -+struct adapter_pvt { -+ int dev_id; /* device id */ -+ int hci_socket; /* device descriptor */ -+ char id[31]; /* the 'name' from mobile.conf */ -+ bdaddr_t addr; /* adddress of adapter */ -+ unsigned int inuse:1; /* are we in use ? */ -+ unsigned int alignment_detection:1; /* do alignment detection on this adpater? */ -+ int sco_socket; -+ AST_LIST_ENTRY(adapter_pvt) entry; -+}; -+ -+static AST_LIST_HEAD_STATIC(adapters, adapter_pvt); -+ -+struct mbl_pvt { -+ struct ast_channel *owner; /* Channel we belong to, possibly NULL */ -+ struct ast_frame fr; /* "null" frame */ -+ enum mbl_type type; /* Phone or Headset */ -+ char id[31]; /* The id from mobile.conf */ -+ int group; /* group number for group dialling */ -+ bdaddr_t addr; /* address of device */ -+ struct adapter_pvt *adapter; /* the adapter we use */ -+ char context[AST_MAX_CONTEXT]; /* the context for incoming calls */ -+ char connected; /* is it connected? */ -+ int rfcomm_port; /* rfcomm port number */ -+ int rfcomm_socket; /* rfcomm socket descriptor */ -+ char rfcomm_buf[256]; -+ char io_buf[CHANNEL_FRAME_SIZE + AST_FRIENDLY_OFFSET]; -+ char io_save_buf[DEVICE_FRAME_SIZE]; -+ int io_save_len; -+ int io_pipe[2]; -+ int sco_socket; /* sco socket descriptor */ -+ pthread_t sco_listener_thread; /* inbound sco listener for this device */ -+ enum mbl_state state; /* monitor thread current state */ -+ pthread_t monitor_thread; /* monitor thread handle */ -+ char dial_number[AST_MAX_EXTENSION]; /* number for the monitor thread to dial */ -+ int dial_timeout; -+ char ciev_call_0[4]; /* dynamically built reponse strings */ -+ char ciev_call_1[4]; -+ char ciev_callsetup_0[4]; -+ char ciev_callsetup_1[4]; -+ char ciev_callsetup_2[4]; -+ char ciev_callsetup_3[4]; -+ unsigned int no_callsetup:1; -+ unsigned int has_sms:1; -+ unsigned int sent_answer:1; -+ unsigned int do_alignment_detection:1; -+ unsigned int alignment_detection_triggered:1; -+ unsigned int do_hangup:1; -+ short alignment_samples[4]; -+ int alignment_count; -+ char sms_txt[160]; -+ struct ast_dsp *dsp; -+ struct ast_frame *dsp_fr; -+ int dtmf_skip; -+ int skip_frames; -+ char hangup_count; -+ AST_LIST_ENTRY(mbl_pvt) entry; -+}; -+ -+static AST_LIST_HEAD_STATIC(devices, mbl_pvt); -+ -+/* CLI stuff */ -+static const char show_usage[] = -+"Usage: mobile show devices\n" -+" Shows the state of Bluetooth Cell / Mobile devices.\n"; -+ -+static const char search_usage[] = -+"Usage: mobile search\n" -+" Searches for Bluetooth Cell / Mobile devices in range.\n"; -+ -+static const char rfcomm_usage[] = -+"Usage: mobile rfcomm command\n" -+" Send command to the rfcomm port.\n"; -+ -+static int do_show_devices(int, int, char **); -+static int do_search_devices(int, int, char **); -+static int do_send_rfcomm(int, int, char **); -+ -+static struct ast_cli_entry mbl_cli[] = { -+ {{"mobile", "show", "devices", NULL}, do_show_devices, "Show Bluetooth Cell / Mobile devices", show_usage}, -+ {{"mobile", "search", NULL}, do_search_devices, "Search for Bluetooth Cell / Mobile devices", search_usage}, -+ {{"mobile", "rfcomm", NULL}, do_send_rfcomm, "Send commands to the rfcomm port for debugging", rfcomm_usage}, -+}; -+ -+/* App stuff */ -+static char *app_mblstatus = "MobileStatus"; -+static char *mblstatus_synopsis = "MobileStatus(Device,Variable)"; -+static char *mblstatus_desc = -+"MobileStatus(Device,Variable)\n" -+" Device - Id of mobile device from mobile.conf\n" -+" Variable - Variable to store status in will be 1-3.\n" -+" In order, Disconnected, Connected & Free, Connected & Busy.\n"; -+ -+static char *app_mblsendsms = "MobileSendSMS"; -+static char *mblsendsms_synopsis = "MobileSendSMS(Device,Dest,Message)"; -+static char *mblsendsms_desc = -+"MobileSendSms(Device,Dest,Message)\n" -+" Device - Id of device from mobile.conf\n" -+" Dest - destination\n" -+" Message - text of the message\n"; -+ -+static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num); -+static struct ast_channel *mbl_request(const char *type, int format, void *data, int *cause); -+static int mbl_call(struct ast_channel *ast, char *dest, int timeout); -+static int mbl_hangup(struct ast_channel *ast); -+static int mbl_answer(struct ast_channel *ast); -+static int mbl_digit_begin(struct ast_channel *ast, char digit); -+static int mbl_digit_end(struct ast_channel *ast, char digit, unsigned int duration); -+static struct ast_frame *mbl_read(struct ast_channel *ast); -+static int mbl_write(struct ast_channel *ast, struct ast_frame *frame); -+static int mbl_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); -+static int mbl_devicestate(void *data); -+ -+static void do_alignment_detection(struct mbl_pvt *pvt, char *buf, int buflen); -+ -+static int rfcomm_connect(bdaddr_t src, bdaddr_t dst, int remote_channel); -+static int rfcomm_write(struct mbl_pvt *pvt, char *buf); -+static int rfcomm_read(struct mbl_pvt *pvt, char *buf, char flush, int timeout); -+ -+static int sco_connect(bdaddr_t src, bdaddr_t dst); -+static int sco_write(int s, char *buf, int len); -+static int sco_read(int s, char *buf, int len); -+ -+static void *do_sco_listen(void *data); -+static int sdp_search(char *addr, int profile); -+ -+static const struct ast_channel_tech mbl_tech = { -+ .type = "Mobile", -+ .description = "Bluetooth Mobile Device Channel Driver", -+ .capabilities = AST_FORMAT_SLINEAR, -+ .requester = mbl_request, -+ .call = mbl_call, -+ .hangup = mbl_hangup, -+ .answer = mbl_answer, -+ .send_digit_begin = mbl_digit_begin, -+ .send_digit_end = mbl_digit_end, -+ .read = mbl_read, -+ .write = mbl_write, -+ .fixup = mbl_fixup, -+ .devicestate = mbl_devicestate -+}; -+ -+/* -+ -+ CLI Commands implementation -+ -+*/ -+ -+static int do_show_devices(int fd, int argc, char **argv) -+{ -+ -+ struct mbl_pvt *pvt; -+ char bdaddr[18]; -+ char group[6]; -+ -+ #define FORMAT "%-15.15s %-17.17s %-5.5s %-15.15s %-9.9s %-5.5s %-3.3s\n" -+ -+ ast_cli(fd, FORMAT, "ID", "Address", "Group", "Adapter", "Connected", "State", "SMS"); -+ AST_LIST_TRAVERSE(&devices, pvt, entry) { -+ ba2str(&pvt->addr, bdaddr); -+ snprintf(group, 5, "%d", pvt->group); -+ ast_cli(fd, FORMAT, pvt->id, bdaddr, group, pvt->adapter->id, pvt->connected?"Yes":"No", (pvt->state == MBL_STATE_IDLE)?"Free":(pvt->state < MBL_STATE_IDLE)?"Init":"Busy", (pvt->has_sms)?"Yes":"No"); -+ } -+ -+ return RESULT_SUCCESS; -+ -+} -+ -+static int do_search_devices(int fd, int argc, char **argv) -+{ -+ -+ struct adapter_pvt *adapter; -+ inquiry_info *ii = NULL; -+ int max_rsp, num_rsp; -+ int len, flags; -+ int i, phport, hsport; -+ char addr[19] = {0}; -+ char name[31] = {0}; -+ -+ #define FORMAT2 "%-17.17s %-30.30s %-6.6s %-7.7s %-4.4s\n" -+ #define FORMAT3 "%-17.17s %-30.30s %-6.6s %-7.7s %d\n" -+ -+ /* find a free adapter */ -+ AST_LIST_TRAVERSE(&adapters, adapter, entry) { -+ if (!adapter->inuse) -+ break; -+ } -+ if (!adapter) { -+ ast_cli(fd, "All Bluetooth adapters are in use at this time.\n"); -+ return RESULT_SUCCESS; -+ } -+ -+ len = 8; -+ max_rsp = 255; -+ flags = IREQ_CACHE_FLUSH; -+ -+ ii = alloca(max_rsp * sizeof(inquiry_info)); -+ num_rsp = hci_inquiry(adapter->dev_id, len, max_rsp, NULL, &ii, flags); -+ if (num_rsp > 0) { -+ ast_cli(fd, FORMAT2, "Address", "Name", "Usable", "Type", "Port"); -+ for (i = 0; i < num_rsp; i++) { -+ ba2str(&(ii+i)->bdaddr, addr); -+ name[0] = 0x00; -+ if (hci_read_remote_name(adapter->hci_socket, &(ii+i)->bdaddr, sizeof(name)-1, name, 0) < 0) -+ strcpy(name, "[unknown]"); -+ phport = sdp_search(addr, HANDSFREE_AGW_PROFILE_ID); -+ if (!phport) -+ hsport = sdp_search(addr, HEADSET_PROFILE_ID); -+ else -+ hsport = 0; -+ ast_cli(fd, FORMAT3, addr, name, (phport > 0 || hsport > 0)?"Yes":"No", (phport > 0)?"Phone":"Headset", (phport > 0)?phport:hsport); -+ } -+ } else -+ ast_cli(fd, "No Bluetooth Cell / Mobile devices found.\n"); -+ -+ return RESULT_SUCCESS; -+ -+} -+ -+static int do_send_rfcomm(int fd, int argc, char **argv) -+{ -+ -+ struct mbl_pvt *pvt; -+ char buf[128]; -+ -+ if (argc != 4) { -+ ast_cli(fd, "You must specify the name of the device and command.\n"); -+ return RESULT_SUCCESS; -+ } -+ -+ AST_LIST_TRAVERSE(&devices, pvt, entry) { -+ if (!strcmp(pvt->id, argv[2])) -+ break; -+ } -+ -+ if (!pvt || !pvt->connected) { -+ ast_cli(fd, "Device %s not found.\n", argv[2]); -+ return RESULT_SUCCESS; -+ } -+ -+ sprintf(buf, "%s\r", argv[3]); -+ rfcomm_write(pvt, buf); -+ -+ return RESULT_SUCCESS; -+ -+} -+ -+/* -+ -+ Dialplan applications implementation -+ -+*/ -+ -+static int mbl_status_exec(struct ast_channel *ast, void *data) -+{ -+ -+ struct mbl_pvt *pvt; -+ char *parse; -+ int stat; -+ char status[2]; -+ -+ AST_DECLARE_APP_ARGS(args, -+ AST_APP_ARG(device); -+ AST_APP_ARG(variable); -+ ); -+ -+ if (ast_strlen_zero(data)) -+ return -1; -+ -+ parse = ast_strdupa(data); -+ -+ AST_STANDARD_APP_ARGS(args, parse); -+ -+ if (ast_strlen_zero(args.device) || ast_strlen_zero(args.variable)) -+ return -1; -+ -+ stat = 1; -+ -+ AST_LIST_TRAVERSE(&devices, pvt, entry) { -+ if (!strcmp(pvt->id, args.device)) -+ break; -+ } -+ -+ if (pvt) { -+ if (pvt->connected) -+ stat = 2; -+ if (pvt->owner) -+ stat = 3; -+ } -+ -+ sprintf(status, "%d", stat); -+ pbx_builtin_setvar_helper(ast, args.variable, status); -+ -+ return 0; -+ -+} -+ -+static int mbl_sendsms_exec(struct ast_channel *ast, void *data) -+{ -+ -+ struct mbl_pvt *pvt; -+ char *parse; -+ -+ AST_DECLARE_APP_ARGS(args, -+ AST_APP_ARG(device); -+ AST_APP_ARG(dest); -+ AST_APP_ARG(message); -+ ); -+ -+ if (ast_strlen_zero(data)) -+ return -1; -+ -+ parse = ast_strdupa(data); -+ -+ AST_STANDARD_APP_ARGS(args, parse); -+ -+ if (ast_strlen_zero(args.device)) { -+ ast_log(LOG_ERROR,"NULL device for message -- SMS will not be sent.\n"); -+ return -1; -+ } -+ -+ if (ast_strlen_zero(args.dest)) { -+ ast_log(LOG_ERROR,"NULL destination for message -- SMS will not be sent.\n"); -+ return -1; -+ } -+ -+ if (ast_strlen_zero(args.message)) { -+ ast_log(LOG_ERROR,"NULL Message to be sent -- SMS will not be sent.\n"); -+ return -1; -+ } -+ -+ AST_LIST_TRAVERSE(&devices, pvt, entry) { -+ if (!strcmp(pvt->id, args.device)) -+ break; -+ } -+ -+ if (!pvt) { -+ ast_log(LOG_ERROR,"Bluetooth device %s wasn't found in the list -- SMS will not be sent.\n", args.device); -+ return -1; -+ } -+ -+ if (!pvt->connected) { -+ ast_log(LOG_ERROR,"Bluetooth device %s wasn't connected -- SMS will not be sent.\n", args.device); -+ return -1; -+ } -+ -+ if (!pvt->has_sms) { -+ ast_log(LOG_ERROR,"Bluetooth device %s doesn't handle SMS -- SMS will not be sent.\n", args.device); -+ return -1; -+ } -+ -+ if (pvt->state != MBL_STATE_IDLE) { -+ ast_log(LOG_ERROR,"Bluetooth device %s isn't IDLE -- SMS will not be sent.\n", args.device); -+ return -1; -+ } -+ -+ ast_copy_string(pvt->dial_number, args.dest, sizeof(pvt->dial_number)); -+ ast_copy_string(pvt->sms_txt, args.message, sizeof(pvt->sms_txt)); -+ pvt->state = MBL_STATE_OUTSMS; -+ -+ return 0; -+ -+} -+ -+/* -+ -+ Channel Driver callbacks -+ -+*/ -+ -+static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num) -+{ -+ -+ struct ast_channel *chn; -+ -+ if (pipe(pvt->io_pipe) == -1) { -+ ast_log(LOG_ERROR, "Failed to create io_pipe.\n"); -+ return NULL; -+ } -+ -+ if (pvt->sco_socket != -1) -+ close(pvt->sco_socket); -+ pvt->sco_socket = -1; -+ pvt->io_save_len = 0; -+ pvt->sent_answer = 0; -+ pvt->skip_frames = 0; -+ pvt->alignment_count = 0; -+ pvt->alignment_detection_triggered = 0; -+ if (pvt->adapter->alignment_detection) -+ pvt->do_alignment_detection = 1; -+ else -+ pvt->do_alignment_detection = 0; -+ pvt->do_hangup = 1; -+ chn = ast_channel_alloc(1, state, cid_num, pvt->id, 0, 0, pvt->context, 0, "Mobile/%s-%04lx", pvt->id, ast_random() & 0xffff); -+ if (chn) { -+ chn->tech = &mbl_tech; -+ chn->nativeformats = prefformat; -+ chn->rawreadformat = prefformat; -+ chn->rawwriteformat = prefformat; -+ chn->writeformat = prefformat; -+ chn->readformat = prefformat; -+ chn->tech_pvt = pvt; -+ ast_channel_lock(chn); -+ chn->fds[0] = pvt->io_pipe[0]; -+ ast_channel_unlock(chn); -+ if (state == AST_STATE_RING) -+ chn->rings = 1; -+ ast_string_field_set(chn, language, "en"); -+ pvt->owner = chn; -+ return chn; -+ } -+ -+ return NULL; -+ -+} -+ -+static struct ast_channel *mbl_request(const char *type, int format, void *data, int *cause) -+{ -+ -+ struct ast_channel *chn = NULL; -+ struct mbl_pvt *pvt; -+ char *dest_dev = NULL; -+ char *dest_num = NULL; -+ int oldformat, group; -+ -+ if (!data) { -+ ast_log(LOG_WARNING, "Channel requested with no data\n"); -+ *cause = AST_CAUSE_INCOMPATIBLE_DESTINATION; -+ return NULL; -+ } -+ -+ oldformat = format; -+ format &= (AST_FORMAT_SLINEAR); -+ if (!format) { -+ ast_log(LOG_WARNING, "Asked to get a channel of unsupported format '%d'\n", oldformat); -+ *cause = AST_CAUSE_FACILITY_NOT_IMPLEMENTED; -+ return NULL; -+ } -+ -+ dest_dev = ast_strdupa((char *)data); -+ -+ dest_num = strchr(dest_dev, '/'); -+ if (dest_num) -+ *dest_num++ = 0x00; -+ -+ /* Find requested device and make sure its connected. */ -+ AST_LIST_TRAVERSE(&devices, pvt, entry) { -+ if (((dest_dev[0] == 'g') || (dest_dev[0] == 'G')) && ((dest_dev[1] >= '0') && (dest_dev[1] <= '9'))) { -+ group = atoi(dest_dev+1); -+ if (pvt->group == group) -+ break; -+ } else if (!strcmp(pvt->id, dest_dev)) { -+ break; -+ } -+ } -+ if (!pvt || !pvt->connected || pvt->owner) { -+ ast_log(LOG_WARNING, "Request to call on device %s which is not connected / already in use.\n", dest_dev); -+ *cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL; -+ return NULL; -+ } -+ -+ if ((pvt->type == MBL_TYPE_PHONE) && !dest_num) { -+ ast_log(LOG_WARNING, "Cant determine destination number.\n"); -+ *cause = AST_CAUSE_INCOMPATIBLE_DESTINATION; -+ return NULL; -+ } -+ -+ chn = mbl_new(AST_STATE_DOWN, pvt, NULL); -+ if (!chn) { -+ ast_log(LOG_WARNING, "Unable to allocate channel structure\n"); -+ *cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL; -+ return NULL; -+ } -+ -+ return chn; -+ -+} -+ -+static int mbl_call(struct ast_channel *ast, char *dest, int timeout) -+{ -+ -+ struct mbl_pvt *pvt; -+ char *dest_dev = NULL; -+ char *dest_num = NULL; -+ -+ dest_dev = ast_strdupa((char *)dest); -+ -+ pvt = ast->tech_pvt; -+ -+ if (pvt->type == MBL_TYPE_PHONE) { -+ dest_num = strchr(dest_dev, '/'); -+ if (!dest_num) { -+ ast_log(LOG_WARNING, "Cant determine destination number.\n"); -+ return -1; -+ } -+ *dest_num++ = 0x00; -+ } -+ -+ if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) { -+ ast_log(LOG_WARNING, "mbl_call called on %s, neither down nor reserved\n", ast->name); -+ return -1; -+ } -+ -+ if (option_debug) -+ ast_log(LOG_DEBUG, "Calling %s on %s\n", dest, ast->name); -+ -+ if (pvt->type == MBL_TYPE_PHONE) { -+ ast_copy_string(pvt->dial_number, dest_num, sizeof(pvt->dial_number)); -+ pvt->state = MBL_STATE_DIAL; -+ pvt->dial_timeout = (timeout == 0) ? 30 : timeout; -+ } else { -+ pvt->state = MBL_STATE_RING; -+ } -+ -+ return 0; -+ -+} -+ -+static int mbl_hangup(struct ast_channel *ast) -+{ -+ -+ struct mbl_pvt *pvt; -+ -+ if (!ast->tech_pvt) { -+ ast_log(LOG_WARNING, "Asked to hangup channel not connected\n"); -+ return 0; -+ } -+ pvt = ast->tech_pvt; -+ -+ if (option_debug) -+ ast_log(LOG_DEBUG, "Hanging up device %s.\n", pvt->id); -+ -+ ast_channel_lock(ast); -+ ast->fds[0] = -1; -+ ast_channel_unlock(ast); -+ close(pvt->io_pipe[0]); -+ close(pvt->io_pipe[1]); -+ -+ if (pvt->type == MBL_TYPE_HEADSET && pvt->sco_socket != -1) { -+ close(pvt->sco_socket); -+ pvt->sco_socket = -1; -+ } -+ -+ if ((pvt->state == MBL_STATE_INCOMING || pvt->state == MBL_STATE_OUTGOING || pvt->state == MBL_STATE_DIAL1 || pvt->state == MBL_STATE_RING3) && pvt->type == MBL_TYPE_PHONE) { -+ if (pvt->do_hangup) { -+ rfcomm_write(pvt, "AT+CHUP\r"); -+ } -+ pvt->state = MBL_STATE_HANGUP; -+ pvt->hangup_count = 0; -+ } else -+ pvt->state = MBL_STATE_IDLE; -+ -+ pvt->owner = NULL; -+ ast->tech_pvt = NULL; -+ ast_setstate(ast, AST_STATE_DOWN); -+ -+ return 0; -+ -+} -+ -+static int mbl_answer(struct ast_channel *ast) -+{ -+ -+ struct mbl_pvt *pvt; -+ -+ pvt = ast->tech_pvt; -+ -+ rfcomm_write(pvt, "ATA\r"); -+ -+ ast_setstate(ast, AST_STATE_UP); -+ -+ pvt->sent_answer = 1; -+ -+ return 0; -+ -+} -+ -+static int mbl_digit_begin(struct ast_channel *chan, char digit) -+{ -+ -+ return 0; -+ -+} -+ -+static int mbl_digit_end(struct ast_channel *ast, char digit, unsigned int duration) -+{ -+ -+ struct mbl_pvt *pvt; -+ char buf[11]; -+ -+ pvt = ast->tech_pvt; -+ -+ if (pvt->type == MBL_TYPE_HEADSET) -+ return 0; -+ -+ if (option_debug) -+ ast_log(LOG_DEBUG, "Dialed %c\n", digit); -+ -+ switch(digit) { -+ case '0': -+ case '1': -+ case '2': -+ case '3': -+ case '4': -+ case '5': -+ case '6': -+ case '7': -+ case '8': -+ case '9': -+ case '*': -+ case '#': -+ sprintf(buf, "AT+VTS=%c\r", digit); -+ rfcomm_write(pvt, buf); -+ break; -+ default: -+ ast_log(LOG_WARNING, "Unknown digit '%c'\n", digit); -+ return -1; -+ } -+ -+ return 0; -+ -+} -+ -+static struct ast_frame *mbl_read(struct ast_channel *ast) -+{ -+ -+ struct mbl_pvt *pvt = ast->tech_pvt; -+ struct ast_frame *f; -+ int r; -+ -+ if (option_debug > 1) -+ ast_log(LOG_DEBUG, "*** mbl_read()\n"); -+ -+ if (!pvt->owner) { -+ return &ast_null_frame; -+ } -+ memset(&pvt->fr, 0x00, sizeof(struct ast_frame)); -+ pvt->fr.frametype = AST_FRAME_VOICE; -+ pvt->fr.subclass = DEVICE_FRAME_FORMAT; -+ pvt->fr.datalen = CHANNEL_FRAME_SIZE; -+ pvt->fr.samples = CHANNEL_FRAME_SIZE / 2; -+ pvt->fr.src = "Mobile"; -+ pvt->fr.offset = AST_FRIENDLY_OFFSET; -+ pvt->fr.mallocd = 0; -+ pvt->fr.delivery.tv_sec = 0; -+ pvt->fr.delivery.tv_usec = 0; -+ pvt->fr.data = pvt->io_buf + AST_FRIENDLY_OFFSET; -+ -+ if ((r = read(pvt->io_pipe[0], pvt->fr.data, CHANNEL_FRAME_SIZE)) != CHANNEL_FRAME_SIZE) { -+ if (r == -1) { -+ ast_log(LOG_ERROR, "read error %d\n", errno); -+ return &ast_null_frame; -+ } else { -+ pvt->fr.datalen = r; -+ pvt->fr.samples = r / 2; -+ } -+ } -+ -+ f = ast_dsp_process(0, pvt->dsp, &pvt->fr); -+ if (f && (f->frametype == AST_FRAME_DTMF_END)) { -+ pvt->fr.frametype = AST_FRAME_DTMF_END; -+ pvt->fr.subclass = f->subclass; -+ } -+ -+ return &pvt->fr; -+ -+} -+ -+static int mbl_write(struct ast_channel *ast, struct ast_frame *frame) -+{ -+ -+ struct mbl_pvt *pvt = ast->tech_pvt; -+ int i, r, io_need, num_frames; -+ char *pfr, buf[DEVICE_FRAME_SIZE]; -+ -+ if (option_debug > 1) -+ ast_log(LOG_DEBUG, "*** mbl_write\n"); -+ -+ if (frame->frametype != AST_FRAME_VOICE) { -+ return 0; -+ } -+ -+ io_need = 0; -+ if (pvt->io_save_len > 0) { -+ io_need = DEVICE_FRAME_SIZE - pvt->io_save_len; -+ memcpy(pvt->io_save_buf + pvt->io_save_len, frame->data, io_need); -+ sco_write(pvt->sco_socket, pvt->io_save_buf, DEVICE_FRAME_SIZE); -+ if ((r = sco_read(pvt->sco_socket, buf, DEVICE_FRAME_SIZE))) { -+ if (pvt->do_alignment_detection) -+ do_alignment_detection(pvt, buf, r); -+ sco_write(pvt->io_pipe[1], buf, r); -+ } -+ } -+ -+ num_frames = (frame->datalen - io_need) / DEVICE_FRAME_SIZE; -+ pfr = frame->data + io_need; -+ -+ for (i=0; isco_socket, pfr, DEVICE_FRAME_SIZE); -+ if ((r = sco_read(pvt->sco_socket, buf, DEVICE_FRAME_SIZE))) { -+ if (pvt->do_alignment_detection) -+ do_alignment_detection(pvt, buf, r); -+ sco_write(pvt->io_pipe[1], buf, r); -+ } -+ pfr += DEVICE_FRAME_SIZE; -+ } -+ -+ pvt->io_save_len = (frame->datalen - io_need) - (num_frames * DEVICE_FRAME_SIZE); -+ if (pvt->io_save_len > 0) { -+ memcpy(pvt->io_save_buf, pfr, pvt->io_save_len); -+ } -+ -+ return 0; -+ -+} -+ -+static int mbl_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) -+{ -+ -+ struct mbl_pvt *pvt = oldchan->tech_pvt; -+ -+ if (pvt && pvt->owner == oldchan) -+ pvt->owner = newchan; -+ -+ return 0; -+ -+} -+ -+static int mbl_devicestate(void *data) -+{ -+ -+ char *device; -+ int res = AST_DEVICE_INVALID; -+ struct mbl_pvt *pvt; -+ -+ device = ast_strdupa(S_OR(data, "")); -+ -+ if (option_debug) -+ ast_log(LOG_DEBUG, "Checking device state for device %s\n", device); -+ -+ AST_LIST_TRAVERSE(&devices, pvt, entry) { -+ if (!strcmp(pvt->id, device)) -+ break; -+ } -+ -+ if (pvt) { -+ if (pvt->connected) { -+ if (pvt->owner) -+ res = AST_DEVICE_INUSE; -+ else -+ res = AST_DEVICE_NOT_INUSE; -+ } -+ } -+ -+ return res; -+ -+} -+ -+/* -+ -+ Callback helpers -+ -+*/ -+ -+/* -+ -+ do_alignment_detection() -+ -+ This routine attempts to detect where we get misaligned sco audio data from the bluetooth adaptor. -+ -+ Its enabled by alignmentdetect=yes under the adapter entry in mobile.conf -+ -+ Some adapters suffer a problem where occasionally they will byte shift the audio stream one byte to the right. -+ The result is static or white noise on the inbound (from the adapter) leg of the call. -+ This is characterised by a sudden jump in magnitude of the value of the 16 bit samples. -+ -+ Here we look at the first 4 48 byte frames. We average the absolute values of each sample in the frame, -+ then average the sum of the averages of frames 1, 2, and 3. -+ Frame zero is usually zero. -+ If the end result > 100, and it usually is if we have the problem, set a flag and compensate by shifting the bytes -+ for each subsequent frame during the call. -+ -+ If the result is <= 100 then clear the flag so we dont come back in here... -+ -+ This seems to work OK.... -+ -+*/ -+ -+static void do_alignment_detection(struct mbl_pvt *pvt, char *buf, int buflen) -+{ -+ -+ int i; -+ short a, *s; -+ char *p; -+ -+ if (pvt->alignment_detection_triggered) { -+ for (i=buflen, p=buf+buflen-1; i>0; i--, p--) -+ *p = *(p-1); -+ *(p+1) = 0; -+ return; -+ } -+ -+ if (pvt->alignment_count < 4) { -+ s = (short *)buf; -+ for (i=0, a=0; ialignment_samples[pvt->alignment_count++] = a; -+ return; -+ } -+ -+ if (option_debug) -+ ast_log(LOG_DEBUG, "Alignment Detection result is [%-d %-d %-d %-d]\n", pvt->alignment_samples[0], pvt->alignment_samples[1], pvt->alignment_samples[2], pvt->alignment_samples[3]); -+ -+ a = abs(pvt->alignment_samples[1]) + abs(pvt->alignment_samples[2]) + abs(pvt->alignment_samples[3]); -+ a /= 3; -+ if (a > 100) { -+ pvt->alignment_detection_triggered = 1; -+ if (option_debug) -+ ast_log(LOG_DEBUG, "Alignment Detection Triggered.\n"); -+ } else -+ pvt->do_alignment_detection = 0; -+ -+} -+ -+/* -+ -+ rfcomm helpers -+ -+*/ -+ -+static int rfcomm_connect(bdaddr_t src, bdaddr_t dst, int remote_channel) { -+ -+ struct sockaddr_rc addr; -+ int s; -+ -+ if ((s = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) { -+ if (option_debug) -+ ast_log(LOG_DEBUG, "socket() failed (%d).\n", errno); -+ return -1; -+ } -+ -+ memset(&addr, 0, sizeof(addr)); -+ addr.rc_family = AF_BLUETOOTH; -+ bacpy(&addr.rc_bdaddr, &src); -+ addr.rc_channel = (uint8_t) 1; -+ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { -+ if (option_debug) -+ ast_log(LOG_DEBUG, "bind() failed (%d).\n", errno); -+ close(s); -+ return -1; -+ } -+ -+ memset(&addr, 0, sizeof(addr)); -+ addr.rc_family = AF_BLUETOOTH; -+ bacpy(&addr.rc_bdaddr, &dst); -+ addr.rc_channel = remote_channel; -+ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { -+ if (option_debug) -+ ast_log(LOG_DEBUG, "connect() failed (%d).\n", errno); -+ close(s); -+ return -1; -+ } -+ -+ return s; -+ -+} -+ -+static int rfcomm_write(struct mbl_pvt *pvt, char *buf) -+{ -+ -+ char *p; -+ ssize_t num_write; -+ int len; -+ -+ if (option_debug) -+ ast_log(LOG_DEBUG, "rfcomm_write() (%s) [%s]\n", pvt->id, buf); -+ len = strlen(buf); -+ p = buf; -+ while (len > 0) { -+ if ((num_write = write(pvt->rfcomm_socket, p, len)) == -1) { -+ if (option_debug) -+ ast_log(LOG_DEBUG, "rfcomm_write() error [%d]\n", errno); -+ return 0; -+ } -+ len -= num_write; -+ p += num_write; -+ } -+ -+ return 1; -+ -+} -+ -+/* -+ -+ Here we need to return complete '\r' terminated single responses to the devices monitor thread, or -+ a timeout if nothing is available. -+ The rfcomm connection to the device is asynchronous, so there is no guarantee that responses will -+ be returned in a single read() call. We handle this by buffering the input and returning one response -+ per call, or a timeout if nothing is available. -+ -+*/ -+ -+static int rfcomm_read(struct mbl_pvt *pvt, char *buf, char flush, int timeout) -+{ -+ -+ int sel, rlen, slen; -+ fd_set rfds; -+ struct timeval tv; -+ char *p; -+ -+ if (!flush) { -+ if ((p = strchr(pvt->rfcomm_buf, '\r'))) { -+ *p++ = 0x00; -+ if (*p == '\n') -+ p++; -+ memmove(buf, pvt->rfcomm_buf, strlen(pvt->rfcomm_buf)); -+ *(buf + strlen(pvt->rfcomm_buf)) = 0x00; -+ memmove(pvt->rfcomm_buf, p, strlen(p)); -+ *(pvt->rfcomm_buf+strlen(p)) = 0x00; -+ return 1; -+ } -+ } else { -+ pvt->rfcomm_buf[0] = 0x00; -+ } -+ -+ FD_ZERO(&rfds); -+ FD_SET(pvt->rfcomm_socket, &rfds); -+ -+ tv.tv_sec = timeout; -+ tv.tv_usec = 0; -+ -+ if ((sel = select(pvt->rfcomm_socket + 1, &rfds, NULL, NULL, &tv)) > 0) { -+ if (FD_ISSET(pvt->rfcomm_socket, &rfds)) { -+ slen = strlen(pvt->rfcomm_buf); -+ rlen = read(pvt->rfcomm_socket, pvt->rfcomm_buf + slen, sizeof(pvt->rfcomm_buf) - slen - 1); -+ if (rlen > 0) { -+ pvt->rfcomm_buf[slen+rlen] = 0x00; -+ if ((p = strchr(pvt->rfcomm_buf, '\r'))) { -+ *p++ = 0x00; -+ if (*p == '\n') -+ p++; -+ memmove(buf, pvt->rfcomm_buf, strlen(pvt->rfcomm_buf)); -+ *(buf + strlen(pvt->rfcomm_buf)) = 0x00; -+ memmove(pvt->rfcomm_buf, p, strlen(p)); -+ *(pvt->rfcomm_buf+strlen(p)) = 0x00; -+ return 1; -+ } -+ } else -+ return rlen; -+ } -+ } else if (sel == 0) { /* timeout */ -+ return 0; -+ } -+ -+ return 1; -+ -+} -+ -+/* -+ -+ sco helpers -+ -+*/ -+ -+static int sco_connect(bdaddr_t src, bdaddr_t dst) -+{ -+ -+ struct sockaddr_sco addr; -+ int s; -+ -+ if ((s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) { -+ if (option_debug) -+ ast_log(LOG_DEBUG, "socket() failed (%d).\n", errno); -+ return -1; -+ } -+ -+ memset(&addr, 0, sizeof(addr)); -+ addr.sco_family = AF_BLUETOOTH; -+ bacpy(&addr.sco_bdaddr, &src); -+ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { -+ if (option_debug) -+ ast_log(LOG_DEBUG, "bind() failed (%d).\n", errno); -+ close(s); -+ return -1; -+ } -+ -+ memset(&addr, 0, sizeof(addr)); -+ addr.sco_family = AF_BLUETOOTH; -+ bacpy(&addr.sco_bdaddr, &dst); -+ -+ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { -+ if (option_debug) -+ ast_log(LOG_DEBUG, "sco connect() failed (%d).\n", errno); -+ close(s); -+ return -1; -+ } -+ -+ return s; -+ -+} -+ -+static int sco_write(int s, char *buf, int len) -+{ -+ -+ int r; -+ -+ if (s == -1) { -+ if (option_debug > 1) -+ ast_log(LOG_DEBUG, "sco_write() not ready\n"); -+ return 0; -+ } -+ -+ if (option_debug > 1) -+ ast_log(LOG_DEBUG, "sco_write()\n"); -+ -+ r = write(s, buf, len); -+ if (r == -1) { -+ if (option_debug > 1) -+ ast_log(LOG_DEBUG, "sco write error %d\n", errno); -+ return 0; -+ } -+ -+ return 1; -+ -+} -+ -+static int sco_read(int s, char *buf, int len) -+{ -+ -+ int r; -+ -+ if (s == -1) { -+ if (option_debug > 1) -+ ast_log(LOG_DEBUG, "sco_read() not ready\n"); -+ return 0; -+ } -+ -+ if (option_debug > 1) -+ ast_log(LOG_DEBUG, "sco_read()\n"); -+ -+ r = read(s, buf, len); -+ if (r == -1) { -+ if (option_debug > 1) -+ ast_log(LOG_DEBUG, "sco_read() error %d\n", errno); -+ return 0; -+ } -+ -+ return r; -+ -+} -+ -+/* -+ -+ sdp helpers -+ -+*/ -+ -+static int sdp_search(char *addr, int profile) -+{ -+ -+ sdp_session_t *session = 0; -+ bdaddr_t bdaddr; -+ uuid_t svc_uuid; -+ uint32_t range = 0x0000ffff; -+ sdp_list_t *response_list, *search_list, *attrid_list; -+ int status, port; -+ sdp_list_t *proto_list; -+ sdp_record_t *sdprec; -+ -+ str2ba(addr, &bdaddr); -+ port = 0; -+ session = sdp_connect(BDADDR_ANY, &bdaddr, SDP_RETRY_IF_BUSY); -+ if (!session) { -+ if (option_debug) -+ ast_log(LOG_DEBUG, "sdp_connect() failed on device %s.\n", addr); -+ return 0; -+ } -+ -+ sdp_uuid32_create(&svc_uuid, profile); -+ search_list = sdp_list_append(0, &svc_uuid); -+ attrid_list = sdp_list_append(0, &range); -+ response_list = 0x00; -+ status = sdp_service_search_attr_req(session, search_list, SDP_ATTR_REQ_RANGE, attrid_list, &response_list); -+ if (status == 0) { -+ if (response_list) { -+ sdprec = (sdp_record_t *) response_list->data; -+ proto_list = 0x00; -+ if (sdp_get_access_protos(sdprec, &proto_list) == 0) { -+ port = sdp_get_proto_port(proto_list, RFCOMM_UUID); -+ sdp_list_free(proto_list, 0); -+ } -+ sdp_record_free(sdprec); -+ sdp_list_free(response_list, 0); -+ } else -+ if (option_debug) -+ ast_log(LOG_DEBUG, "No responses returned for device %s.\n", addr); -+ } else -+ if (option_debug) -+ ast_log(LOG_DEBUG, "sdp_service_search_attr_req() failed on device %s.\n", addr); -+ -+ sdp_list_free(search_list, 0); -+ sdp_list_free(attrid_list, 0); -+ sdp_close(session); -+ -+ return port; -+ -+} -+ -+static sdp_session_t *sdp_register(void) -+{ -+ -+ uint32_t service_uuid_int[] = {0, 0, 0, GENERIC_AUDIO_SVCLASS_ID}; -+ uint8_t rfcomm_channel = 1; -+ const char *service_name = "Asterisk PABX"; -+ const char *service_dsc = "Asterisk PABX"; -+ const char *service_prov = "Asterisk"; -+ -+ uuid_t root_uuid, l2cap_uuid, rfcomm_uuid, svc_uuid, svc_class1_uuid, svc_class2_uuid; -+ sdp_list_t *l2cap_list = 0, *rfcomm_list = 0, *root_list = 0, *proto_list = 0, *access_proto_list = 0, *svc_uuid_list = 0; -+ sdp_data_t *channel = 0; -+ -+ int err = 0; -+ sdp_session_t *session = 0; -+ -+ sdp_record_t *record = sdp_record_alloc(); -+ -+ sdp_uuid128_create(&svc_uuid, &service_uuid_int); -+ sdp_set_service_id(record, svc_uuid); -+ -+ sdp_uuid32_create(&svc_class1_uuid, GENERIC_AUDIO_SVCLASS_ID); -+ sdp_uuid32_create(&svc_class2_uuid, HEADSET_PROFILE_ID); -+ -+ svc_uuid_list = sdp_list_append(0, &svc_class1_uuid); -+ svc_uuid_list = sdp_list_append(svc_uuid_list, &svc_class2_uuid); -+ sdp_set_service_classes(record, svc_uuid_list); -+ -+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); -+ root_list = sdp_list_append(0, &root_uuid); -+ sdp_set_browse_groups( record, root_list ); -+ -+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); -+ l2cap_list = sdp_list_append(0, &l2cap_uuid); -+ proto_list = sdp_list_append(0, l2cap_list); -+ -+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); -+ channel = sdp_data_alloc(SDP_UINT8, &rfcomm_channel); -+ rfcomm_list = sdp_list_append(0, &rfcomm_uuid); -+ sdp_list_append(rfcomm_list, channel); -+ sdp_list_append(proto_list, rfcomm_list); -+ -+ access_proto_list = sdp_list_append(0, proto_list); -+ sdp_set_access_protos(record, access_proto_list); -+ -+ sdp_set_info_attr(record, service_name, service_prov, service_dsc); -+ -+ if (!(session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY))) -+ ast_log(LOG_WARNING, "Failed to connect sdp and create session.\n"); -+ else -+ err = sdp_record_register(session, record, 0); -+ -+ sdp_data_free(channel); -+ sdp_list_free(rfcomm_list, 0); -+ sdp_list_free(root_list, 0); -+ sdp_list_free(access_proto_list, 0); -+ sdp_list_free(svc_uuid_list, 0); -+ -+ return session; -+ -+} -+ -+/* -+ -+ Thread routines -+ -+*/ -+ -+static void *do_monitor_phone(void *data) -+{ -+ -+ struct mbl_pvt *pvt = (struct mbl_pvt *)data; -+ struct ast_channel *chn; -+ char monitor = 1; -+ char buf[256]; -+ char cid_num[AST_MAX_EXTENSION], *pcids, *pcide; -+ int s, t, i, smsi; -+ int group, group2; -+ int callp = 0, callsetupp; -+ char brsf, nsmode, *p, *p1; -+ char sms_src[13]; -+ char sms_txt[160]; -+ -+ brsf = nsmode = 0; -+ -+ if (!rfcomm_write(pvt, "AT+BRSF=4\r")) -+ monitor = 0; -+ -+ while (monitor) { -+ -+ if (pvt->state == MBL_STATE_DIAL1) -+ t = pvt->dial_timeout; -+ else if (pvt->state == MBL_STATE_HANGUP) -+ t = 2; -+ else if (pvt->state == MBL_STATE_OUTSMS1) -+ t = 2; -+ else if (pvt->state == MBL_STATE_OUTSMS2) -+ t = 10; -+ else -+ t = 1; -+ -+ s = rfcomm_read(pvt, buf, 0, t); -+ -+ if ((s > 0) && (buf[0] != 0x0) && (buf[0] != '\r')) { -+ if (option_debug) -+ ast_log(LOG_DEBUG, "rfcomm_read() (%s) [%s]\n", pvt->id, buf); -+ switch (pvt->state) { -+ case MBL_STATE_INIT: -+ if (strstr(buf, "+BRSF:")) { -+ brsf = 1; -+ } else if (strstr(buf, "ERROR") && !nsmode) { /* Hmmm, Non-Standard Phone, just continue */ -+ rfcomm_write(pvt, "AT+CIND=?\r"); -+ pvt->state++; -+ nsmode = 1; -+ } else if (strstr(buf, "OK") && brsf) { -+ rfcomm_write(pvt, "AT+CIND=?\r"); -+ pvt->state++; -+ } -+ break; -+ case MBL_STATE_INIT1: -+ if (strstr(buf, "+CIND:")) { -+ group = callp = callsetupp = 0; -+ group2 = 1; -+ for (i=0; iciev_call_0, "%d,0", callp); -+ sprintf(pvt->ciev_call_1, "%d,1", callp); -+ sprintf(pvt->ciev_callsetup_0, "%d,0", callsetupp); -+ sprintf(pvt->ciev_callsetup_1, "%d,1", callsetupp); -+ sprintf(pvt->ciev_callsetup_2, "%d,2", callsetupp); -+ sprintf(pvt->ciev_callsetup_3, "%d,3", callsetupp); -+ if (callsetupp == 0) /* This phone has no call setup indication!! ... */ -+ pvt->no_callsetup = 1; -+ if (option_debug) -+ ast_log(LOG_DEBUG, "CIEV_CALL=%d CIEV_CALLSETUP=%d\n", callp, callsetupp); -+ } -+ if (strstr(buf, "OK")) { -+ rfcomm_write(pvt, "AT+CIND?\r"); -+ pvt->state++; -+ } -+ break; -+ case MBL_STATE_INIT2: -+ if ((p = strstr(buf, "+CIND:"))) { -+ p += 5; -+ if (*(p+(callp*2)) == '1') { -+ ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s has a call in progress - delaying connection.\n", pvt->id); -+ monitor = 0; -+ } -+ } else if (strstr(buf, "OK")) { -+ rfcomm_write(pvt, "AT+CMER=3,0,0,1\r"); -+ pvt->state++; -+ } -+ break; -+ case MBL_STATE_INIT3: -+ if (strstr(buf, "OK")) { -+ rfcomm_write(pvt, "AT+CLIP=1\r"); -+ pvt->state++; -+ } -+ break; -+ case MBL_STATE_INIT4: -+ if (strstr(buf, "OK")) { -+ rfcomm_write(pvt, "AT+CMGF=1\r"); -+ pvt->state++; -+ } -+ break; -+ case MBL_STATE_INIT5: -+ if (strstr(buf, "ERROR")) { /* No SMS Support ! */ -+ pvt->state = MBL_STATE_PREIDLE; -+ } else if (strstr(buf, "OK")) { -+ rfcomm_write(pvt, "AT+CNMI=2,1,0,1,0\r"); -+ pvt->state++; -+ } -+ break; -+ case MBL_STATE_INIT6: -+ if (strstr(buf, "OK")) { /* We have SMS Support */ -+ pvt->has_sms = 1; -+ pvt->state = MBL_STATE_PREIDLE; -+ } else if (strstr(buf, "ERROR")) { -+ pvt->has_sms = 0; -+ pvt->state = MBL_STATE_PREIDLE; -+ } -+ break; -+ case MBL_STATE_PREIDLE: /* Nothing handled here, wait for timeout, then off we go... */ -+ break; -+ case MBL_STATE_IDLE: -+ if (option_debug) -+ ast_log(LOG_DEBUG, "Device %s [%s]\n", pvt->id, buf); -+ if (strstr(buf, "RING")) { -+ pvt->state = MBL_STATE_RING; -+ } else if (strstr(buf, "+CIEV:")) { -+ if (strstr(buf, pvt->ciev_callsetup_3)) { /* User has dialed out on handset */ -+ monitor = 0; /* We disconnect now, as he is */ -+ } /* probably leaving BT range... */ -+ } -+ break; -+ case MBL_STATE_DIAL: /* Nothing handled here, we need to wait for a timeout */ -+ break; -+ case MBL_STATE_DIAL1: -+ if (strstr(buf, "OK")) { -+ if (pvt->no_callsetup) { -+ ast_queue_control(pvt->owner, AST_CONTROL_ANSWER); -+ } else { -+ ast_setstate(pvt->owner, AST_STATE_RINGING); -+ } -+ pvt->state = MBL_STATE_OUTGOING; -+ } -+ break; -+ case MBL_STATE_OUTGOING: -+ if (strstr(buf, "+CIEV")) { -+ if (strstr(buf, pvt->ciev_call_0)) { /* call was hung up */ -+ pvt->do_hangup = 0; -+ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP); -+ } else if (strstr(buf, pvt->ciev_callsetup_3)) { /* b-party ringing */ -+ ast_queue_control(pvt->owner, AST_CONTROL_RINGING); -+ } else if (strstr(buf, pvt->ciev_call_1) && !pvt->no_callsetup) { /* b-party answer */ -+ ast_queue_control(pvt->owner, AST_CONTROL_ANSWER); -+ } -+ } -+ break; -+ case MBL_STATE_RING: -+ cid_num[0] = 0x00; -+ if ((pcids = strstr(buf, "+CLIP:"))) { -+ if ((pcids = strchr(pcids, '"'))) { -+ if ((pcide = strchr(pcids+1, '"'))) { -+ strncpy(cid_num, pcids+1, pcide - pcids - 1); -+ cid_num[pcide - pcids - 1] = 0x00; -+ } -+ } -+ chn = mbl_new(AST_STATE_RING, pvt, cid_num); -+ if (chn) { -+ if (ast_pbx_start(chn)) { -+ ast_log(LOG_ERROR, "Unable to start pbx on incoming call.\n"); -+ ast_hangup(chn); -+ } else -+ pvt->state = MBL_STATE_RING3; -+ } else { -+ ast_log(LOG_ERROR, "Unable to allocate channel for incoming call.\n"); -+ rfcomm_write(pvt, "AT+CHUP\r"); -+ pvt->state = MBL_STATE_IDLE; -+ } -+ } -+ break; -+ case MBL_STATE_RING2: -+ chn = mbl_new(AST_STATE_RING, pvt, cid_num); -+ if (chn) { -+ if (ast_pbx_start(chn)) { -+ ast_log(LOG_ERROR, "Unable to start pbx on incoming call.\n"); -+ ast_hangup(chn); -+ } else -+ pvt->state = MBL_STATE_RING3; -+ } else { -+ ast_log(LOG_ERROR, "Unable to allocate channel for incoming call.\n"); -+ rfcomm_write(pvt, "AT+CHUP\r"); -+ pvt->state = MBL_STATE_IDLE; -+ } -+ break; -+ case MBL_STATE_RING3: -+ if (strstr(buf, "+CIEV")) { -+ if (strstr(buf, pvt->ciev_call_1)) { -+ if (pvt->sent_answer) { /* We answered */ -+ pvt->state = MBL_STATE_INCOMING; -+ } else { /* User answered on handset!, disconnect */ -+ pvt->state = MBL_STATE_IDLE; -+ if (pvt->sco_socket > -1) -+ close(pvt->sco_socket); -+ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP); -+ } -+ } -+ if ((strstr(buf, pvt->ciev_callsetup_0) || strstr(buf, pvt->ciev_call_0))) { /* Caller disconnected */ -+ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP); -+ } -+ } -+ break; -+ case MBL_STATE_INCOMING: -+ if (strstr(buf, "+CIEV")) { -+ if (strstr(buf, pvt->ciev_call_0)) { -+ pvt->do_hangup = 0; -+ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP); -+ } -+ } -+ break; -+ case MBL_STATE_HANGUP: -+ if (strstr(buf, "OK") || strstr(buf, pvt->ciev_call_0)) { -+ close(pvt->sco_socket); -+ pvt->sco_socket = -1; -+ pvt->state = MBL_STATE_IDLE; -+ } -+ break; -+ case MBL_STATE_INSMS: -+ if (strstr(buf, "+CMGR:")) { -+ memset(sms_src, 0x00, sizeof(sms_src)); -+ if ((p = strchr(buf, ','))) { -+ if (*(++p) == '"') -+ p++; -+ if ((p1 = strchr(p, ','))) { -+ if (*(--p1) == '"') -+ p1--; -+ memset(sms_src, 0x00, sizeof(sms_src)); -+ strncpy(sms_src, p, p1 - p + 1); -+ } -+ } -+ } else if (strstr(buf, "OK")) { -+ chn = mbl_new(AST_STATE_DOWN, pvt, NULL); -+ strcpy(chn->exten, "sms"); -+ pbx_builtin_setvar_helper(chn, "SMSSRC", sms_src); -+ pbx_builtin_setvar_helper(chn, "SMSTXT", sms_txt); -+ if (ast_pbx_start(chn)) -+ ast_log(LOG_ERROR, "Unable to start pbx on incoming sms.\n"); -+ pvt->state = MBL_STATE_IDLE; -+ } else { -+ ast_copy_string(sms_txt, buf, sizeof(sms_txt)); -+ } -+ break; -+ case MBL_STATE_OUTSMS: -+ break; -+ case MBL_STATE_OUTSMS1: -+ break; -+ case MBL_STATE_OUTSMS2: -+ if (strstr(buf, "OK")) { -+ pvt->state = MBL_STATE_IDLE; -+ } -+ break; -+ } -+ /* Unsolicited responses */ -+ -+ if (strstr(buf, "+CMTI:")) { /* SMS Incoming... */ -+ if ((p = strchr(buf, ','))) { -+ p++; -+ smsi = atoi(p); -+ if (smsi > 0) { -+ sprintf(buf, "AT+CMGR=%d\r", smsi); -+ rfcomm_write(pvt, buf); -+ pvt->state = MBL_STATE_INSMS; -+ } -+ } -+ } -+ -+ } else if (s == 0) { /* Timeouts */ -+ if (pvt->state == MBL_STATE_INIT2) { /* Some devices dont respond to AT+CIND? properly. RIM Blackberry 4 example */ -+ pvt->state++; -+ rfcomm_write(pvt, "AT+CMER=3,0,0,1\r"); -+ } else if (pvt->state == MBL_STATE_INIT3) { /* Some devices dont respond to AT+CMER=3,0,0,1 properly. VK 2020 for example */ -+ pvt->state++; -+ rfcomm_write(pvt, "AT+CLIP=1\r"); -+ } else if (pvt->state == MBL_STATE_PREIDLE) { -+ pvt->connected = 1; -+ ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s initialised and ready.\n", pvt->id); -+ pvt->state = MBL_STATE_IDLE; -+ } else if (pvt->state == MBL_STATE_DIAL) { -+ sprintf(buf, "ATD%s;\r", pvt->dial_number); -+ if (!rfcomm_write(pvt, buf)) { -+ ast_log(LOG_ERROR, "Dial failed on %s state %d\n", pvt->owner->name, pvt->state); -+ ast_queue_control(pvt->owner, AST_CONTROL_CONGESTION); -+ pvt->state = MBL_STATE_IDLE; -+ } else { -+ pvt->state = MBL_STATE_DIAL1; -+ } -+ } else if (pvt->state == MBL_STATE_DIAL1) { -+ ast_log(LOG_ERROR, "Dial failed on %s state %d\n", pvt->owner->name, pvt->state); -+ ast_queue_control(pvt->owner, AST_CONTROL_CONGESTION); -+ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP); -+ pvt->state = MBL_STATE_IDLE; -+ } else if (pvt->state == MBL_STATE_RING) { /* No CLIP?, bump it */ -+ pvt->state = MBL_STATE_RING2; -+ } else if (pvt->state == MBL_STATE_HANGUP) { -+ if (pvt->do_hangup) { -+ if (pvt->hangup_count == 6) { -+ if (option_debug) -+ ast_log(LOG_DEBUG, "Device %s failed to hangup after 6 tries, disconnecting.\n", pvt->id); -+ monitor = 0; -+ } -+ rfcomm_write(pvt, "AT+CHUP\r"); -+ pvt->hangup_count++; -+ } else -+ pvt->state = MBL_STATE_IDLE; -+ } else if (pvt->state == MBL_STATE_OUTSMS) { -+ sprintf(buf, "AT+CMGS=\"%s\"\r", pvt->dial_number); -+ rfcomm_write(pvt, buf); -+ pvt->state = MBL_STATE_OUTSMS1; -+ } else if (pvt->state == MBL_STATE_OUTSMS1) { -+ if (pvt->rfcomm_buf[0] == '>') { -+ snprintf(buf, sizeof(buf), "%s%c", pvt->sms_txt, 0x1a); -+ rfcomm_write(pvt, buf); -+ pvt->state = MBL_STATE_OUTSMS2; -+ } else { -+ ast_log(LOG_ERROR, "Failed to send SMS to %s on device %s\n", pvt->dial_number, pvt->id); -+ pvt->state = MBL_STATE_IDLE; -+ } -+ } else if (pvt->state == MBL_STATE_OUTSMS2) { -+ ast_log(LOG_ERROR, "Failed to send SMS to %s on device %s\n", pvt->dial_number, pvt->id); -+ pvt->state = MBL_STATE_IDLE; -+ } -+ } else if (s == -1) { -+ if (option_verbose > 2) -+ ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s has disconnected, reason (%d).\n", pvt->id, errno); -+ monitor = 0; -+ } -+ -+ } -+ -+ if (pvt->rfcomm_socket > -1) -+ close(pvt->rfcomm_socket); -+ if (pvt->sco_socket > -1) -+ close(pvt->sco_socket); -+ pvt->sco_socket = -1; -+ pvt->connected = 0; -+ pvt->monitor_thread = AST_PTHREADT_NULL; -+ -+ pthread_cancel(pvt->sco_listener_thread); -+ pthread_join(pvt->sco_listener_thread, NULL); -+ pvt->sco_listener_thread = AST_PTHREADT_NULL; -+ -+ close(pvt->adapter->sco_socket); -+ -+ manager_event(EVENT_FLAG_SYSTEM, "MobileStatus", "Status: Disconnect\r\nDevice: %s\r\n", pvt->id); -+ -+ pvt->adapter->inuse = 0; -+ -+ return NULL; -+ -+} -+ -+static void *do_monitor_headset(void *data) -+{ -+ -+ struct mbl_pvt *pvt = (struct mbl_pvt *)data; -+ char monitor = 1; -+ char buf[256]; -+ int s, t; -+ -+ pvt->state = MBL_STATE_PREIDLE; -+ -+ while (monitor) { -+ -+ if (pvt->state == MBL_STATE_RING2) -+ t = 2; -+ else -+ t = 1; -+ s = rfcomm_read(pvt, buf, 0, t); -+ -+ if ((s > 0) && (buf[0] != 0x0) && (buf[0] != '\r')) { -+ if (option_debug) -+ ast_log(LOG_DEBUG, "rfcomm_read() (%s) [%s]\n", pvt->id, buf); -+ switch (pvt->state) { -+ case MBL_STATE_RING2: -+ if (strstr(buf, "AT+CKPD=")) { -+ ast_queue_control(pvt->owner, AST_CONTROL_ANSWER); -+ pvt->state = MBL_STATE_INCOMING; -+ rfcomm_write(pvt, "\r\n+VGS=13\r\n"); -+ rfcomm_write(pvt, "\r\n+VGM=13\r\n"); -+ } -+ break; -+ case MBL_STATE_INCOMING: -+ if (strstr(buf, "AT+CKPD=")) { -+ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP); -+ } -+ break; -+ default: -+ break; -+ } -+ if (strstr(buf, "AT+VGS=")) { -+ rfcomm_write(pvt, "\r\nOK\r\n"); -+ } else if (strstr(buf, "AT+VGM=")) { -+ rfcomm_write(pvt, "\r\nOK\r\n"); -+ } -+ } else if (s == 0) { /* Timeouts */ -+ if (pvt->state == MBL_STATE_PREIDLE) { -+ pvt->connected = 1; -+ ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s initialised and ready.\n", pvt->id); -+ pvt->state = MBL_STATE_IDLE; -+ } else if (pvt->state == MBL_STATE_RING) { -+ pvt->sco_socket = sco_connect(pvt->adapter->addr, pvt->addr); -+ if (pvt->sco_socket > -1) { -+ ast_setstate(pvt->owner, AST_STATE_RINGING); -+ ast_queue_control(pvt->owner, AST_CONTROL_RINGING); -+ pvt->state = MBL_STATE_RING2; -+ } else { -+ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP); -+ } -+ } else if (pvt->state == MBL_STATE_RING2) { -+ rfcomm_write(pvt, "\r\nRING\r\n"); -+ } -+ } else if (s == -1) { -+ if (option_verbose > 2) -+ ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s has disconnected, reason (%d).\n", pvt->id, errno); -+ monitor = 0; -+ } -+ -+ } -+ -+ if (pvt->rfcomm_socket > -1) -+ close(pvt->rfcomm_socket); -+ if (pvt->sco_socket > -1) -+ close(pvt->sco_socket); -+ pvt->sco_socket = -1; -+ pvt->connected = 0; -+ pvt->monitor_thread = AST_PTHREADT_NULL; -+ -+ manager_event(EVENT_FLAG_SYSTEM, "MobileStatus", "Status: Disconnect\r\nDevice: %s\r\n", pvt->id); -+ -+ pvt->adapter->inuse = 0; -+ -+ return NULL; -+ -+} -+ -+static int start_monitor(struct mbl_pvt *pvt) -+{ -+ -+ if (pvt->type == MBL_TYPE_PHONE) { -+ if (ast_pthread_create_background(&pvt->monitor_thread, NULL, do_monitor_phone, pvt) < 0) { -+ pvt->monitor_thread = AST_PTHREADT_NULL; -+ return 0; -+ } -+ /* we are a phone, so spin the sco listener on the adapter as well */ -+ if (ast_pthread_create_background(&pvt->sco_listener_thread, NULL, do_sco_listen, pvt->adapter) < 0) { -+ ast_log(LOG_ERROR, "Unable to create sco listener thread for device %s.\n", pvt->id); -+ } -+ -+ } else { -+ if (ast_pthread_create_background(&pvt->monitor_thread, NULL, do_monitor_headset, pvt) < 0) { -+ pvt->monitor_thread = AST_PTHREADT_NULL; -+ return 0; -+ } -+ } -+ -+ return 1; -+ -+} -+ -+static void *do_discovery(void *data) -+{ -+ -+ struct adapter_pvt *adapter; -+ struct mbl_pvt *pvt; -+ -+ for (;;) { -+ AST_LIST_TRAVERSE(&adapters, adapter, entry) { -+ if (!adapter->inuse) { -+ AST_LIST_TRAVERSE(&devices, pvt, entry) { -+ if (!adapter->inuse && !pvt->connected && !strcmp(adapter->id, pvt->adapter->id)) { -+ if ((pvt->rfcomm_socket = rfcomm_connect(adapter->addr, pvt->addr, pvt->rfcomm_port)) > -1) { -+ pvt->state = 0; -+ if (start_monitor(pvt)) { -+ pvt->connected = 1; -+ adapter->inuse = 1; -+ manager_event(EVENT_FLAG_SYSTEM, "MobileStatus", "Status: Connect\r\nDevice: %s\r\n", pvt->id); -+ if (option_verbose > 2) -+ ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s has connected.\n", pvt->id); -+ } -+ } -+ } -+ } -+ } -+ } -+ /* Go to sleep */ -+ sleep(discovery_interval); -+ } -+ -+ return NULL; -+} -+ -+static void *do_sco_listen(void *data) -+{ -+ -+ int ns; -+ struct sockaddr_sco addr; -+ char saddr[18]; -+ struct sco_options so; -+ socklen_t len; -+ int opt = 1; -+ socklen_t addrlen; -+ struct mbl_pvt *pvt; -+ struct adapter_pvt *adapter = (struct adapter_pvt *) data; -+ -+ if ((adapter->sco_socket = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) { -+ ast_log(LOG_ERROR, "Unable to create sco listener socket.\n"); -+ return NULL; -+ } -+ memset(&addr, 0, sizeof(addr)); -+ addr.sco_family = AF_BLUETOOTH; -+ bacpy(&addr.sco_bdaddr, &adapter->addr); -+ if (bind(adapter->sco_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) { -+ ast_log(LOG_ERROR, "Unable to bind sco listener socket. (%d)\n", errno); -+ close(adapter->sco_socket); -+ return NULL; -+ } -+ if (setsockopt(adapter->sco_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) { -+ ast_log(LOG_ERROR, "Unable to setsockopt sco listener socket.\n"); -+ close(adapter->sco_socket); -+ return NULL; -+ } -+ if (listen(adapter->sco_socket, 5) < 0) { -+ ast_log(LOG_ERROR, "Unable to listen sco listener socket.\n"); -+ close(adapter->sco_socket); -+ return NULL; -+ } -+ while (1) { -+ if (option_debug) -+ ast_log(LOG_DEBUG, "About to accept() socket.\n"); -+ addrlen = sizeof(struct sockaddr_sco); -+ if ((ns = accept(adapter->sco_socket, (struct sockaddr *)&addr, &addrlen)) > -1) { -+ if (option_debug) -+ ast_log(LOG_DEBUG, "accept()ed socket.\n"); -+ len = sizeof(so); -+ getsockopt(ns, SOL_SCO, SCO_OPTIONS, &so, &len); -+ -+ ba2str(&addr.sco_bdaddr, saddr); -+ if (option_debug) -+ ast_log(LOG_DEBUG, "Incoming Audio Connection from device %s MTU is %d\n", saddr, so.mtu); -+ -+ pvt = NULL; -+ AST_LIST_TRAVERSE(&devices, pvt, entry) { -+ if (!bacmp(&pvt->addr, &addr.sco_bdaddr)) -+ break; -+ } -+ if (pvt) { -+ if (pvt->sco_socket != -1) -+ close(pvt->sco_socket); -+ pvt->sco_socket = ns; -+ } else -+ if (option_debug) -+ ast_log(LOG_DEBUG, "Could not find device for incoming Audio Connection.\n"); -+ } else { -+ ast_log(LOG_ERROR, "accept() failed %d\n", errno); -+ } -+ } -+ -+ return NULL; -+ -+} -+ -+/* -+ -+ Module -+ -+*/ -+ -+static int mbl_load_config(void) -+{ -+ -+ struct ast_config *cfg = NULL; -+ char *cat = NULL; -+ struct ast_variable *var; -+ const char *id, *address, *useadapter, *port, *context, *type, *skip, *group, *master, *nocallsetup, *aligndetect; -+ struct mbl_pvt *pvt; -+ struct adapter_pvt *adapter; -+ uint16_t vs; -+ struct hci_dev_req dr; -+ char nadapters = 0; -+ -+ cfg = ast_config_load(MBL_CONFIG); -+ if (!cfg) -+ return 0; -+ -+ for (var = ast_variable_browse(cfg, "general"); var; var = var->next) { -+ if (!strcasecmp(var->name, "interval")) -+ discovery_interval = atoi(var->value); -+ } -+ -+ /* load adapters first */ -+ cat = ast_category_browse(cfg, NULL); -+ while (cat) { -+ if (!strcasecmp(cat, "adapter")) { -+ id = ast_variable_retrieve(cfg, cat, "id"); -+ address = ast_variable_retrieve(cfg, cat, "address"); -+ master = ast_variable_retrieve(cfg, cat, "forcemaster"); -+ aligndetect = ast_variable_retrieve(cfg, cat, "alignmentdetection"); -+ if (option_debug) -+ ast_log(LOG_DEBUG, "Loading adapter %s %s.\n", id, address); -+ if (!ast_strlen_zero(id) && !ast_strlen_zero(address)) { -+ if ((adapter = ast_calloc(1, sizeof(*adapter)))) { -+ ast_copy_string(adapter->id, id, sizeof(adapter->id)); -+ str2ba(address, &adapter->addr); -+ if (!ast_strlen_zero(aligndetect)) { -+ if (*aligndetect == 'Y' || *aligndetect == 'y') -+ adapter->alignment_detection = 1; -+ } -+ adapter->dev_id = hci_devid(address); -+ adapter->hci_socket = hci_open_dev(adapter->dev_id); -+ if (adapter->dev_id < 0 || adapter->hci_socket < 0) { -+ ast_log(LOG_ERROR, "Unable to open adapter %s. It won't be enabled.\n", adapter->id); -+ ast_free(adapter); -+ } else { -+ if ((master) && (*master)) { -+ dr.dev_id = adapter->dev_id; -+ if (hci_strtolm("master", &dr.dev_opt)) { -+ if (ioctl(adapter->hci_socket, HCISETLINKMODE, (unsigned long) &dr) < 0) { -+ ast_log(LOG_WARNING, "Unable to set adapter %s link mode to MASTER.\n", adapter->id); -+ } -+ } -+ } -+ hci_read_voice_setting(adapter->hci_socket, &vs, 1000); -+ vs = htobs(vs); -+ if (vs != 0x0060) { -+ ast_log(LOG_ERROR, "Incorrect voice setting for adapter %s, it must be 0x0060 - see 'man hciconfig' for details.\n", adapter->id); -+ hci_close_dev(adapter->hci_socket); -+ ast_free(adapter); -+ } else { -+ AST_LIST_INSERT_HEAD(&adapters, adapter, entry); -+ nadapters++; -+ } -+ } -+ } -+ } else -+ ast_log(LOG_ERROR, "id/address missing for adapter %s. It won't be enabled.\n", cat); -+ } -+ cat = ast_category_browse(cfg, cat); -+ } -+ -+ if (!nadapters) { -+ ast_log(LOG_WARNING, "***********************************************************************\n"); -+ ast_log(LOG_WARNING, "No Adapters defined. Please review mobile.conf. See sample for details.\n"); -+ ast_log(LOG_WARNING, "***********************************************************************\n"); -+ } -+ -+ /* now load devices */ -+ cat = ast_category_browse(cfg, NULL); -+ while (cat) { -+ if (strcasecmp(cat, "general") && strcasecmp(cat, "adapter")) { -+ if (option_debug) -+ ast_log(LOG_DEBUG, "Loading device %s.\n", cat); -+ address = ast_variable_retrieve(cfg, cat, "address"); -+ useadapter = ast_variable_retrieve(cfg, cat, "adapter"); -+ port = ast_variable_retrieve(cfg, cat, "port"); -+ context = ast_variable_retrieve(cfg, cat, "context"); -+ type = ast_variable_retrieve(cfg, cat, "type"); -+ skip = ast_variable_retrieve(cfg, cat, "dtmfskip"); -+ group = ast_variable_retrieve(cfg, cat, "group"); -+ nocallsetup = ast_variable_retrieve(cfg, cat, "nocallsetup"); -+ if (!ast_strlen_zero(address) && !ast_strlen_zero(port) && !ast_strlen_zero(useadapter)) { -+ /* find the adapter */ -+ AST_LIST_TRAVERSE(&adapters, adapter, entry) { -+ if (!strcmp(adapter->id, useadapter)) -+ break; -+ } -+ if (!adapter) { -+ ast_log(LOG_ERROR, "Device %s configured to use unknown adapter %s. It won't be enabled.\n", cat, useadapter); -+ break; -+ } -+ if ((pvt = ast_calloc(1, sizeof(*pvt)))) { -+ if (type && !strcmp(type, "headset")) -+ pvt->type = MBL_TYPE_HEADSET; -+ else -+ pvt->type = MBL_TYPE_PHONE; -+ ast_copy_string(pvt->id, cat, sizeof(pvt->id)); -+ str2ba(address, &pvt->addr); -+ ast_copy_string(pvt->context, S_OR(context, "default"), sizeof(pvt->context)); -+ if (group) -+ pvt->group = atoi(group); /* group 0 if invalid */ -+ pvt->state = MBL_STATE_INIT; -+ pvt->rfcomm_socket = -1; -+ pvt->rfcomm_port = atoi(port); -+ pvt->sco_socket = -1; -+ pvt->monitor_thread = AST_PTHREADT_NULL; -+ pvt->sco_listener_thread = AST_PTHREADT_NULL; -+ if (!ast_strlen_zero(nocallsetup)) { -+ if ((*nocallsetup == 'y') || (*nocallsetup == 'Y')) { -+ pvt->no_callsetup = 1; -+ if (option_debug) -+ ast_log(LOG_DEBUG, "Setting nocallsetup mode for device %s.\n", pvt->id); -+ } -+ } -+ pvt->dsp = ast_dsp_new(); -+ if (skip) { -+ if ((pvt->dtmf_skip = atoi(skip)) == 0) -+ pvt->dtmf_skip = 200; -+ } else -+ pvt->dtmf_skip = 200; -+ ast_dsp_set_features(pvt->dsp, DSP_FEATURE_DTMF_DETECT); -+ ast_dsp_digitmode(pvt->dsp, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF); -+ pvt->adapter = adapter; -+ AST_LIST_INSERT_HEAD(&devices, pvt, entry); -+ } -+ } else { -+ ast_log(LOG_ERROR, "Device %s has no address/port/adapter configured. It won't be enabled.\n", cat); -+ } -+ } -+ cat = ast_category_browse(cfg, cat); -+ } -+ -+ ast_config_destroy(cfg); -+ -+ return 1; -+ -+} -+ -+static int unload_module(void) -+{ -+ -+ struct mbl_pvt *pvt; -+ struct adapter_pvt *adapter; -+ -+ /* First, take us out of the channel loop */ -+ ast_channel_unregister(&mbl_tech); -+ -+ /* Kill the discovery thread */ -+ if (discovery_thread != AST_PTHREADT_NULL) { -+ pthread_cancel(discovery_thread); -+ pthread_join(discovery_thread, NULL); -+ } -+ -+ /* Destroy the device list */ -+ while ((pvt = AST_LIST_REMOVE_HEAD(&devices, entry))) { -+ if (pvt->monitor_thread != AST_PTHREADT_NULL) { -+ pthread_cancel(pvt->monitor_thread); -+ pthread_join(pvt->monitor_thread, NULL); -+ } -+ if (pvt->sco_listener_thread != AST_PTHREADT_NULL) { -+ pthread_cancel(pvt->sco_listener_thread); -+ pthread_join(pvt->sco_listener_thread, NULL); -+ } -+ if (pvt->sco_socket > -1) { -+ close(pvt->sco_socket); -+ } -+ if (pvt->adapter->sco_socket > -1) { -+ close(pvt->adapter->sco_socket); -+ } -+ if (pvt->rfcomm_socket > -1) { -+ close(pvt->rfcomm_socket); -+ } -+ ast_dsp_free(pvt->dsp); -+ ast_free(pvt); -+ } -+ -+ /* Destroy the adapter list */ -+ while ((adapter = AST_LIST_REMOVE_HEAD(&adapters, entry))) { -+ hci_close_dev(adapter->hci_socket); -+ ast_free(adapter); -+ } -+ -+ if (sdp_session) -+ sdp_close(sdp_session); -+ -+ /* Unregister the CLI & APP */ -+ ast_cli_unregister_multiple(mbl_cli, sizeof(mbl_cli) / sizeof(mbl_cli[0])); -+ ast_unregister_application(app_mblstatus); -+ ast_unregister_application(app_mblsendsms); -+ -+ return 0; -+ -+} -+ -+static int load_module(void) -+{ -+ -+ int dev_id, s; -+ -+ /* Check if we have Bluetooth, no point loading otherwise... */ -+ dev_id = hci_get_route(NULL); -+ s = hci_open_dev(dev_id); -+ if (dev_id < 0 || s < 0) { -+ ast_log(LOG_ERROR, "No Bluetooth device found. Not loading module.\n"); -+ return AST_MODULE_LOAD_DECLINE; -+ } -+ -+ hci_close_dev(s); -+ -+ if (!mbl_load_config()) { -+ ast_log(LOG_ERROR, "Unable to read config file %s. Not loading module.\n", MBL_CONFIG); -+ return AST_MODULE_LOAD_DECLINE; -+ } -+ -+ sdp_session = sdp_register(); -+ -+ /* Spin the discovery thread */ -+ if (ast_pthread_create_background(&discovery_thread, NULL, do_discovery, NULL) < 0) { -+ ast_log(LOG_ERROR, "Unable to create discovery thread.\n"); -+ return AST_MODULE_LOAD_DECLINE; -+ } -+ -+ ast_cli_register_multiple(mbl_cli, sizeof(mbl_cli) / sizeof(mbl_cli[0])); -+ ast_register_application(app_mblstatus, mbl_status_exec, mblstatus_synopsis, mblstatus_desc); -+ ast_register_application(app_mblsendsms, mbl_sendsms_exec, mblsendsms_synopsis, mblsendsms_desc); -+ -+ /* Make sure we can register our channel type */ -+ if (ast_channel_register(&mbl_tech)) { -+ ast_log(LOG_ERROR, "Unable to register channel class %s\n", "Mobile"); -+ return AST_MODULE_LOAD_FAILURE; -+ } -+ -+ return 0; -+} -+ -+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Bluetooth Mobile Device Channel Driver", -+ .load = load_module, -+ .unload = unload_module, -+); -diff --git a/configs/mobile.conf.sample b/configs/mobile.conf.sample -new file mode 100644 -index 0000000..1ea4182 ---- /dev/null -+++ b/configs/mobile.conf.sample -@@ -0,0 +1,60 @@ -+; -+; mobile.conf -+; configuration file for chan_mobile -+; -+ -+[general] -+interval=30 ; Number of seconds between trying to connect to devices. -+ -+; The following is a list of adapters we use. -+; id must be unique and address is the bdaddr of the adapter from hciconfig. -+; Each adapter may only have one device (headset or phone) connected at a time. -+; Add an [adapter] entry for each adapter you have. -+ -+[adapter] -+id=blue -+address=00:09:DD:60:01:A3 -+;forcemaster=yes ; attempt to force adapter into master mode. default is no. -+;alignmentdetection=yes ; enable this if you sometimes get 'white noise' on asterisk side of the call -+ ; its a bug in the bluetooth adapter firmware, enabling this will compensate for it. -+ ; default is no. -+ -+[adapter] -+id=dlink -+address=00:80:C8:35:52:78 -+ -+; The following is a list of the devices we deal with. -+; Every device listed below will be available for calls in and out of Asterisk. -+; Each device needs an adapter=xxxx entry which determines which bluetooth adapter is used. -+; Use the CLI command 'mobile search' to discover devices. -+; Use the CLI command 'mobile show devices' to see device status. -+; -+; To place a call out through a mobile phone use Dial(Mobile/[device]/NNN.....) or Dial(Mobile/gn/NNN......) in your dialplan. -+; To call a headset use Dial(Mobile/[device]). -+ -+[LGTU550] -+address=00:E0:91:7F:46:44 ; the address of the phone -+port=4 ; the rfcomm port number (from mobile search) -+context=incoming-mobile ; dialplan context for incoming calls -+adapter=dlink ; adapter to use -+group=1 ; this phone is in channel group 1 -+;nocallsetup=yes ; set this only if your phone reports that it supports call progress notification, but does not do it. Motorola L6 for example. -+ -+[6310i] -+address=00:60:57:32:7E:B1 -+port=13 -+context=incoming-mobile -+adapter=dlink -+group=1 ; this phone is in channel group 1 also. -+ -+[headset] -+address=00:0B:9E:11:AE:C6 -+port=1 -+type=headset ; This is a headset, not a Phone ! -+adapter=blue -+ -+[headset1] -+address=00:0B:9E:11:74:A5 -+port=1 -+type=headset -+adapter=dlink -diff --git a/configure.ac b/configure.ac -index 17ecb3d..cb05662 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -168,6 +168,7 @@ AC_SUBST(AST_DEVMODE) - # by the --with option name, to make things easier for the users :-) - - AST_EXT_LIB_SETUP([ALSA], [Advanced Linux Sound Architecture], [asound]) -+AST_EXT_LIB_SETUP([BLUETOOTH], [Bluetooth Support], [bluetooth]) - AST_EXT_LIB_SETUP([CURL], [cURL], [curl]) - AST_EXT_LIB_SETUP([CAP], [POSIX 1.e capabilities], [cap]) - AST_EXT_LIB_SETUP([CURSES], [curses], [curses]) -@@ -394,6 +395,8 @@ AC_CHECK_SIZEOF(int) - - AST_EXT_LIB_CHECK([ALSA], [asound], [snd_spcm_init], [alsa/asoundlib.h], [-lm -ldl]) - -+AST_EXT_LIB_CHECK([BLUETOOTH], [bluetooth], [ba2str], [bluetooth/bluetooth.h]) -+ - AST_EXT_LIB_CHECK([CURSES], [curses], [initscr], [curses.h]) - - if test "x${host_os}" = "xlinux-gnu" ; then -diff --git a/doc/chan_mobile.txt b/doc/chan_mobile.txt -new file mode 100644 -index 0000000..5c8cafb ---- /dev/null -+++ b/doc/chan_mobile.txt -@@ -0,0 +1,240 @@ -+chan_mobile -+ -+Asterisk Channel Driver to allow Bluetooth Cell/Mobile Phones to be used as FXO devices, and Headsets as FXS devices. -+ -+ -+Features :- -+ -+Multiple Bluetooth Adapters supported. -+Multiple phones can be connected. -+Multiple headsets can be connected. -+Asterisk automatically connects to each configured mobile phone / headset when it comes in range. -+CLI command to discover bluetooth devices. -+Inbound calls on the mobile network to the mobile phones are handled by Asterisk, just like inbound calls on a Zap channel. -+CLI passed through on inbound calls. -+Dial outbound on a mobile phone using Dial(Mobile/device/nnnnnnn) in the dialplan. -+Dial a headset using Dial(Mobile/device) in the dialplan. -+Application MobileStatus can be used in the dialplan to see if a mobile phone / headset is connected. -+Supports devicestate for dialplan hinting. -+Supports Inbound and Outbound SMS. -+Supports 'channel' groups for implementing 'GSM Gateways' -+ -+ -+Requirements :- -+ -+In order to use chan_mobile, you must have a working bluetooth subsystem on your Asterisk box. -+This means one or more working bluetooth adapters, and the BlueZ packages. -+ -+Any bluetooth adapter supported by the Linux kernel will do, including usb bluetooth dongles. -+ -+The BlueZ package you need is bluez-utils. If you are using a GUI then you might want to install bluez-pin also. -+You also need libbluetooth, and libbluetooth-dev if you are compiling Asterisk from source. -+ -+You need to get bluetooth working with your phone before attempting to use chan_mobile. -+This means 'pairing' your phone or headset with your Asterisk box. I dont describe how to do this here as the process -+differs from distro to distro. You only need to pair once per adapter. -+ -+See www.bluez.org for details about setting up Bluetooth under Linux. -+ -+ -+Concepts :- -+ -+chan_mobile deals with both bluetooth adapters and bluetooth devices. This means you need to tell chan_mobile about the -+bluetooth adapters installed in your server as well as the devices (phones / headsets) you wish to use. -+ -+chan_mobile currently only allows one device (phone or headset) to be connected to an adapter at a time. This means you need -+one adapter for each device you wish to use simultaneously. Much effort has gone into trying to make multiple devices per adapter -+work, but in short it doesnt. -+ -+Periodically chan_mobile looks at each configured adapter, and if it is not in use (i.e. no device connected) will initiate a -+search for devices configured to use this adapater that may be in range. If it finds one it will connect the device and it -+will be available for Asterisk to use. When the device goes out of range, chan_mobile will disconnect the device and the adapter -+will become available for other devices. -+ -+ -+Configuring chan_mobile :- -+ -+The configuration file for chan_mobile is /etc/asterisk/mobile.conf. It is a normal Asterisk config file consisting of sections and key=value pairs. -+ -+See configs/mobile.conf.sample for an example and an explanation of the configuration. -+ -+ -+Using chan_mobile :- -+ -+chan_mobile.so must be loaded either by loading it using the Asterisk CLI, or by adding it to /etc/asterisk/modules.conf -+ -+Search for your bluetooth devices using the CLI command 'mobile search'. Be patient with this command as -+it will take 8 - 10 seconds to do the discovery. This requires a free adapter. -+ -+Headsets will generally have to be put into 'pairing' mode before they will show up here. -+ -+This will return something like the following :- -+ -+*CLI> mobile search -+Address Name Usable Type Port -+00:12:56:90:6E:00 LG TU500 Yes Phone 4 -+00:80:C8:35:52:78 Toaster No Headset 0 -+00:0B:9E:11:74:A5 Hello II Plus Yes Headset 1 -+00:0F:86:0E:AE:42 Daves Blackberry Yes Phone 7 -+ -+This is a list of all bluetooth devices seen and whether or not they are usable with chan_mobile. -+The Address field contains the 'bd address' of the device. This is like an ethernet mac address. -+The Name field is whatever is configured into the device as its name. -+The Usable field tells you whether or not the device supports the Bluetooth Handsfree Profile or Headset profile. -+The Type field tells you whether the device is usable as a Phone line (FXO) or a headset (FXS) -+The Port field is the number to put in the configuration file. -+ -+Choose which device(s) you want to use and edit /etc/asterisk/mobile.conf. There is a sample included -+with the Asterisk-addons source under configs/mobile.conf.sample. -+ -+Be sure to configure the right bd address and port number from the search. If you want inbound -+calls on a device to go to a specific context, add a context= line, otherwise the default will -+be used. The 'id' of the device [bitinbrackets] can be anything you like, just make it unique. -+ -+If you are configuring a Headset be sure to include the type=headset line, if left out it defaults -+to phone. -+ -+The CLI command 'mobile show devices' can be used at any time to show the status of configured devices, -+and whether or not the device is capable of sending / receiving SMS via bluetooth. -+ -+*CLI> mobile show devices -+ID Address Group Adapter Connected State SMS -+headset 00:0B:9E:11:AE:C6 0 blue No Init No -+LGTU550 00:E0:91:7F:46:44 1 dlink No Init No -+*CLI> -+ -+As each phone is connected you will see a message on the Asterisk console :- -+ -+ Loaded chan_mobile.so => (Bluetooth Mobile Device Channel Driver) -+ -- Bluetooth Device blackberry has connected. -+ -- Bluetooth Device dave has connected. -+ -+To make outbound calls, add something to you Dialplan like the following :- (modify to suit) -+ -+; Calls via LGTU5500 -+exten => _9X.,1,Dial(Mobile/LGTU550/${EXTEN:1},45) -+exten => _9X.,n,Hangup -+ -+To use channel groups, add an entry to each phones definition in mobile.conf like group=n -+where n is a number. -+ -+Then if you do something like Dial(Mobile/g1/123456) Asterisk will dial 123456 on the first -+connected free phone in group 1. -+ -+Phones which do not have a specific 'group=n' will be in group 0. -+ -+ -+To dial out on a headset, you need to use some other mechanism, because the headset is not likely -+to have all the needed buttons on it. res_clioriginate is good for this :- -+ -+*CLI> originate Mobile/headset extension NNNNN@context -+ -+This will call your headset, once you answer, Asterisk will call NNNNN at context context -+ -+Dialplan hints :- -+ -+chan_mobile supports 'device status' so you can do somthing like -+ -+exten => 1234,hint,SIP/30&Mobile/dave&Mobile/blackberry -+ -+ -+MobileStatus Application :- -+ -+chan_mobile also registers an application named MobileStatus. You can use this in your Dialplan -+to determine the 'state' of a device. -+ -+For example, suppose you wanted to call dave's extension, but only if he was in the office. You could -+test to see if his mobile phone was attached to Asterisk, if it is dial his extension, otherwise dial his -+mobile phone. -+ -+exten => 40,1,MobileStatus(dave,DAVECELL) -+exten => 40,2,GotoIf($["${DAVECELL}" = "1"]?3:5) -+exten => 40,3,Dial(ZAP/g1/0427466412,45,tT) -+exten => 40,4,Hangup -+exten => 40,5,Dial(SIP/40,45,tT) -+exten => 40,6,Hangup -+ -+MobileStatus sets the value of the given variable to :- -+ -+1 = Disconnected. i.e. Device not in range of Asterisk, or turned off etc etc -+2 = Connected and Not on a call. i.e. Free -+3 = Connected and on a call. i.e. Busy -+ -+ -+SMS Sending / Receiving -+ -+If Asterisk has detected your mobile phone is capable of SMS via bluetooth, you will be able to send and -+receive SMS. -+ -+Incoming SMS's cause Asterisk to create an inbound call to the context you defined in mobile.conf or the default -+context if you did not define one. The call will start at extension 'sms'. Two channel variables will be available, -+SMSSRC = the number of the originator of the SMS and SMSTXT which is the text of the SMS. -+This is not a voice call, so grab the values of the variables and hang the call up. -+ -+So, to handle incoming SMS's, do something like the following in your dialplan -+ -+[incoming-mobile] -+exten => sms,1,Verbose(Incoming SMS from ${SMSSRC} ${SMSTXT}) -+exten => sms,n,Hangup() -+ -+The above will just print the message on the console. -+ -+If you use res_jabber, you could do something like this :- -+ -+[incoming-mobile] -+exten => sms,1,JabberSend(transport,user@jabber.somewhere.com,SMS from ${SMSRC} ${SMSTXT}) -+exten => sms,2,Hangup() -+ -+To send an SMS, use the application MobileSendSMS like the following :- -+ -+exten => 99,1,MobileSendSMS(dave,0427123456,Hello World) -+ -+This will send 'Hello World' via device 'dave' to '0427123456' -+ -+ -+DTMF Debouncing :- -+ -+DTMF detection varies from phone to phone. There is a configuration variable that allows you to tune -+this to your needs. e.g. in mobile.conf -+ -+[LGTU550] -+address=00:12:56:90:6E:00 -+port=4 -+context=incoming-mobile -+dtmfskip=50 -+ -+change dtmfskip to suit your phone. The default is 200. The larger the number, the more chance of missed DTMF. -+The smaller the number the more chance of multiple digits being detected. -+ -+ -+Debugging :- -+ -+Different phone manufacturers have different interpretations of the Bluetooth Handsfree Profile Spec. -+This means that not all phones work the same way, particularly in the connection setup / initialisation -+sequence. I've tried to make chan_mobile as general as possible, but it may need modification to -+support some phone i've never tested. -+ -+Some phones, most notably Sony Ericsson 'T' series, dont quite conform to the Bluetooth HFP spec. -+chan_mobile will detect these and adapt accordingly. The T-610 and T-630 have been tested and -+work fine. -+ -+If your phone doesnt behave has expected, turn on Asterisk debugging with 'core set debug 1'. -+ -+This will log a bunch of debug messages indicating what the phone is doing, importantly the rfcomm -+conversation between Asterisk and the phone. This can be used to sort out what your phone is doing -+and make chan_mobile support it. -+ -+Be aware also, that just about all mobile phones behave differently. For example my LG TU500 wont dial unless -+the phone is a the 'idle' screen. i.e. if the phone is showing a 'menu' on the display, when you dial via -+Asterisk, the call will not work. chan_mobile handles this, but there may be other phones that do -+other things too... -+ -+Important: Watch what your mobile phone is doing the first few times. Asterisk wont make random calls but -+if chan_mobile fails to hangup for some reason and you get a huge bill from your telco, dont blame me ;) -+ -+ -+Feedback, Support, Please can you make Mobile Phone X work... etc :- -+ -+as always, bugs should be reported at http://bugs.digium.com -+ -+email me at david.bowerman at gmail.com or dseeb_ on #asterisk & #asterisk-dev irc. -diff --git a/makeopts.in b/makeopts.in -index a81503c..ac67901 100644 ---- a/makeopts.in -+++ b/makeopts.in -@@ -67,6 +67,9 @@ AST_DECLARATION_AFTER_STATEMENT=@AST_DECLARATION_AFTER_STATEMENT@ - ASOUND_INCLUDE=@ALSA_INCLUDE@ - ASOUND_LIB=@ALSA_LIB@ - -+BLUETOOTH_LIB=@BLUETOOTH_LIB@ -+BLUETOOTH_INCLUDE=@BLUETOOTH_INCLUDE@ -+ - CURL_INCLUDE=@CURL_INCLUDE@ - CURL_LIB=@CURL_LIB@ - --- -1.5.3.6 - diff --git a/asterisk-1.4.16-initscripts.patch b/asterisk-1.4.16-initscripts.patch deleted file mode 100644 index dbeb417..0000000 --- a/asterisk-1.4.16-initscripts.patch +++ /dev/null @@ -1,131 +0,0 @@ -From cc796206f187ff1d3f4bebde7381fcf5505db759 Mon Sep 17 00:00:00 2001 -From: Jeffrey C. Ollie -Date: Sun, 18 Nov 2007 21:47:59 -0600 -Subject: [PATCH] Modify init scripts for better Fedora compatibility. - ---- - contrib/init.d/rc.redhat.asterisk | 54 +++++++++++++++---------------------- - contrib/sysconfig/asterisk | 9 ++++++ - 2 files changed, 31 insertions(+), 32 deletions(-) - create mode 100644 contrib/sysconfig/asterisk - -diff --git a/contrib/init.d/rc.redhat.asterisk b/contrib/init.d/rc.redhat.asterisk -index 27d633e..f160af4 100755 ---- a/contrib/init.d/rc.redhat.asterisk -+++ b/contrib/init.d/rc.redhat.asterisk -@@ -3,7 +3,7 @@ - # - # asterisk Starts, Stops and Reloads Asterisk. - # --# chkconfig: 2345 90 60 -+# chkconfig: - 90 60 - # description: Asterisk PBX and telephony daemon. - # processname: asterisk - # pidfile: /var/run/asterisk.pid -@@ -20,59 +20,49 @@ - # - Added support for -U and -G command line options - # - Modified "reload" to call asterisk -rx 'reload' - --# Use this option to specify a different configuration directory --#AST_CONFIG=/etc/asterisk -+# Do not modify this script to change any of the settings, instead -+# edit /etc/sysconfig/asterisk -+ -+# Specify the configuration file -+AST_CONFIG=/etc/asterisk/asterisk.conf - - # Installation directory - AST_SBIN=/usr/sbin - --# Uncomment the following and set them to the user/groups that you --# want to run Asterisk as. NOTE: this requires substantial work to --# be sure that Asterisk's environment has permission to write the --# files required for its operation, including logs, its comm --# socket, the asterisk database, etc. --#AST_USER="asterisk" --#AST_GROUP="asterisk" -+# The user/group that Asterisk will run as. -+AST_USER="asterisk" -+AST_GROUP="asterisk" - --# Source function library. --. /etc/rc.d/init.d/functions -+# Allow configuration overrides in /etc/sysconfig/asterisk -+CONFIG0=`readlink $0` -+if [ "$CONFIG0" = "" ]; then -+ CONFIGFILE=/etc/sysconfig/`basename $0` -+else -+ CONFIGFILE=/etc/sysconfig/`basename $CONFIG0` -+fi -+[ -f $CONFIGFILE ] && . $CONFIGFILE - - if ! [ -x $AST_SBIN/asterisk ] ; then - echo "ERROR: $AST_SBIN/asterisk not found" - exit 0 - fi - --if ! [ -d $AST_CONFIG ] ; then -- echo "ERROR: $AST_CONFIG directory not found" -+if ! [ -f $AST_CONFIG ] ; then -+ echo "ERROR: $AST_CONFIG not found" - exit 0 - fi - --# Uncomment this ONLY if you know what you are doing. --# export LD_ASSUME_KERNEL=2.4.1 -+# Source function library. -+. /etc/rc.d/init.d/functions - - # Full path to asterisk binary - DAEMON=$AST_SBIN/asterisk - --# Full path to safe_asterisk script --SAFE_ASTERISK=$AST_SBIN/safe_asterisk -- --# Allow configuration overrides in /etc/sysconfig/asterisk --CONFIG0=`readlink $0` --if [ "$CONFIG0" = "" ]; then -- CONFIGFILE=/etc/sysconfig/`basename $0` --else -- CONFIGFILE=/etc/sysconfig/`basename $CONFIG0` --fi --[ -x $CONFIGFILE ] && . $CONFIGFILE -- - RETVAL=0 - - start() { - # Start daemons. - echo -n $"Starting asterisk: " -- if [ -f $SAFE_ASTERISK ] ; then -- DAEMON=$SAFE_ASTERISK -- fi - if [ $AST_USER ] ; then - ASTARGS="-U $AST_USER" - fi -@@ -91,7 +81,7 @@ start() { - - stop() { - # Stop daemons. -- echo -n $"Shutting down asterisk: " -+ echo -n $"Stopping asterisk: " - killproc asterisk - RETVAL=$? - [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/asterisk -diff --git a/contrib/sysconfig/asterisk b/contrib/sysconfig/asterisk -new file mode 100644 -index 0000000..05f5adb ---- /dev/null -+++ b/contrib/sysconfig/asterisk -@@ -0,0 +1,9 @@ -+# Specify the configuration file -+AST_CONFIG=/etc/asterisk/asterisk.conf -+ -+# Installation directory -+AST_SBIN=/usr/sbin -+ -+# The user/group that Asterisk will run as. -+AST_USER="asterisk" -+AST_GROUP="asterisk" --- -1.5.3.6 - diff --git a/asterisk-1.4.16-libcap.patch b/asterisk-1.4.16-libcap.patch deleted file mode 100644 index f3b81dd..0000000 --- a/asterisk-1.4.16-libcap.patch +++ /dev/null @@ -1,160 +0,0 @@ -From a7efa0ed8b28de55ef0fa2b39fefc3753ce1db8a Mon Sep 17 00:00:00 2001 -From: Jeffrey C. Ollie -Date: Thu, 8 Nov 2007 16:42:56 -0600 -Subject: [PATCH] Backport patch that lets Asterisk set the TOS byte even when running as non-root. - ---- - CHANGES | 5 +++++ - configure.ac | 5 +++++ - doc/security.txt | 7 +++++++ - main/Makefile | 3 +++ - main/asterisk.c | 30 +++++++++++++++++++++++++----- - makeopts.in | 3 +++ - 6 files changed, 48 insertions(+), 5 deletions(-) - -diff --git a/CHANGES b/CHANGES -index faa1855..7825d58 100644 ---- a/CHANGES -+++ b/CHANGES -@@ -339,3 +339,8 @@ Changes since Asterisk 1.2: - 1. aelparse -- compile .ael files outside of asterisk - * New manager events: - 1. OriginateResponse event comes to replace OriginateSuccess and OriginateFailure -+ -+ * Ability to use libcap to set high ToS bits when non-root on -+ Linux. If configure is unable to find libcap then you can use -+ --with-cap to specify the path. -+ -diff --git a/configure.ac b/configure.ac -index 1488a1e..17ecb3d 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -169,6 +169,7 @@ AC_SUBST(AST_DEVMODE) - - AST_EXT_LIB_SETUP([ALSA], [Advanced Linux Sound Architecture], [asound]) - AST_EXT_LIB_SETUP([CURL], [cURL], [curl]) -+AST_EXT_LIB_SETUP([CAP], [POSIX 1.e capabilities], [cap]) - AST_EXT_LIB_SETUP([CURSES], [curses], [curses]) - AST_EXT_LIB_SETUP([GNUTLS], [GNU TLS support (used for iksemel only)], [gnutls]) - AST_EXT_LIB_SETUP([GSM], [GSM], [gsm], [, or 'internal']) -@@ -395,6 +396,10 @@ AST_EXT_LIB_CHECK([ALSA], [asound], [snd_spcm_init], [alsa/asoundlib.h], [-lm -l - - AST_EXT_LIB_CHECK([CURSES], [curses], [initscr], [curses.h]) - -+if test "x${host_os}" = "xlinux-gnu" ; then -+ AST_EXT_LIB_CHECK([CAP], [cap], [cap_from_text], [sys/capability.h]) -+fi -+ - GSM_INTERNAL="yes" - AC_SUBST(GSM_INTERNAL) - GSM_SYSTEM="yes" -diff --git a/doc/security.txt b/doc/security.txt -index 0801679..3adf536 100644 ---- a/doc/security.txt -+++ b/doc/security.txt -@@ -28,6 +28,13 @@ The IAX2 protocol supports strong RSA key authentication as well as - AES encryption of voice and signalling. The SIP channel does not - support encryption in this version of Asterisk. - -+By default, if you have libcap available, Asterisk will try to retain the -+CAP_NET_ADMIN capability when running as a non-root user. If you do not need -+that capability you may want to configure Asterisk with --without-cap; however, -+this will prevent Asterisk from being able to mark high ToS bits under Linux. -+More information on CAP_NET_ADMIN is available at: -+http://www.lids.org/lids-howto/node48.html -+ - * DIALPLAN SECURITY - - First and foremost remember this: -diff --git a/main/Makefile b/main/Makefile -index 4f405b5..cee9e13 100644 ---- a/main/Makefile -+++ b/main/Makefile -@@ -55,6 +55,9 @@ ifneq ($(findstring $(OSARCH), linux-gnu uclinux linux-uclibc ),) - ifneq ($(findstring LOADABLE_MODULES,$(MENUSELECT_CFLAGS)),) - AST_LIBS+=-ldl - endif -+ ifneq (x$(CAP_LIB),x) -+ AST_LIBS+=$(CAP_LIB) -+ endif - AST_LIBS+=-lpthread $(EDITLINE_LIB) -lm -lresolv - else - AST_LIBS+=$(EDITLINE_LIB) -lm -diff --git a/main/asterisk.c b/main/asterisk.c -index 202a190..bd8cd9b 100644 ---- a/main/asterisk.c -+++ b/main/asterisk.c -@@ -82,13 +82,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - #include - #ifdef linux - #include --#endif -+#ifdef HAVE_CAP -+#include -+#endif /* HAVE_CAP */ -+#endif /* linux */ - #include - --#ifdef linux --#include --#endif -- - #if defined(__FreeBSD__) || defined( __NetBSD__ ) || defined(SOLARIS) - #include - #if defined(SOLARIS) -@@ -2734,6 +2733,10 @@ int main(int argc, char *argv[]) - } - - if (runuser && !ast_test_flag(&ast_options, AST_OPT_FLAG_REMOTE)) { -+#ifdef HAVE_CAP -+ cap_t cap; -+ int has_cap = 1; -+#endif /* HAVE_CAP */ - struct passwd *pw; - pw = getpwnam(runuser); - if (!pw) { -@@ -2744,6 +2747,12 @@ int main(int argc, char *argv[]) - ast_log(LOG_ERROR, "Asterisk started as nonroot, but runuser '%s' requested.\n", runuser); - exit(1); - } -+#ifdef HAVE_CAP -+ if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) { -+ ast_log(LOG_WARNING, "Unable to keep capabilities.\n"); -+ has_cap = 0; -+ } -+#endif /* HAVE_CAP */ - if (!rungroup) { - if (setgid(pw->pw_gid)) { - ast_log(LOG_WARNING, "Unable to setgid to %d!\n", (int)pw->pw_gid); -@@ -2760,6 +2769,17 @@ int main(int argc, char *argv[]) - } - if (option_verbose) - ast_verbose("Running as user '%s'\n", runuser); -+#ifdef HAVE_CAP -+ if (has_cap) { -+ cap = cap_from_text("cap_net_admin=ep"); -+ if (cap_set_proc(cap)) { -+ ast_log(LOG_WARNING, "Unable to install capabilities.\n"); -+ } -+ if (cap_free(cap)) { -+ ast_log(LOG_WARNING, "Unable to drop capabilities.\n"); -+ } -+ } -+#endif /* HAVE_CAP */ - } - - #endif /* __CYGWIN__ */ -diff --git a/makeopts.in b/makeopts.in -index 8138dd8..a81503c 100644 ---- a/makeopts.in -+++ b/makeopts.in -@@ -189,3 +189,6 @@ TERMCAP_DIR=@TERMCAP_DIR@ - TINFO_INCLUDE=@TINFO_INCLUDE@ - TINFO_LIB=@TINFO_LIB@ - TINFO_DIR=@TINFO_DIR@ -+ -+CAP_LIB=@CAP_LIB@ -+CAP_INCLUDE=@CAP_INCLUDE@ --- -1.5.3.6 - diff --git a/asterisk-1.4.16-optimization.patch b/asterisk-1.4.16-optimization.patch deleted file mode 100644 index df64893..0000000 --- a/asterisk-1.4.16-optimization.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 910a5c383dd583d11010eaed678e249b7f00b6dc Mon Sep 17 00:00:00 2001 -From: Jeffrey C. Ollie -Date: Thu, 8 Nov 2007 16:42:14 -0600 -Subject: [PATCH] Pick proper optimization flags for Fedora. - ---- - Makefile | 16 ++++++++++++---- - 1 files changed, 12 insertions(+), 4 deletions(-) - -diff --git a/Makefile b/Makefile -index 04a7b44..e8fae1d 100644 ---- a/Makefile -+++ b/Makefile -@@ -196,10 +196,16 @@ ifeq ($(OSARCH),linux-gnu) - endif - - ifeq ($(findstring -save-temps,$(ASTCFLAGS)),) --ASTCFLAGS+=-pipe -+ ifeq ($(findstring -pipe,$(ASTCFLAGS)),) -+ ASTCFLAGS+=-pipe -+ endif -+endif -+ -+ifeq ($(findstring -Wall,$(ASTCFLAGS)),) -+ ASTCFLAGS+=-Wall - endif - --ASTCFLAGS+=-Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations $(DEBUG) -+ASTCFLAGS+=-Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations $(DEBUG) - - ASTCFLAGS+=-include $(ASTTOPDIR)/include/asterisk/autoconfig.h - -@@ -212,8 +218,10 @@ ifneq ($(findstring BSD,$(OSARCH)),) - ASTLDFLAGS+=-L/usr/local/lib - endif - --ifneq ($(PROC),ultrasparc) -- ASTCFLAGS+=$(shell if $(CC) -march=$(PROC) -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=$(PROC)"; fi) -+ifeq ($(findstring -march,$(ASTCFLAGS)),) -+ ifneq ($(PROC),ultrasparc) -+ ASTCFLAGS+=$(shell if $(CC) -march=$(PROC) -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=$(PROC)"; fi) -+ endif - endif - - ifeq ($(PROC),ppc) --- -1.5.3.6 - diff --git a/asterisk-1.4.16-spandspfax.patch b/asterisk-1.4.16-spandspfax.patch deleted file mode 100644 index 2bc258a..0000000 --- a/asterisk-1.4.16-spandspfax.patch +++ /dev/null @@ -1,1254 +0,0 @@ -From e9bd907b6bc3f68e1cbabe13358472da68d6198a Mon Sep 17 00:00:00 2001 -From: Jeffrey C. Ollie -Date: Mon, 12 Nov 2007 15:51:12 -0600 -Subject: [PATCH] Add FAX apps. - ---- - apps/app_rxfax.c | 380 +++++++++++++++++++++++++++++++++ - apps/app_txfax.c | 306 ++++++++++++++++++++++++++ - build_tools/menuselect-deps.in | 1 + - configure | 436 ++++++++++++++++++++++++++++++++++++++ - configure.ac | 3 + - include/asterisk/autoconfig.h.in | 3 + - makeopts.in | 3 + - 7 files changed, 1132 insertions(+), 0 deletions(-) - create mode 100644 apps/app_rxfax.c - create mode 100644 apps/app_txfax.c - -diff --git a/apps/app_rxfax.c b/apps/app_rxfax.c -new file mode 100644 -index 0000000..bd81ea1 ---- /dev/null -+++ b/apps/app_rxfax.c -@@ -0,0 +1,380 @@ -+/* -+ * Asterisk -- A telephony toolkit for Linux. -+ * -+ * Trivial application to receive a TIFF FAX file -+ * -+ * Copyright (C) 2003, Steve Underwood -+ * -+ * Steve Underwood -+ * -+ * This program is free software, distributed under the terms of -+ * the GNU General Public License -+ */ -+ -+/*** MODULEINFO -+ spandsp -+***/ -+ -+#include "asterisk.h" -+ -+ASTERISK_FILE_VERSION(__FILE__, "$Revision:$") -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include "asterisk/lock.h" -+#include "asterisk/file.h" -+#include "asterisk/logger.h" -+#include "asterisk/channel.h" -+#include "asterisk/pbx.h" -+#include "asterisk/module.h" -+#include "asterisk/manager.h" -+ -+#ifndef AST_MODULE -+#define AST_MODULE "app_rxfax" -+#endif -+ -+static char *app = "RxFAX"; -+ -+static char *synopsis = "Receive a FAX to a file"; -+ -+static char *descrip = -+" RxFAX(filename[|caller][|debug]): Receives a FAX from the channel into the\n" -+"given filename. If the file exists it will be overwritten. The file\n" -+"should be in TIFF/F format.\n" -+"The \"caller\" option makes the application behave as a calling machine,\n" -+"rather than the answering machine. The default behaviour is to behave as\n" -+"an answering machine.\n" -+"Uses LOCALSTATIONID to identify itself to the remote end.\n" -+" LOCALHEADERINFO to generate a header line on each page.\n" -+"Sets REMOTESTATIONID to the sender CSID.\n" -+" FAXPAGES to the number of pages received.\n" -+" FAXBITRATE to the transmition rate.\n" -+" FAXRESOLUTION to the resolution.\n" -+"Returns -1 when the user hangs up.\n" -+"Returns 0 otherwise.\n"; -+ -+#define MAX_BLOCK_SIZE 240 -+ -+static void span_message(int level, const char *msg) -+{ -+ int ast_level; -+ -+ if (level == SPAN_LOG_WARNING) -+ ast_level = __LOG_WARNING; -+ else if (level == SPAN_LOG_WARNING) -+ ast_level = __LOG_WARNING; -+ else -+ ast_level = __LOG_DEBUG; -+ ast_log(ast_level, __FILE__, __LINE__, __PRETTY_FUNCTION__, msg); -+} -+/*- End of function --------------------------------------------------------*/ -+ -+#if 0 -+static void t30_flush(t30_state_t *s, int which) -+{ -+ /* TODO: */ -+} -+/*- End of function --------------------------------------------------------*/ -+#endif -+ -+static void phase_e_handler(t30_state_t *s, void *user_data, int result) -+{ -+ struct ast_channel *chan; -+ t30_stats_t t; -+ char local_ident[21]; -+ char far_ident[21]; -+ char buf[11]; -+ -+ chan = (struct ast_channel *) user_data; -+ if (result == T30_ERR_OK) -+ { -+ t30_get_transfer_statistics(s, &t); -+ t30_get_far_ident(s, far_ident); -+ t30_get_local_ident(s, local_ident); -+ ast_log(LOG_DEBUG, "==============================================================================\n"); -+ ast_log(LOG_DEBUG, "Fax successfully received.\n"); -+ ast_log(LOG_DEBUG, "Remote station id: %s\n", far_ident); -+ ast_log(LOG_DEBUG, "Local station id: %s\n", local_ident); -+ ast_log(LOG_DEBUG, "Pages transferred: %i\n", t.pages_transferred); -+ ast_log(LOG_DEBUG, "Image resolution: %i x %i\n", t.x_resolution, t.y_resolution); -+ ast_log(LOG_DEBUG, "Transfer Rate: %i\n", t.bit_rate); -+ ast_log(LOG_DEBUG, "==============================================================================\n"); -+ manager_event(EVENT_FLAG_CALL, -+ "FaxReceived", "Channel: %s\nExten: %s\nCallerID: %s\nRemoteStationID: %s\nLocalStationID: %s\nPagesTransferred: %i\nResolution: %i\nTransferRate: %i\nFileName: %s\n", -+ chan->name, -+ chan->exten, -+ (chan->cid.cid_num) ? chan->cid.cid_num : "", -+ far_ident, -+ local_ident, -+ t.pages_transferred, -+ t.y_resolution, -+ t.bit_rate, -+ s->rx_file); -+ pbx_builtin_setvar_helper(chan, "REMOTESTATIONID", far_ident); -+ snprintf(buf, sizeof(buf), "%i", t.pages_transferred); -+ pbx_builtin_setvar_helper(chan, "FAXPAGES", buf); -+ snprintf(buf, sizeof(buf), "%i", t.y_resolution); -+ pbx_builtin_setvar_helper(chan, "FAXRESOLUTION", buf); -+ snprintf(buf, sizeof(buf), "%i", t.bit_rate); -+ pbx_builtin_setvar_helper(chan, "FAXBITRATE", buf); -+ } -+ else -+ { -+ ast_log(LOG_DEBUG, "==============================================================================\n"); -+ ast_log(LOG_DEBUG, "Fax receive not successful - result (%d) %s.\n", result, t30_completion_code_to_str(result)); -+ ast_log(LOG_DEBUG, "==============================================================================\n"); -+ } -+} -+/*- End of function --------------------------------------------------------*/ -+ -+static void phase_d_handler(t30_state_t *s, void *user_data, int result) -+{ -+ struct ast_channel *chan; -+ t30_stats_t t; -+ -+ chan = (struct ast_channel *) user_data; -+ if (result) -+ { -+ t30_get_transfer_statistics(s, &t); -+ ast_log(LOG_DEBUG, "==============================================================================\n"); -+ ast_log(LOG_DEBUG, "Pages transferred: %i\n", t.pages_transferred); -+ ast_log(LOG_DEBUG, "Image size: %i x %i\n", t.width, t.length); -+ ast_log(LOG_DEBUG, "Image resolution %i x %i\n", t.x_resolution, t.y_resolution); -+ ast_log(LOG_DEBUG, "Transfer Rate: %i\n", t.bit_rate); -+ ast_log(LOG_DEBUG, "Bad rows %i\n", t.bad_rows); -+ ast_log(LOG_DEBUG, "Longest bad row run %i\n", t.longest_bad_row_run); -+ ast_log(LOG_DEBUG, "Compression type %i\n", t.encoding); -+ ast_log(LOG_DEBUG, "Image size (bytes) %i\n", t.image_size); -+ ast_log(LOG_DEBUG, "==============================================================================\n"); -+ } -+} -+/*- End of function --------------------------------------------------------*/ -+ -+static int rxfax_exec(struct ast_channel *chan, void *data) -+{ -+ int res = 0; -+ char template_file[256]; -+ char target_file[256]; -+ char *s; -+ char *t; -+ char *v; -+ const char *x; -+ int option; -+ int len; -+ int i; -+ fax_state_t fax; -+ int calling_party; -+ int verbose; -+ int samples; -+ -+ struct ast_module_user *u; -+ struct ast_frame *inf = NULL; -+ struct ast_frame outf; -+ -+ int original_read_fmt; -+ int original_write_fmt; -+ -+ uint8_t __buf[sizeof(uint16_t)*MAX_BLOCK_SIZE + 2*AST_FRIENDLY_OFFSET]; -+ uint8_t *buf = __buf + AST_FRIENDLY_OFFSET; -+ -+ if (chan == NULL) -+ { -+ ast_log(LOG_WARNING, "Fax receive channel is NULL. Giving up.\n"); -+ return -1; -+ } -+ -+ span_set_message_handler(span_message); -+ -+ /* The next few lines of code parse out the filename and header from the input string */ -+ if (data == NULL) -+ { -+ /* No data implies no filename or anything is present */ -+ ast_log(LOG_WARNING, "Rxfax requires an argument (filename)\n"); -+ return -1; -+ } -+ -+ calling_party = FALSE; -+ verbose = FALSE; -+ target_file[0] = '\0'; -+ -+ for (option = 0, v = s = data; v; option++, s++) -+ { -+ t = s; -+ v = strchr(s, '|'); -+ s = (v) ? v : s + strlen(s); -+ strncpy((char *) buf, t, s - t); -+ buf[s - t] = '\0'; -+ if (option == 0) -+ { -+ /* The first option is always the file name */ -+ len = s - t; -+ if (len > 255) -+ len = 255; -+ strncpy(target_file, t, len); -+ target_file[len] = '\0'; -+ /* Allow the use of %d in the file name for a wild card of sorts, to -+ create a new file with the specified name scheme */ -+ if ((x = strchr(target_file, '%')) && x[1] == 'd') -+ { -+ strcpy(template_file, target_file); -+ i = 0; -+ do -+ { -+ snprintf(target_file, 256, template_file, 1); -+ i++; -+ } -+ while (ast_fileexists(target_file, "", chan->language) != -1); -+ } -+ } -+ else if (strncmp("caller", t, s - t) == 0) -+ { -+ calling_party = TRUE; -+ } -+ else if (strncmp("debug", t, s - t) == 0) -+ { -+ verbose = TRUE; -+ } -+ } -+ -+ /* Done parsing */ -+ -+ u = ast_module_user_add(chan); -+ -+ if (chan->_state != AST_STATE_UP) -+ { -+ /* Shouldn't need this, but checking to see if channel is already answered -+ * Theoretically asterisk should already have answered before running the app */ -+ res = ast_answer(chan); -+ } -+ -+ if (!res) -+ { -+ original_read_fmt = chan->readformat; -+ if (original_read_fmt != AST_FORMAT_SLINEAR) -+ { -+ res = ast_set_read_format(chan, AST_FORMAT_SLINEAR); -+ if (res < 0) -+ { -+ ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n"); -+ return -1; -+ } -+ } -+ original_write_fmt = chan->writeformat; -+ if (original_write_fmt != AST_FORMAT_SLINEAR) -+ { -+ res = ast_set_write_format(chan, AST_FORMAT_SLINEAR); -+ if (res < 0) -+ { -+ ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up\n"); -+ res = ast_set_read_format(chan, original_read_fmt); -+ if (res) -+ ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); -+ return -1; -+ } -+ } -+ fax_init(&fax, calling_party); -+ if (verbose) -+ fax.logging.level = SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW; -+ x = pbx_builtin_getvar_helper(chan, "LOCALSTATIONID"); -+ if (x && x[0]) -+ t30_set_local_ident(&fax.t30_state, x); -+ x = pbx_builtin_getvar_helper(chan, "LOCALHEADERINFO"); -+ if (x && x[0]) -+ t30_set_header_info(&fax.t30_state, x); -+ t30_set_rx_file(&fax.t30_state, target_file, -1); -+ //t30_set_phase_b_handler(&fax.t30_state, phase_b_handler, chan); -+ t30_set_phase_d_handler(&fax.t30_state, phase_d_handler, chan); -+ t30_set_phase_e_handler(&fax.t30_state, phase_e_handler, chan); -+ t30_set_ecm_capability(&fax.t30_state, TRUE); -+ t30_set_supported_compressions(&fax.t30_state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION); -+ while (ast_waitfor(chan, -1) > -1) -+ { -+ inf = ast_read(chan); -+ if (inf == NULL) -+ { -+ res = -1; -+ break; -+ } -+ if (inf->frametype == AST_FRAME_VOICE) -+ { -+ if (fax_rx(&fax, inf->data, inf->samples)) -+ break; -+ samples = (inf->samples <= MAX_BLOCK_SIZE) ? inf->samples : MAX_BLOCK_SIZE; -+ len = fax_tx(&fax, (int16_t *) &buf[AST_FRIENDLY_OFFSET], samples); -+ if (len) -+ { -+ memset(&outf, 0, sizeof(outf)); -+ outf.frametype = AST_FRAME_VOICE; -+ outf.subclass = AST_FORMAT_SLINEAR; -+ outf.datalen = len*sizeof(int16_t); -+ outf.samples = len; -+ outf.data = &buf[AST_FRIENDLY_OFFSET]; -+ outf.offset = AST_FRIENDLY_OFFSET; -+ outf.src = "RxFAX"; -+ if (ast_write(chan, &outf) < 0) -+ { -+ ast_log(LOG_WARNING, "Unable to write frame to channel; %s\n", strerror(errno)); -+ break; -+ } -+ } -+ } -+ ast_frfree(inf); -+ } -+ if (inf == NULL) -+ { -+ ast_log(LOG_DEBUG, "Got hangup\n"); -+ res = -1; -+ } -+ if (original_read_fmt != AST_FORMAT_SLINEAR) -+ { -+ res = ast_set_read_format(chan, original_read_fmt); -+ if (res) -+ ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); -+ } -+ if (original_write_fmt != AST_FORMAT_SLINEAR) -+ { -+ res = ast_set_write_format(chan, original_write_fmt); -+ if (res) -+ ast_log(LOG_WARNING, "Unable to restore write format on '%s'\n", chan->name); -+ } -+ t30_terminate(&fax.t30_state); -+ } -+ else -+ { -+ ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name); -+ } -+ ast_module_user_remove(u); -+ return res; -+} -+/*- End of function --------------------------------------------------------*/ -+ -+static int unload_module(void) -+{ -+ int res; -+ -+ ast_module_user_hangup_all(); -+ -+ res = ast_unregister_application(app); -+ -+ -+ return res; -+} -+/*- End of function --------------------------------------------------------*/ -+ -+static int load_module(void) -+{ -+ return ast_register_application(app, rxfax_exec, synopsis, descrip); -+} -+/*- End of function --------------------------------------------------------*/ -+ -+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Trivial FAX Receive Application"); -+ -+/*- End of file ------------------------------------------------------------*/ -diff --git a/apps/app_txfax.c b/apps/app_txfax.c -new file mode 100644 -index 0000000..713ecf1 ---- /dev/null -+++ b/apps/app_txfax.c -@@ -0,0 +1,306 @@ -+/* -+ * Asterisk -- A telephony toolkit for Linux. -+ * -+ * Trivial application to send a TIFF file as a FAX -+ * -+ * Copyright (C) 2003, Steve Underwood -+ * -+ * Steve Underwood -+ * -+ * This program is free software, distributed under the terms of -+ * the GNU General Public License -+ */ -+ -+/*** MODULEINFO -+ spandsp -+***/ -+ -+#include "asterisk.h" -+ -+ASTERISK_FILE_VERSION(__FILE__, "$Revision:$") -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include "asterisk/lock.h" -+#include "asterisk/file.h" -+#include "asterisk/logger.h" -+#include "asterisk/channel.h" -+#include "asterisk/pbx.h" -+#include "asterisk/module.h" -+ -+#ifndef AST_MODULE -+#define AST_MODULE "app_txfax" -+#endif -+ -+static char *app = "TxFAX"; -+ -+static char *synopsis = "Send a FAX file"; -+ -+static char *descrip = -+" TxFAX(filename[|caller][|debug]): Send a given TIFF file to the channel as a FAX.\n" -+"The \"caller\" option makes the application behave as a calling machine,\n" -+"rather than the answering machine. The default behaviour is to behave as\n" -+"an answering machine.\n" -+"Uses LOCALSTATIONID to identify itself to the remote end.\n" -+" LOCALHEADERINFO to generate a header line on each page.\n" -+"Sets REMOTESTATIONID to the receiver CSID.\n" -+"Returns -1 when the user hangs up, or if the file does not exist.\n" -+"Returns 0 otherwise.\n"; -+ -+#define MAX_BLOCK_SIZE 240 -+ -+static void span_message(int level, const char *msg) -+{ -+ int ast_level; -+ -+ if (level == SPAN_LOG_WARNING) -+ ast_level = __LOG_WARNING; -+ else if (level == SPAN_LOG_WARNING) -+ ast_level = __LOG_WARNING; -+ else -+ ast_level = __LOG_DEBUG; -+ ast_log(ast_level, __FILE__, __LINE__, __PRETTY_FUNCTION__, msg); -+} -+/*- End of function --------------------------------------------------------*/ -+ -+#if 0 -+static void t30_flush(t30_state_t *s, int which) -+{ -+ /* TODO: */ -+} -+/*- End of function --------------------------------------------------------*/ -+#endif -+ -+static void phase_e_handler(t30_state_t *s, void *user_data, int result) -+{ -+ struct ast_channel *chan; -+ char far_ident[21]; -+ -+ chan = (struct ast_channel *) user_data; -+ if (result == T30_ERR_OK) -+ { -+ t30_get_far_ident(s, far_ident); -+ pbx_builtin_setvar_helper(chan, "REMOTESTATIONID", far_ident); -+ } -+ else -+ { -+ ast_log(LOG_DEBUG, "==============================================================================\n"); -+ ast_log(LOG_DEBUG, "Fax send not successful - result (%d) %s.\n", result, t30_completion_code_to_str(result)); -+ ast_log(LOG_DEBUG, "==============================================================================\n"); -+ } -+} -+/*- End of function --------------------------------------------------------*/ -+ -+static int txfax_exec(struct ast_channel *chan, void *data) -+{ -+ int res = 0; -+ char source_file[256]; -+ char *s; -+ char *t; -+ char *v; -+ const char *x; -+ int option; -+ int len; -+ fax_state_t fax; -+ int calling_party; -+ int verbose; -+ int samples; -+ -+ struct ast_module_user *u; -+ struct ast_frame *inf = NULL; -+ struct ast_frame outf; -+ -+ int original_read_fmt; -+ int original_write_fmt; -+ -+ uint8_t __buf[sizeof(uint16_t)*MAX_BLOCK_SIZE + 2*AST_FRIENDLY_OFFSET]; -+ uint8_t *buf = __buf + AST_FRIENDLY_OFFSET; -+ -+ if (chan == NULL) -+ { -+ ast_log(LOG_WARNING, "Fax transmit channel is NULL. Giving up.\n"); -+ return -1; -+ } -+ -+ span_set_message_handler(span_message); -+ -+ /* The next few lines of code parse out the filename and header from the input string */ -+ if (data == NULL) -+ { -+ /* No data implies no filename or anything is present */ -+ ast_log(LOG_WARNING, "Txfax requires an argument (filename)\n"); -+ return -1; -+ } -+ -+ calling_party = FALSE; -+ verbose = FALSE; -+ source_file[0] = '\0'; -+ -+ for (option = 0, v = s = data; v; option++, s++) -+ { -+ t = s; -+ v = strchr(s, '|'); -+ s = (v) ? v : s + strlen(s); -+ strncpy((char *) buf, t, s - t); -+ buf[s - t] = '\0'; -+ if (option == 0) -+ { -+ /* The first option is always the file name */ -+ len = s - t; -+ if (len > 255) -+ len = 255; -+ strncpy(source_file, t, len); -+ source_file[len] = '\0'; -+ } -+ else if (strncmp("caller", t, s - t) == 0) -+ { -+ calling_party = TRUE; -+ } -+ else if (strncmp("debug", t, s - t) == 0) -+ { -+ verbose = TRUE; -+ } -+ } -+ -+ /* Done parsing */ -+ -+ u = ast_module_user_add(chan); -+ -+ if (chan->_state != AST_STATE_UP) -+ { -+ /* Shouldn't need this, but checking to see if channel is already answered -+ * Theoretically asterisk should already have answered before running the app */ -+ res = ast_answer(chan); -+ } -+ -+ if (!res) -+ { -+ original_read_fmt = chan->readformat; -+ if (original_read_fmt != AST_FORMAT_SLINEAR) -+ { -+ res = ast_set_read_format(chan, AST_FORMAT_SLINEAR); -+ if (res < 0) -+ { -+ ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n"); -+ return -1; -+ } -+ } -+ original_write_fmt = chan->writeformat; -+ if (original_write_fmt != AST_FORMAT_SLINEAR) -+ { -+ res = ast_set_write_format(chan, AST_FORMAT_SLINEAR); -+ if (res < 0) -+ { -+ ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up\n"); -+ res = ast_set_read_format(chan, original_read_fmt); -+ if (res) -+ ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); -+ return -1; -+ } -+ } -+ fax_init(&fax, calling_party); -+ if (verbose) -+ fax.logging.level = SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW; -+ -+ x = pbx_builtin_getvar_helper(chan, "LOCALSTATIONID"); -+ if (x && x[0]) -+ t30_set_local_ident(&fax.t30_state, x); -+ x = pbx_builtin_getvar_helper(chan, "LOCALHEADERINFO"); -+ if (x && x[0]) -+ t30_set_header_info(&fax.t30_state, x); -+ t30_set_tx_file(&fax.t30_state, source_file, -1, -1); -+ //t30_set_phase_b_handler(&fax.t30_state, phase_b_handler, chan); -+ //t30_set_phase_d_handler(&fax.t30_state, phase_d_handler, chan); -+ t30_set_phase_e_handler(&fax.t30_state, phase_e_handler, chan); -+ t30_set_ecm_capability(&fax.t30_state, TRUE); -+ t30_set_supported_compressions(&fax.t30_state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION); -+ while (ast_waitfor(chan, -1) > -1) -+ { -+ inf = ast_read(chan); -+ if (inf == NULL) -+ { -+ res = -1; -+ break; -+ } -+ if (inf->frametype == AST_FRAME_VOICE) -+ { -+ if (fax_rx(&fax, inf->data, inf->samples)) -+ break; -+ samples = (inf->samples <= MAX_BLOCK_SIZE) ? inf->samples : MAX_BLOCK_SIZE; -+ len = fax_tx(&fax, (int16_t *) &buf[AST_FRIENDLY_OFFSET], samples); -+ if (len) -+ { -+ memset(&outf, 0, sizeof(outf)); -+ outf.frametype = AST_FRAME_VOICE; -+ outf.subclass = AST_FORMAT_SLINEAR; -+ outf.datalen = len*sizeof(int16_t); -+ outf.samples = len; -+ outf.data = &buf[AST_FRIENDLY_OFFSET]; -+ outf.offset = AST_FRIENDLY_OFFSET; -+ if (ast_write(chan, &outf) < 0) -+ { -+ ast_log(LOG_WARNING, "Unable to write frame to channel; %s\n", strerror(errno)); -+ break; -+ } -+ } -+ } -+ ast_frfree(inf); -+ } -+ if (inf == NULL) -+ { -+ ast_log(LOG_DEBUG, "Got hangup\n"); -+ res = -1; -+ } -+ if (original_read_fmt != AST_FORMAT_SLINEAR) -+ { -+ res = ast_set_read_format(chan, original_read_fmt); -+ if (res) -+ ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); -+ } -+ if (original_write_fmt != AST_FORMAT_SLINEAR) -+ { -+ res = ast_set_write_format(chan, original_write_fmt); -+ if (res) -+ ast_log(LOG_WARNING, "Unable to restore write format on '%s'\n", chan->name); -+ } -+ t30_terminate(&fax.t30_state); -+ } -+ else -+ { -+ ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name); -+ } -+ ast_module_user_remove(u); -+ return res; -+} -+/*- End of function --------------------------------------------------------*/ -+ -+static int unload_module(void) -+{ -+ int res; -+ -+ ast_module_user_hangup_all(); -+ -+ res = ast_unregister_application(app); -+ -+ -+ return res; -+} -+/*- End of function --------------------------------------------------------*/ -+ -+static int load_module(void) -+{ -+ return ast_register_application(app, txfax_exec, synopsis, descrip); -+} -+/*- End of function --------------------------------------------------------*/ -+ -+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Trivial FAX Transmit Application"); -+ -+/*- End of file ------------------------------------------------------------*/ -diff --git a/build_tools/menuselect-deps.in b/build_tools/menuselect-deps.in -index 60a53b4..fb10545 100644 ---- a/build_tools/menuselect-deps.in -+++ b/build_tools/menuselect-deps.in -@@ -22,6 +22,7 @@ POPT=@PBX_POPT@ - PRI=@PBX_PRI@ - QT=@PBX_QT@ - RADIUS=@PBX_RADIUS@ -+SPANDSP=@PBX_SPANDSP@ - SPEEX=@PBX_SPEEX@ - SQLITE=@PBX_SQLITE@ - SSL=@PBX_OPENSSL@ -diff --git a/configure b/configure -index fb3bdbf..8fcf1b9 100755 ---- a/configure -+++ b/configure -@@ -821,6 +821,10 @@ RADIUS_LIB - RADIUS_INCLUDE - RADIUS_DIR - PBX_RADIUS -+SPANDSP_LIB -+SPANDSP_INCLUDE -+SPANDSP_DIR -+PBX_SPANDSP - SPEEX_LIB - SPEEX_INCLUDE - SPEEX_DIR -@@ -1532,6 +1536,7 @@ Optional Packages: - --with-h323=PATH use OpenH323 files in PATH - --with-qt=PATH use Qt files in PATH - --with-radius=PATH use Radius Client files in PATH -+ --with-spandsp=PATH use spandsp Library files in PATH - --with-speex=PATH use Speex files in PATH - --with-sqlite=PATH use SQLite files in PATH - --with-suppserv=PATH use mISDN Supplemental Services files in PATH -@@ -8365,6 +8370,34 @@ PBX_RADIUS=0 - - - -+SPANDSP_DESCRIP="spandsp Library" -+SPANDSP_OPTION="spandsp" -+ -+# Check whether --with-spandsp was given. -+if test "${with_spandsp+set}" = set; then -+ withval=$with_spandsp; -+case ${withval} in -+ n|no) -+ USE_SPANDSP=no -+ ;; -+ y|ye|yes) -+ SPANDSP_MANDATORY="yes" -+ ;; -+ *) -+ SPANDSP_DIR="${withval}" -+ SPANDSP_MANDATORY="yes" -+ ;; -+esac -+ -+fi -+ -+PBX_SPANDSP=0 -+ -+ -+ -+ -+ -+ - SPEEX_DESCRIP="Speex" - SPEEX_OPTION="speex" - -@@ -27885,6 +27918,405 @@ fi - - - -+if test "${USE_SPANDSP}" != "no"; then -+ pbxlibdir="" -+ if test "x${SPANDSP_DIR}" != "x"; then -+ if test -d ${SPANDSP_DIR}/lib; then -+ pbxlibdir="-L${SPANDSP_DIR}/lib" -+ else -+ pbxlibdir="-L${SPANDSP_DIR}" -+ fi -+ fi -+ { echo "$as_me:$LINENO: checking for fax_init in -lspandsp" >&5 -+echo $ECHO_N "checking for fax_init in -lspandsp... $ECHO_C" >&6; } -+if test "${ac_cv_lib_spandsp_fax_init+set}" = set; then -+ echo $ECHO_N "(cached) $ECHO_C" >&6 -+else -+ ac_check_lib_save_LIBS=$LIBS -+LIBS="-lspandsp ${pbxlibdir} -ltiff $LIBS" -+cat >conftest.$ac_ext <<_ACEOF -+/* confdefs.h. */ -+_ACEOF -+cat confdefs.h >>conftest.$ac_ext -+cat >>conftest.$ac_ext <<_ACEOF -+/* end confdefs.h. */ -+ -+/* Override any GCC internal prototype to avoid an error. -+ Use char because int might match the return type of a GCC -+ builtin and then its argument prototype would still apply. */ -+#ifdef __cplusplus -+extern "C" -+#endif -+char fax_init (); -+int -+main () -+{ -+return fax_init (); -+ ; -+ return 0; -+} -+_ACEOF -+rm -f conftest.$ac_objext conftest$ac_exeext -+if { (ac_try="$ac_link" -+case "(($ac_try" in -+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; -+ *) ac_try_echo=$ac_try;; -+esac -+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 -+ (eval "$ac_link") 2>conftest.er1 -+ ac_status=$? -+ grep -v '^ *+' conftest.er1 >conftest.err -+ rm -f conftest.er1 -+ cat conftest.err >&5 -+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); } && { -+ test -z "$ac_c_werror_flag" || -+ test ! -s conftest.err -+ } && test -s conftest$ac_exeext && -+ $as_test_x conftest$ac_exeext; then -+ ac_cv_lib_spandsp_fax_init=yes -+else -+ echo "$as_me: failed program was:" >&5 -+sed 's/^/| /' conftest.$ac_ext >&5 -+ -+ ac_cv_lib_spandsp_fax_init=no -+fi -+ -+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ -+ conftest$ac_exeext conftest.$ac_ext -+LIBS=$ac_check_lib_save_LIBS -+fi -+{ echo "$as_me:$LINENO: result: $ac_cv_lib_spandsp_fax_init" >&5 -+echo "${ECHO_T}$ac_cv_lib_spandsp_fax_init" >&6; } -+if test $ac_cv_lib_spandsp_fax_init = yes; then -+ AST_SPANDSP_FOUND=yes -+else -+ AST_SPANDSP_FOUND=no -+fi -+ -+ -+ if test "${AST_SPANDSP_FOUND}" = "yes"; then -+ SPANDSP_LIB="-lspandsp -ltiff" -+ SPANDSP_HEADER_FOUND="1" -+ if test "x${SPANDSP_DIR}" != "x"; then -+ SPANDSP_LIB="${pbxlibdir} ${SPANDSP_LIB}" -+ SPANDSP_INCLUDE="-I${SPANDSP_DIR}/include" -+ saved_cppflags="${CPPFLAGS}" -+ CPPFLAGS="${CPPFLAGS} -I${SPANDSP_DIR}/include" -+ if test "xspandsp.h" != "x" ; then -+ as_ac_Header=`echo "ac_cv_header_${SPANDSP_DIR}/include/spandsp.h" | $as_tr_sh` -+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then -+ { echo "$as_me:$LINENO: checking for ${SPANDSP_DIR}/include/spandsp.h" >&5 -+echo $ECHO_N "checking for ${SPANDSP_DIR}/include/spandsp.h... $ECHO_C" >&6; } -+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then -+ echo $ECHO_N "(cached) $ECHO_C" >&6 -+fi -+ac_res=`eval echo '${'$as_ac_Header'}'` -+ { echo "$as_me:$LINENO: result: $ac_res" >&5 -+echo "${ECHO_T}$ac_res" >&6; } -+else -+ # Is the header compilable? -+{ echo "$as_me:$LINENO: checking ${SPANDSP_DIR}/include/spandsp.h usability" >&5 -+echo $ECHO_N "checking ${SPANDSP_DIR}/include/spandsp.h usability... $ECHO_C" >&6; } -+cat >conftest.$ac_ext <<_ACEOF -+/* confdefs.h. */ -+_ACEOF -+cat confdefs.h >>conftest.$ac_ext -+cat >>conftest.$ac_ext <<_ACEOF -+/* end confdefs.h. */ -+$ac_includes_default -+#include <${SPANDSP_DIR}/include/spandsp.h> -+_ACEOF -+rm -f conftest.$ac_objext -+if { (ac_try="$ac_compile" -+case "(($ac_try" in -+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; -+ *) ac_try_echo=$ac_try;; -+esac -+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 -+ (eval "$ac_compile") 2>conftest.er1 -+ ac_status=$? -+ grep -v '^ *+' conftest.er1 >conftest.err -+ rm -f conftest.er1 -+ cat conftest.err >&5 -+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); } && { -+ test -z "$ac_c_werror_flag" || -+ test ! -s conftest.err -+ } && test -s conftest.$ac_objext; then -+ ac_header_compiler=yes -+else -+ echo "$as_me: failed program was:" >&5 -+sed 's/^/| /' conftest.$ac_ext >&5 -+ -+ ac_header_compiler=no -+fi -+ -+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 -+echo "${ECHO_T}$ac_header_compiler" >&6; } -+ -+# Is the header present? -+{ echo "$as_me:$LINENO: checking ${SPANDSP_DIR}/include/spandsp.h presence" >&5 -+echo $ECHO_N "checking ${SPANDSP_DIR}/include/spandsp.h presence... $ECHO_C" >&6; } -+cat >conftest.$ac_ext <<_ACEOF -+/* confdefs.h. */ -+_ACEOF -+cat confdefs.h >>conftest.$ac_ext -+cat >>conftest.$ac_ext <<_ACEOF -+/* end confdefs.h. */ -+#include <${SPANDSP_DIR}/include/spandsp.h> -+_ACEOF -+if { (ac_try="$ac_cpp conftest.$ac_ext" -+case "(($ac_try" in -+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; -+ *) ac_try_echo=$ac_try;; -+esac -+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 -+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 -+ ac_status=$? -+ grep -v '^ *+' conftest.er1 >conftest.err -+ rm -f conftest.er1 -+ cat conftest.err >&5 -+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); } >/dev/null && { -+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || -+ test ! -s conftest.err -+ }; then -+ ac_header_preproc=yes -+else -+ echo "$as_me: failed program was:" >&5 -+sed 's/^/| /' conftest.$ac_ext >&5 -+ -+ ac_header_preproc=no -+fi -+ -+rm -f conftest.err conftest.$ac_ext -+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 -+echo "${ECHO_T}$ac_header_preproc" >&6; } -+ -+# So? What about this header? -+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in -+ yes:no: ) -+ { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: accepted by the compiler, rejected by the preprocessor!" >&5 -+echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: accepted by the compiler, rejected by the preprocessor!" >&2;} -+ { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: proceeding with the compiler's result" >&5 -+echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: proceeding with the compiler's result" >&2;} -+ ac_header_preproc=yes -+ ;; -+ no:yes:* ) -+ { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: present but cannot be compiled" >&5 -+echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: present but cannot be compiled" >&2;} -+ { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: check for missing prerequisite headers?" >&5 -+echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: check for missing prerequisite headers?" >&2;} -+ { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: see the Autoconf documentation" >&5 -+echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: see the Autoconf documentation" >&2;} -+ { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: section \"Present But Cannot Be Compiled\"" >&5 -+echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: section \"Present But Cannot Be Compiled\"" >&2;} -+ { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: proceeding with the preprocessor's result" >&5 -+echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: proceeding with the preprocessor's result" >&2;} -+ { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: in the future, the compiler will take precedence" >&5 -+echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: in the future, the compiler will take precedence" >&2;} -+ -+ ;; -+esac -+{ echo "$as_me:$LINENO: checking for ${SPANDSP_DIR}/include/spandsp.h" >&5 -+echo $ECHO_N "checking for ${SPANDSP_DIR}/include/spandsp.h... $ECHO_C" >&6; } -+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then -+ echo $ECHO_N "(cached) $ECHO_C" >&6 -+else -+ eval "$as_ac_Header=\$ac_header_preproc" -+fi -+ac_res=`eval echo '${'$as_ac_Header'}'` -+ { echo "$as_me:$LINENO: result: $ac_res" >&5 -+echo "${ECHO_T}$ac_res" >&6; } -+ -+fi -+if test `eval echo '${'$as_ac_Header'}'` = yes; then -+ SPANDSP_HEADER_FOUND=1 -+else -+ SPANDSP_HEADER_FOUND=0 -+fi -+ -+ -+ fi -+ CPPFLAGS="${saved_cppflags}" -+ else -+ if test "xspandsp.h" != "x" ; then -+ if test "${ac_cv_header_spandsp_h+set}" = set; then -+ { echo "$as_me:$LINENO: checking for spandsp.h" >&5 -+echo $ECHO_N "checking for spandsp.h... $ECHO_C" >&6; } -+if test "${ac_cv_header_spandsp_h+set}" = set; then -+ echo $ECHO_N "(cached) $ECHO_C" >&6 -+fi -+{ echo "$as_me:$LINENO: result: $ac_cv_header_spandsp_h" >&5 -+echo "${ECHO_T}$ac_cv_header_spandsp_h" >&6; } -+else -+ # Is the header compilable? -+{ echo "$as_me:$LINENO: checking spandsp.h usability" >&5 -+echo $ECHO_N "checking spandsp.h usability... $ECHO_C" >&6; } -+cat >conftest.$ac_ext <<_ACEOF -+/* confdefs.h. */ -+_ACEOF -+cat confdefs.h >>conftest.$ac_ext -+cat >>conftest.$ac_ext <<_ACEOF -+/* end confdefs.h. */ -+$ac_includes_default -+#include -+_ACEOF -+rm -f conftest.$ac_objext -+if { (ac_try="$ac_compile" -+case "(($ac_try" in -+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; -+ *) ac_try_echo=$ac_try;; -+esac -+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 -+ (eval "$ac_compile") 2>conftest.er1 -+ ac_status=$? -+ grep -v '^ *+' conftest.er1 >conftest.err -+ rm -f conftest.er1 -+ cat conftest.err >&5 -+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); } && { -+ test -z "$ac_c_werror_flag" || -+ test ! -s conftest.err -+ } && test -s conftest.$ac_objext; then -+ ac_header_compiler=yes -+else -+ echo "$as_me: failed program was:" >&5 -+sed 's/^/| /' conftest.$ac_ext >&5 -+ -+ ac_header_compiler=no -+fi -+ -+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 -+echo "${ECHO_T}$ac_header_compiler" >&6; } -+ -+# Is the header present? -+{ echo "$as_me:$LINENO: checking spandsp.h presence" >&5 -+echo $ECHO_N "checking spandsp.h presence... $ECHO_C" >&6; } -+cat >conftest.$ac_ext <<_ACEOF -+/* confdefs.h. */ -+_ACEOF -+cat confdefs.h >>conftest.$ac_ext -+cat >>conftest.$ac_ext <<_ACEOF -+/* end confdefs.h. */ -+#include -+_ACEOF -+if { (ac_try="$ac_cpp conftest.$ac_ext" -+case "(($ac_try" in -+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; -+ *) ac_try_echo=$ac_try;; -+esac -+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 -+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 -+ ac_status=$? -+ grep -v '^ *+' conftest.er1 >conftest.err -+ rm -f conftest.er1 -+ cat conftest.err >&5 -+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); } >/dev/null && { -+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || -+ test ! -s conftest.err -+ }; then -+ ac_header_preproc=yes -+else -+ echo "$as_me: failed program was:" >&5 -+sed 's/^/| /' conftest.$ac_ext >&5 -+ -+ ac_header_preproc=no -+fi -+ -+rm -f conftest.err conftest.$ac_ext -+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 -+echo "${ECHO_T}$ac_header_preproc" >&6; } -+ -+# So? What about this header? -+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in -+ yes:no: ) -+ { echo "$as_me:$LINENO: WARNING: spandsp.h: accepted by the compiler, rejected by the preprocessor!" >&5 -+echo "$as_me: WARNING: spandsp.h: accepted by the compiler, rejected by the preprocessor!" >&2;} -+ { echo "$as_me:$LINENO: WARNING: spandsp.h: proceeding with the compiler's result" >&5 -+echo "$as_me: WARNING: spandsp.h: proceeding with the compiler's result" >&2;} -+ ac_header_preproc=yes -+ ;; -+ no:yes:* ) -+ { echo "$as_me:$LINENO: WARNING: spandsp.h: present but cannot be compiled" >&5 -+echo "$as_me: WARNING: spandsp.h: present but cannot be compiled" >&2;} -+ { echo "$as_me:$LINENO: WARNING: spandsp.h: check for missing prerequisite headers?" >&5 -+echo "$as_me: WARNING: spandsp.h: check for missing prerequisite headers?" >&2;} -+ { echo "$as_me:$LINENO: WARNING: spandsp.h: see the Autoconf documentation" >&5 -+echo "$as_me: WARNING: spandsp.h: see the Autoconf documentation" >&2;} -+ { echo "$as_me:$LINENO: WARNING: spandsp.h: section \"Present But Cannot Be Compiled\"" >&5 -+echo "$as_me: WARNING: spandsp.h: section \"Present But Cannot Be Compiled\"" >&2;} -+ { echo "$as_me:$LINENO: WARNING: spandsp.h: proceeding with the preprocessor's result" >&5 -+echo "$as_me: WARNING: spandsp.h: proceeding with the preprocessor's result" >&2;} -+ { echo "$as_me:$LINENO: WARNING: spandsp.h: in the future, the compiler will take precedence" >&5 -+echo "$as_me: WARNING: spandsp.h: in the future, the compiler will take precedence" >&2;} -+ -+ ;; -+esac -+{ echo "$as_me:$LINENO: checking for spandsp.h" >&5 -+echo $ECHO_N "checking for spandsp.h... $ECHO_C" >&6; } -+if test "${ac_cv_header_spandsp_h+set}" = set; then -+ echo $ECHO_N "(cached) $ECHO_C" >&6 -+else -+ ac_cv_header_spandsp_h=$ac_header_preproc -+fi -+{ echo "$as_me:$LINENO: result: $ac_cv_header_spandsp_h" >&5 -+echo "${ECHO_T}$ac_cv_header_spandsp_h" >&6; } -+ -+fi -+if test $ac_cv_header_spandsp_h = yes; then -+ SPANDSP_HEADER_FOUND=1 -+else -+ SPANDSP_HEADER_FOUND=0 -+fi -+ -+ -+ fi -+ fi -+ if test "x${SPANDSP_HEADER_FOUND}" = "x0" ; then -+ if test -n "${SPANDSP_MANDATORY}" ; -+ then -+ { echo "$as_me:$LINENO: ***" >&5 -+echo "$as_me: ***" >&6;} -+ { echo "$as_me:$LINENO: *** It appears that you do not have the spandsp development package installed." >&5 -+echo "$as_me: *** It appears that you do not have the spandsp development package installed." >&6;} -+ { echo "$as_me:$LINENO: *** Please install it to include ${SPANDSP_DESCRIP} support, or re-run configure" >&5 -+echo "$as_me: *** Please install it to include ${SPANDSP_DESCRIP} support, or re-run configure" >&6;} -+ { echo "$as_me:$LINENO: *** without explicitly specifying --with-${SPANDSP_OPTION}" >&5 -+echo "$as_me: *** without explicitly specifying --with-${SPANDSP_OPTION}" >&6;} -+ exit 1 -+ fi -+ SPANDSP_LIB="" -+ SPANDSP_INCLUDE="" -+ PBX_SPANDSP=0 -+ else -+ PBX_SPANDSP=1 -+ -+cat >>confdefs.h <<_ACEOF -+#define HAVE_SPANDSP 1 -+_ACEOF -+ -+ fi -+ elif test -n "${SPANDSP_MANDATORY}"; -+ then -+ { echo "$as_me:$LINENO: ***" >&5 -+echo "$as_me: ***" >&6;} -+ { echo "$as_me:$LINENO: *** The ${SPANDSP_DESCRIP} installation on this system appears to be broken." >&5 -+echo "$as_me: *** The ${SPANDSP_DESCRIP} installation on this system appears to be broken." >&6;} -+ { echo "$as_me:$LINENO: *** Either correct the installation, or run configure" >&5 -+echo "$as_me: *** Either correct the installation, or run configure" >&6;} -+ { echo "$as_me:$LINENO: *** without explicitly specifying --with-${SPANDSP_OPTION}" >&5 -+echo "$as_me: *** without explicitly specifying --with-${SPANDSP_OPTION}" >&6;} -+ exit 1 -+ fi -+fi -+ -+ -+ - if test "${USE_SPEEX}" != "no"; then - pbxlibdir="" - if test "x${SPEEX_DIR}" != "x"; then -@@ -34376,6 +34808,10 @@ RADIUS_LIB!$RADIUS_LIB$ac_delim - RADIUS_INCLUDE!$RADIUS_INCLUDE$ac_delim - RADIUS_DIR!$RADIUS_DIR$ac_delim - PBX_RADIUS!$PBX_RADIUS$ac_delim -+SPANDSP_LIB!$SPANDSP_LIB$ac_delim -+SPANDSP_INCLUDE!$SPANDSP_INCLUDE$ac_delim -+SPANDSP_DIR!$SPANDSP_DIR$ac_delim -+PBX_SPANDSP!$PBX_SPANDSP$ac_delim - SPEEX_LIB!$SPEEX_LIB$ac_delim - SPEEX_INCLUDE!$SPEEX_INCLUDE$ac_delim - SPEEX_DIR!$SPEEX_DIR$ac_delim -diff --git a/configure.ac b/configure.ac -index 4b074ba..1488a1e 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -193,6 +193,7 @@ AST_EXT_LIB_SETUP([PWLIB], [PWlib], [pwlib]) - AST_EXT_LIB_SETUP([OPENH323], [OpenH323], [h323]) - AST_EXT_LIB_SETUP([QT], [Qt], [qt]) - AST_EXT_LIB_SETUP([RADIUS], [Radius Client], [radius]) -+AST_EXT_LIB_SETUP([SPANDSP], [spandsp Library], [spandsp]) - AST_EXT_LIB_SETUP([SPEEX], [Speex], [speex]) - AST_EXT_LIB_SETUP([SQLITE], [SQLite], [sqlite]) - AST_EXT_LIB_SETUP([SUPPSERV], [mISDN Supplemental Services], [suppserv]) -@@ -958,6 +959,8 @@ AC_LANG_POP - - AST_EXT_LIB_CHECK([RADIUS], [radiusclient-ng], [rc_read_config], [radiusclient-ng.h]) - -+AST_EXT_LIB_CHECK([SPANDSP], [spandsp], [fax_init], [spandsp.h], [-ltiff]) -+ - AST_EXT_LIB_CHECK([SPEEX], [speex], [speex_encode], [speex/speex.h], [-lm]) - - AST_EXT_LIB_CHECK([SQLITE], [sqlite], [sqlite_exec], [sqlite.h]) -diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in -index d5a6faa..47fcad9 100644 ---- a/include/asterisk/autoconfig.h.in -+++ b/include/asterisk/autoconfig.h.in -@@ -323,6 +323,9 @@ - /* Define to 1 if you have the `socket' function. */ - #undef HAVE_SOCKET - -+/* Define to indicate the ${SPANDSP_DESCRIP} library */ -+#undef HAVE_SPANDSP -+ - /* Define to indicate the ${SPEEX_DESCRIP} library */ - #undef HAVE_SPEEX - -diff --git a/makeopts.in b/makeopts.in -index 193ec3c..8138dd8 100644 ---- a/makeopts.in -+++ b/makeopts.in -@@ -141,6 +141,9 @@ QT_LIB=@QT_LIB@ - RADIUS_INCLUDE=@RADIUS_INCLUDE@ - RADIUS_LIB=@RADIUS_LIB@ - -+SPANDSP_INCLUDE=@SPANDSP_INCLUDE@ -+SPANDSP_LIB=@SPANDSP_LIB@ -+ - SPEEX_INCLUDE=@SPEEX_INCLUDE@ - SPEEX_LIB=@SPEEX_LIB@ - --- -1.5.3.6 - diff --git a/asterisk-1.4.16-system-imap.patch b/asterisk-1.4.16-system-imap.patch deleted file mode 100644 index 31b8b5f..0000000 --- a/asterisk-1.4.16-system-imap.patch +++ /dev/null @@ -1,254 +0,0 @@ -From 964b72f05f056a6a494e8650c37f7172320fb9cd Mon Sep 17 00:00:00 2001 -From: Jeffrey C. Ollie -Date: Thu, 8 Nov 2007 15:47:38 -0600 -Subject: [PATCH] Patch so that system IMAP library can be used. - ---- - apps/app_voicemail.c | 10 ++++++++++ - configure | 37 ++++++++++++++++++++++++++++--------- - configure.ac | 36 +++++++++++++++++++++++++++--------- - doc/imapstorage.txt | 34 ++++++++++++++++------------------ - 4 files changed, 81 insertions(+), 36 deletions(-) - -diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c -index fec54fc..d455a0f 100644 ---- a/apps/app_voicemail.c -+++ b/apps/app_voicemail.c -@@ -70,10 +70,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - #include - #include - #include -+#ifdef USE_SYSTEM_IMAP -+#include -+#include -+#include -+#else - #include "c-client.h" - #include "imap4r1.h" - #include "linkage.h" - #endif -+#endif - #include "asterisk/lock.h" - #include "asterisk/file.h" - #include "asterisk/logger.h" -@@ -4708,7 +4714,11 @@ static int init_mailstream(struct vm_state *vms, int box) - - if (delimiter == '\0') { /* did not probe the server yet */ - char *cp; -+#if USE_SYSTEM_IMAP -+#include -+#else - #include "linkage.c" -+#endif - /* Connect to INBOX first to get folders delimiter */ - imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1); - stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL); -diff --git a/configure b/configure -index f296f05..fb3bdbf 100755 ---- a/configure -+++ b/configure -@@ -18652,7 +18652,10 @@ fi - fi - - if test "${USE_IMAP_TK}" != "no"; then -- if test "${IMAP_TK_DIR}" = ""; then -+ if test "${IMAP_TK_DIR}" = "system" ; then -+ { echo "$as_me:$LINENO: Checking for system c-client library..." >&5 -+echo "$as_me: Checking for system c-client library..." >&6;} -+ elif test "${IMAP_TK_DIR}" = ""; then - IMAP_TK_DIR=`pwd`"/../imap-2004g" - if test -n "${IMAP_TK_MANDATORY}"; then - { echo "$as_me:$LINENO: The --with-imap option does not search your system for installed" >&5 -@@ -18665,22 +18668,34 @@ echo "$as_me: the configure script will assume you have placed built the c-clien - echo "$as_me: files at ${IMAP_TK_DIR}, as outlined in the doc/imapstorage.txt file." >&6;} - fi - fi -- { echo "$as_me:$LINENO: checking for UW IMAP Toolkit c-client library" >&5 -+ if test "${IMAP_TK_DIR}" != "system" ; then -+ { echo "$as_me:$LINENO: checking for UW IMAP Toolkit c-client library" >&5 - echo $ECHO_N "checking for UW IMAP Toolkit c-client library... $ECHO_C" >&6; } -+ fi - saved_cppflags="${CPPFLAGS}" - saved_libs="${LIBS}" -- if test -f ${IMAP_TK_DIR}/c-client/LDFLAGS ; then -+ if test "${IMAP_TK_DIR}" = "system" ; then -+ imap_ldflags="" -+ imap_libs="-lc-client" -+ imap_include="-DUSE_SYSTEM_IMAP" -+ elif test -f ${IMAP_TK_DIR}/c-client/LDFLAGS ; then - imap_ldflags=`cat ${IMAP_TK_DIR}/c-client/LDFLAGS` -+ imap_libs="${IMAP_TK_DIR}/c-client/c-client.a" -+ imap_include="-I${IMAP_TK_DIR}/c-client" - fi -- CPPFLAGS="${CPPFLAGS} -I${IMAP_TK_DIR}/c-client" -- LIBS="${LIBS} ${IMAP_TK_DIR}/c-client/c-client.a "`echo ${imap_ldflags}` -+ CPPFLAGS="${CPPFLAGS} ${imap_include}" -+ LIBS="${LIBS} ${imap_libs} "`echo ${imap_ldflags}` - cat >conftest.$ac_ext <<_ACEOF - /* confdefs.h. */ - _ACEOF - cat confdefs.h >>conftest.$ac_ext - cat >>conftest.$ac_ext <<_ACEOF - /* end confdefs.h. */ --#include "c-client.h" -+#ifdef USE_SYSTEM_IMAP -+ #include -+ #else -+ #include "c-client.h" -+ #endif - void mm_searched (MAILSTREAM *stream,unsigned long number) - { - } -@@ -18773,7 +18788,11 @@ _ACEOF - cat confdefs.h >>conftest.$ac_ext - cat >>conftest.$ac_ext <<_ACEOF - /* end confdefs.h. */ --#include "c-client.h" -+#ifdef USE_SYSTEM_IMAP -+ #include -+ #else -+ #include "c-client.h" -+ #endif - void mm_searched (MAILSTREAM *stream,unsigned long number) - { - } -@@ -18865,8 +18884,8 @@ rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - if test "${ac_cv_imap_tk}" = "yes"; then - { echo "$as_me:$LINENO: result: yes" >&5 - echo "${ECHO_T}yes" >&6; } -- IMAP_TK_LIB="${IMAP_TK_DIR}/c-client/c-client.a "`echo ${imap_ldflags}` -- IMAP_TK_INCLUDE="-I${IMAP_TK_DIR}/c-client" -+ IMAP_TK_LIB="${imap_libs}"`echo ${imap_ldflags}` -+ IMAP_TK_INCLUDE="${imap_include}" - PBX_IMAP_TK=1 - - cat >>confdefs.h <<\_ACEOF -diff --git a/configure.ac b/configure.ac -index 8f1d95c..4b074ba 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -467,7 +467,9 @@ if test "${PBX_IKSEMEL}" = 1; then - fi - - if test "${USE_IMAP_TK}" != "no"; then -- if test "${IMAP_TK_DIR}" = ""; then -+ if test "${IMAP_TK_DIR}" = "system" ; then -+ AC_MSG_NOTICE([Checking for system c-client library...]) -+ elif test "${IMAP_TK_DIR}" = ""; then - IMAP_TK_DIR=`pwd`"/../imap-2004g" - if test -n "${IMAP_TK_MANDATORY}"; then - AC_MSG_NOTICE([The --with-imap option does not search your system for installed]) -@@ -476,17 +478,29 @@ if test "${USE_IMAP_TK}" != "no"; then - AC_MSG_NOTICE([files at ${IMAP_TK_DIR}, as outlined in the doc/imapstorage.txt file.]) - fi - fi -- AC_MSG_CHECKING(for UW IMAP Toolkit c-client library) -+ if test "${IMAP_TK_DIR}" != "system" ; then -+ AC_MSG_CHECKING(for UW IMAP Toolkit c-client library) -+ fi - saved_cppflags="${CPPFLAGS}" - saved_libs="${LIBS}" -- if test -f ${IMAP_TK_DIR}/c-client/LDFLAGS ; then -+ if test "${IMAP_TK_DIR}" = "system" ; then -+ imap_ldflags="" -+ imap_libs="-lc-client" -+ imap_include="-DUSE_SYSTEM_IMAP" -+ elif test -f ${IMAP_TK_DIR}/c-client/LDFLAGS ; then - imap_ldflags=`cat ${IMAP_TK_DIR}/c-client/LDFLAGS` -+ imap_libs="${IMAP_TK_DIR}/c-client/c-client.a" -+ imap_include="-I${IMAP_TK_DIR}/c-client" - fi -- CPPFLAGS="${CPPFLAGS} -I${IMAP_TK_DIR}/c-client" -- LIBS="${LIBS} ${IMAP_TK_DIR}/c-client/c-client.a "`echo ${imap_ldflags}` -+ CPPFLAGS="${CPPFLAGS} ${imap_include}" -+ LIBS="${LIBS} ${imap_libs} "`echo ${imap_ldflags}` - AC_LINK_IFELSE( - AC_LANG_PROGRAM( -- [#include "c-client.h" -+ [#ifdef USE_SYSTEM_IMAP -+ #include -+ #else -+ #include "c-client.h" -+ #endif - void mm_searched (MAILSTREAM *stream,unsigned long number) - { - } -@@ -542,7 +556,11 @@ if test "${USE_IMAP_TK}" != "no"; then - if test "${ac_cv_imap_tk}" = "yes"; then - AC_LINK_IFELSE( - AC_LANG_PROGRAM( -- [#include "c-client.h" -+ [#ifdef USE_SYSTEM_IMAP -+ #include -+ #else -+ #include "c-client.h" -+ #endif - void mm_searched (MAILSTREAM *stream,unsigned long number) - { - } -@@ -600,8 +618,8 @@ if test "${USE_IMAP_TK}" != "no"; then - LIBS="${saved_libs}" - if test "${ac_cv_imap_tk}" = "yes"; then - AC_MSG_RESULT(yes) -- IMAP_TK_LIB="${IMAP_TK_DIR}/c-client/c-client.a "`echo ${imap_ldflags}` -- IMAP_TK_INCLUDE="-I${IMAP_TK_DIR}/c-client" -+ IMAP_TK_LIB="${imap_libs}"`echo ${imap_ldflags}` -+ IMAP_TK_INCLUDE="${imap_include}" - PBX_IMAP_TK=1 - AC_DEFINE([HAVE_IMAP_TK], 1, [Define if your system has the UW IMAP Toolkit c-client library.]) - if test "${ac_cv_imap_tk2006}" = "yes"; then -diff --git a/doc/imapstorage.txt b/doc/imapstorage.txt -index 1e5484b..d292b5d 100644 ---- a/doc/imapstorage.txt -+++ b/doc/imapstorage.txt -@@ -38,10 +38,11 @@ Installation Notes - -------------------------------------- - University of Washington IMAP C-Client - -------------------------------------- --You will need a source distribution of University of Washington's IMAP --c-client (http://www.washington.edu/imap/). Asterisk supports both the --2004 and 2006 versions of c-client, however mail_expunge_full is enabled --in the 2006 version. -+If you do not have the University of Washington's IMAP c-client -+installed on your system, you will need to download the c-client -+source distribution (http://www.washington.edu/imap/) and compile it. -+Asterisk supports both the 2004 and 2006 versions of c-client, however -+mail_expunge_full is enabled in the 2006 version. - - Note that Asterisk only uses the 'client' portion of the UW IMAP toolkit, - but building it also builds an IMAP server and various other utilities. -@@ -68,20 +69,17 @@ need to run 'make install'. - ------------------ - Compiling Asterisk - ------------------ -- --Configure with ./configure --with-imap=/usr/src/imap --or where ever you built thfe UWashington IMAP Toolkit. When you run --'make menuselect', choose 'Voicemail Build Options' and the --IMAP_STORAGE option should be available for selection. -- --Note that the --with-imap option will NOT search your system for an --installed copy of the IMAP Toolkit c-client library; the Asterisk --Makefiles and configure script are designed to build against an --unpacked and compiled source tree of the IMAP Toolkit, not a binary --distribution. -- --After selecting it, use the 'x' key to exit menuselect and save --your changes, and the build/install Asterisk normally. -+To use the system c-client library, configure Asterisk with -+./configure --with-imap=system. If you downloaded the c-client source -+and compiled it according to the above instructions, configure -+Asterisk with with ./configure --with-imap=/usr/src/imap or where ever -+you built the UWashington IMAP Toolkit. When you run 'make -+menuselect', choose 'Voicemail Build Options' and the IMAP_STORAGE -+option should be available for selection. -+ -+After selecting the IMAP_STORAGE option, use the 'x' key to exit -+menuselect and save your changes, and the build/install Asterisk -+normally. - - --------------------- - Modify voicemail.conf --- -1.5.3.6 - diff --git a/asterisk-1.4.16.2-alternate-extensions.patch b/asterisk-1.4.16.2-alternate-extensions.patch new file mode 100644 index 0000000..1af5daa --- /dev/null +++ b/asterisk-1.4.16.2-alternate-extensions.patch @@ -0,0 +1,49 @@ +From 94016f05e10e8787fb7a7a8803a0ab4216588ed0 Mon Sep 17 00:00:00 2001 +From: Jeffrey C. Ollie +Date: Thu, 8 Nov 2007 16:32:56 -0600 +Subject: [PATCH] Allow alternate extensions to be specified in users.conf + +--- + pbx/pbx_config.c | 14 ++++++++++++++ + 1 files changed, 14 insertions(+), 0 deletions(-) + +diff --git a/pbx/pbx_config.c b/pbx/pbx_config.c +index 6d0ec2e..969c77a 100644 +--- a/pbx/pbx_config.c ++++ b/pbx/pbx_config.c +@@ -2357,11 +2357,14 @@ static void pbx_load_users(void) + { + struct ast_config *cfg; + char *cat, *chan; ++ char *ext; + const char *zapchan; + const char *hasexten; ++ const char *altexts; + char tmp[256]; + char iface[256]; + char zapcopy[256]; ++ char altcopy[256]; + char *c; + int len; + int hasvoicemail; +@@ -2441,6 +2444,17 @@ static void pbx_load_users(void) + } else { + ast_add_extension2(con, 0, cat, 1, NULL, NULL, "Dial", strdup("${HINT}"), ast_free, registrar); + } ++ altexts = ast_variable_retrieve(cfg, cat, "alternateexts"); ++ if (!ast_strlen_zero(altexts)) { ++ snprintf(tmp, sizeof(tmp), "%s|1", cat); ++ ast_copy_string(altcopy, altexts, sizeof(altcopy)); ++ c = altcopy; ++ ext = strsep(&c, ","); ++ while (ext) { ++ ast_add_extension2(con, 0, ext, 1, NULL, NULL, "Goto", strdup(tmp), ast_free, registrar); ++ ext = strsep(&c, ","); ++ } ++ } + } + } + ast_config_destroy(cfg); +-- +1.5.3.7 + diff --git a/asterisk-1.4.16.2-alternate-voicemail.patch b/asterisk-1.4.16.2-alternate-voicemail.patch new file mode 100644 index 0000000..7d18981 --- /dev/null +++ b/asterisk-1.4.16.2-alternate-voicemail.patch @@ -0,0 +1,47 @@ +From c83d8069197a0c8545580cc715867e09fa3cc162 Mon Sep 17 00:00:00 2001 +From: Jeffrey C. Ollie +Date: Thu, 8 Nov 2007 15:52:36 -0600 +Subject: [PATCH] Modify modules.conf so that different voicemail modules can be loaded. + +--- + configs/modules.conf.sample | 27 +++++++++++++++++++++++++++ + 1 files changed, 27 insertions(+), 0 deletions(-) + +diff --git a/configs/modules.conf.sample b/configs/modules.conf.sample +index 0c92e1d..43c3f48 100644 +--- a/configs/modules.conf.sample ++++ b/configs/modules.conf.sample +@@ -36,3 +36,30 @@ load => res_musiconhold.so + ; + noload => chan_alsa.so + ;noload => chan_oss.so ++ ++; ++; Voicemail storage selection ++; ++; Comment out the "noload" lines for the voicemail ++; storage system that you want. Leave the ones that ++; you don't want uncommented. ++; ++ ++; ++; Voicemail with IMAP storage ++; ++noload => app_directory_imap.so ++noload => app_voicemail_imap.so ++ ++; ++; Voicemail with ODBC storage ++; ++noload => app_directory_odbc.so ++noload => app_voicemail_odbc.so ++ ++; ++; Voicemail with filesystem storage ++; ++;noload => app_directory_plain.so ++;noload => app_voicemail_plain.so ++ +-- +1.5.3.7 + diff --git a/asterisk-1.4.16.2-appconference.patch b/asterisk-1.4.16.2-appconference.patch new file mode 100644 index 0000000..b5b08ab --- /dev/null +++ b/asterisk-1.4.16.2-appconference.patch @@ -0,0 +1,10238 @@ +From e1b0800664d4cb8c7d87e04f23bf3a5b98f6e1f0 Mon Sep 17 00:00:00 2001 +From: Jeffrey C. Ollie +Date: Sun, 18 Nov 2007 22:23:17 -0600 +Subject: [PATCH] Add app_conference rev 911 + +--- + apps/Makefile | 14 + + apps/app_conference.c | 113 ++ + apps/conference/CLI.txt | 96 + + apps/conference/Flags.txt | 31 + + apps/conference/LICENSE | 341 ++++ + apps/conference/README | 125 ++ + apps/conference/README.videoswitch | 87 + + apps/conference/TODO | 4 + + apps/conference/app_conference.h | 244 +++ + apps/conference/cli.c | 1265 ++++++++++++++ + apps/conference/cli.h | 99 ++ + apps/conference/common.h | 63 + + apps/conference/conf_frame.h | 73 + + apps/conference/conference.c | 2923 +++++++++++++++++++++++++++++++ + apps/conference/conference.h | 194 +++ + apps/conference/frame.c | 679 ++++++++ + apps/conference/frame.h | 75 + + apps/conference/member.c | 3332 ++++++++++++++++++++++++++++++++++++ + apps/conference/member.h | 315 ++++ + 19 files changed, 10073 insertions(+), 0 deletions(-) + create mode 100644 apps/app_conference.c + create mode 100644 apps/conference/CLI.txt + create mode 100644 apps/conference/Flags.txt + create mode 100644 apps/conference/LICENSE + create mode 100644 apps/conference/README + create mode 100644 apps/conference/README.videoswitch + create mode 100644 apps/conference/TODO + create mode 100644 apps/conference/app_conference.h + create mode 100644 apps/conference/cli.c + create mode 100644 apps/conference/cli.h + create mode 100644 apps/conference/common.h + create mode 100644 apps/conference/conf_frame.h + create mode 100644 apps/conference/conference.c + create mode 100644 apps/conference/conference.h + create mode 100644 apps/conference/frame.c + create mode 100644 apps/conference/frame.h + create mode 100644 apps/conference/member.c + create mode 100644 apps/conference/member.h + +diff --git a/apps/Makefile b/apps/Makefile +index 4006c9a..cf33505 100644 +--- a/apps/Makefile ++++ b/apps/Makefile +@@ -44,4 +44,18 @@ endif + + all: _all + ++app_conference.o: ASTCFLAGS+=-DSILDET=2 -Iconference ++ ++conference/conference.o: ASTCFLAGS+=-DSILDET=2 -Iconference ++ ++conference/member.o: ASTCFLAGS+=-DSILDET=2 -Iconference ++ ++conference/frame.o: ASTCFLAGS+=-DSILDET=2 -Iconference ++ ++conference/cli.o: ASTCFLAGS+=-DSILDET=2 -Iconference ++ ++app_conference.so: app_conference.o conference/conference.o conference/member.o conference/frame.o conference/cli.o ++ $(ECHO_PREFIX) echo " [LD] $^ -> $@" ++ $(CMD_PREFIX) $(CXX) $(PTHREAD_CFLAGS) $(ASTLDFLAGS) $(SOLINK) -o $@ $^ -lspeex ++ + include $(ASTTOPDIR)/Makefile.moddir_rules +diff --git a/apps/app_conference.c b/apps/app_conference.c +new file mode 100644 +index 0000000..824d5dd +--- /dev/null ++++ b/apps/app_conference.c +@@ -0,0 +1,113 @@ ++/* ++ * app_conference ++ * ++ * A channel independent conference application for Asterisk ++ * ++ * Copyright (C) 2002, 2003 Junghanns.NET GmbH ++ * Copyright (C) 2003, 2004 HorizonLive.com, Inc. ++ * Copyright (C) 2005, 2006 HorizonWimba, Inc. ++ * Copyright (C) 2007 Wimba, Inc. ++ * ++ * Klaus-Peter Junghanns ++ * ++ * Video Conferencing support added by ++ * Neil Stratford ++ * Copyright (C) 2005, 2005 Vipadia Limited ++ * ++ * VAD driven video conferencing, text message support ++ * and miscellaneous enhancements added by ++ * Mihai Balea ++ * ++ * This program may be modified and distributed under the ++ * terms of the GNU General Public License. You should have received ++ * a copy of the GNU General Public License along with this ++ * program; if not, write to the Free Software Foundation, Inc. ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include "asterisk.h" ++ ++// SVN revision number, provided by make ++#ifndef REVISION ++#define REVISION "unknown" ++#endif ++ ++static char *revision = REVISION; ++ ++ASTERISK_FILE_VERSION(__FILE__, REVISION) ++ ++#include "app_conference.h" ++#include "common.h" ++ ++/* ++ * a conference has n + 1 threads, where n is the number of ++ * members and 1 is a conference thread which sends audio ++ * back to the members. ++ * ++ * each member thread reads frames from the channel and ++ * add's them to the member's frame queue. ++ * ++ * the conference thread reads frames from each speaking members ++ * queue, mixes them, and then re-queues them for the member thread ++ * to send back to the user. ++ */ ++ ++static char *app = "Conference"; ++static char *synopsis = "Channel Independent Conference"; ++static char *descrip = "Channel Independent Conference Application"; ++ ++static int app_conference_main(struct ast_channel* chan, void* data) ++{ ++ int res ; ++ struct ast_module_user *u ; ++ ++ u = ast_module_user_add(chan); ++ ++ // call member thread function ++ res = member_exec( chan, data ) ; ++ ++ ast_module_user_remove(u); ++ ++ return res ; ++} ++ ++static int unload_module( void ) ++{ ++ ast_log( LOG_NOTICE, "unloading app_conference module\n" ) ; ++ ++ ast_module_user_hangup_all(); ++ ++ unregister_conference_cli() ; ++ ++ return ast_unregister_application( app ) ; ++} ++ ++static int load_module( void ) ++{ ++ ast_log( LOG_NOTICE, "Loading app_conference module, revision=%s\n", revision) ; ++ ++ init_conference() ; ++ ++ register_conference_cli() ; ++ ++ return ast_register_application( app, app_conference_main, synopsis, descrip ) ; ++} ++ ++// increment a timeval by ms milliseconds ++void add_milliseconds(struct timeval* tv, long ms) ++{ ++ // add the microseconds to the microseconds field ++ tv->tv_usec += ( ms * 1000 ) ; ++ ++ // calculate the number of seconds to increment ++ long s = ( tv->tv_usec / 1000000 ) ; ++ ++ // adjust the microsends field ++ if ( s > 0 ) tv->tv_usec -= ( s * 1000000 ) ; ++ ++ // increment the seconds field ++ tv->tv_sec += s ; ++} ++ ++AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, ++ "Channel Independent Conference Application"); +diff --git a/apps/conference/CLI.txt b/apps/conference/CLI.txt +new file mode 100644 +index 0000000..48acf5a +--- /dev/null ++++ b/apps/conference/CLI.txt +@@ -0,0 +1,96 @@ ++Current command line used by app_conference ++Please note that app_conference is still work in progress, so this document might be outdated. As always, the source code is the definitive reference (cli.[ch] and conference.[ch]). You can also obtain help/usage information by using Asterisk CLI help system ("help conference") ++ ++A member in a conference can be referred to by its id or by its channel. Id is a positive number assigned automatically when the member joins the conference. Channel is Asterisk channel identifier. To obtain a list of member ids and channels in a conference, do: ++ ++*CLI> conference list ++ ++ ++- conference debug: enable debugging for a conference ++ usage: conference debug [ on | off ] ++ ++- conference end: stops a conference ++ usage: conference end ++ ++- conference kick: kick member from a conference ++ usage: conference kick ++ ++- conference kickchannel: kick channel from a conference ++ usage: conference kickchannel ++ ++- conference list: list members of a conference. If no conference is specified, all conferences are listed ++ usage: conference list {conference_name} ++ ++- conference lock: locks incoming video to a member ++ usage: conference lock ++ ++- conference lockchannel: locks incoming video to a channel ++ usage: conference lockchannel ++ ++- conference mute: mute member in a conference ++ usage: conference mute ++ ++- conference mutechannel: mute channel in a conference ++ usage: conference mutechannel ++ ++- conference play sound: play a sound to a conference member ++ usage: conference play sound [mute] ++ If mute is specified, all other audio is muted while the sound is played back. ++ ++- conference restart: kick all users in all conferences ++ usage: conference restart ++ ++- conference set default: sets default video source ++ usage: conference set default ++ use a negative value for member if you want to clear the default ++ ++- conference set defaultchannel: sets default video source channel ++ usage: conference set defaultchannel ++ ++- conference show stats: show conference stats ++ usage: conference show stats ++ ++- conference text: sends a text message to a member. Depends on the member's channel capabilities. ++ usage: conference text ++ ++- conference textbroadcast: sends a text message to all members in a conference ++ usage: conference textbroadcast ++ ++- conference textchannel: sends a text message to a channel ++ usage: conference textchannel ++ ++- conference unlock: unlocks incoming video ++ usage: conference unlock ++ ++- conference unmute: unmute member in a conference ++ usage: conference unmute ++ ++- conference unmutechannel: unmute channel in a conference ++ usage: conference unmutechannel ++ ++- conference video mute: mutes video from a member ++ usage: conference video mute ++ ++- conference video mutechannel: mutes video from a channel ++ usage: conference video mutechannel ++ ++- conference video unmute: unmutes video from a member ++ usage: conference video unmute ++ ++- conference video unmutechannel: unmutes video from a channel ++ usage: conference video unmutechannel ++ ++- conference viewchannel: switch video for a channel in a conference ++ usage: conference viewchannel ++ ++- conference viewstream: switch video for a member a conference ++ usage: conference viewstream ++ ++- conference drive: drive VAD video switching of destination member using audio from source member ++ usage: conference drive [destination member] ++ If destination member is missing or negative, break existing connection ++ ++- conference drivechannel: drive VAD video switching of destination channel using audio from source channel ++ usage: conference drivechannel [destination channel] ++ If destination channel is missing, break existing connection ++ +diff --git a/apps/conference/Flags.txt b/apps/conference/Flags.txt +new file mode 100644 +index 0000000..dbb9732 +--- /dev/null ++++ b/apps/conference/Flags.txt +@@ -0,0 +1,31 @@ ++Current dialplan flags used by app_conference ++Please note that app_conference is still work in progress, so this document might be outdated. As always, the source code is the definitive reference (member.c in create_member()) ++ ++Mute/no receive options: ++'C' : member starts with video muted ++'c' : member starts unable to receive video ++'L' : member starts with audio muted ++'l' : member starts unable to receive audio ++ ++Speex preprocessing options (right now app_conference does preprocessing only for Zaptel members): ++'V' : enable speex preprocessing Voice Activity Detection ++'D' : enable speex preprocessing De-noise ++'A' : enable speex preprocessing Automatic Gain Control ++'T' : member connects through Zaptel, so speex preprocessing should be enabled ++ ++DTMF options: ++'X' : enable DTMF switch: video can be switched by users using DTMF. Do not use with 'S'. ++'R' : enable DTMF relay: DTMF tones generate a manager event ++If neither 'X' nor 'R' are present, DTMF tones will be forwarded to all members in the conference ++ ++Moderator/video switch options: ++'M' : member is a "moderator". When a moderator quits, all members are kicked and the conference is disabled. ++'S' : member accepts VAD controlled video switching. Do not use with 'X'. ++ ++Miscellaneous: ++'t' : member accepts text based control messages. The messages are described in a separate document ++'N' : Assume that the member starts off with camera disabled. ++ ++Future development (these are not implemented yet): ++'x' : marked member. We plan to change the behavior so that when ALL moderators quit, all members that are marked will get kicked. Other members in the conference are not affected. ++ +diff --git a/apps/conference/LICENSE b/apps/conference/LICENSE +new file mode 100644 +index 0000000..a52b16e +--- /dev/null ++++ b/apps/conference/LICENSE +@@ -0,0 +1,341 @@ ++ ++ GNU GENERAL PUBLIC LICENSE ++ Version 2, June 1991 ++ ++ Copyright (C) 1989, 1991 Free Software Foundation, Inc. ++ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ Everyone is permitted to copy and distribute verbatim copies ++ of this license document, but changing it is not allowed. ++ ++ Preamble ++ ++ The licenses for most software are designed to take away your ++freedom to share and change it. By contrast, the GNU General Public ++License is intended to guarantee your freedom to share and change free ++software--to make sure the software is free for all its users. This ++General Public License applies to most of the Free Software ++Foundation's software and to any other program whose authors commit to ++using it. (Some other Free Software Foundation software is covered by ++the GNU Library General Public License instead.) You can apply it to ++your programs, too. ++ ++ When we speak of free software, we are referring to freedom, not ++price. Our General Public Licenses are designed to make sure that you ++have the freedom to distribute copies of free software (and charge for ++this service if you wish), that you receive source code or can get it ++if you want it, that you can change the software or use pieces of it ++in new free programs; and that you know you can do these things. ++ ++ To protect your rights, we need to make restrictions that forbid ++anyone to deny you these rights or to ask you to surrender the rights. ++These restrictions translate to certain responsibilities for you if you ++distribute copies of the software, or if you modify it. ++ ++ For example, if you distribute copies of such a program, whether ++gratis or for a fee, you must give the recipients all the rights that ++you have. You must make sure that they, too, receive or can get the ++source code. And you must show them these terms so they know their ++rights. ++ ++ We protect your rights with two steps: (1) copyright the software, and ++(2) offer you this license which gives you legal permission to copy, ++distribute and/or modify the software. ++ ++ Also, for each author's protection and ours, we want to make certain ++that everyone understands that there is no warranty for this free ++software. If the software is modified by someone else and passed on, we ++want its recipients to know that what they have is not the original, so ++that any problems introduced by others will not reflect on the original ++authors' reputations. ++ ++ Finally, any free program is threatened constantly by software ++patents. We wish to avoid the danger that redistributors of a free ++program will individually obtain patent licenses, in effect making the ++program proprietary. To prevent this, we have made it clear that any ++patent must be licensed for everyone's free use or not licensed at all. ++ ++ The precise terms and conditions for copying, distribution and ++modification follow. ++ ++ GNU GENERAL PUBLIC LICENSE ++ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION ++ ++ 0. This License applies to any program or other work which contains ++a notice placed by the copyright holder saying it may be distributed ++under the terms of this General Public License. The "Program", below, ++refers to any such program or work, and a "work based on the Program" ++means either the Program or any derivative work under copyright law: ++that is to say, a work containing the Program or a portion of it, ++either verbatim or with modifications and/or translated into another ++language. (Hereinafter, translation is included without limitation in ++the term "modification".) Each licensee is addressed as "you". ++ ++Activities other than copying, distribution and modification are not ++covered by this License; they are outside its scope. The act of ++running the Program is not restricted, and the output from the Program ++is covered only if its contents constitute a work based on the ++Program (independent of having been made by running the Program). ++Whether that is true depends on what the Program does. ++ ++ 1. You may copy and distribute verbatim copies of the Program's ++source code as you receive it, in any medium, provided that you ++conspicuously and appropriately publish on each copy an appropriate ++copyright notice and disclaimer of warranty; keep intact all the ++notices that refer to this License and to the absence of any warranty; ++and give any other recipients of the Program a copy of this License ++along with the Program. ++ ++You may charge a fee for the physical act of transferring a copy, and ++you may at your option offer warranty protection in exchange for a fee. ++ ++ 2. You may modify your copy or copies of the Program or any portion ++of it, thus forming a work based on the Program, and copy and ++distribute such modifications or work under the terms of Section 1 ++above, provided that you also meet all of these conditions: ++ ++ a) You must cause the modified files to carry prominent notices ++ stating that you changed the files and the date of any change. ++ ++ b) You must cause any work that you distribute or publish, that in ++ whole or in part contains or is derived from the Program or any ++ part thereof, to be licensed as a whole at no charge to all third ++ parties under the terms of this License. ++ ++ c) If the modified program normally reads commands interactively ++ when run, you must cause it, when started running for such ++ interactive use in the most ordinary way, to print or display an ++ announcement including an appropriate copyright notice and a ++ notice that there is no warranty (or else, saying that you provide ++ a warranty) and that users may redistribute the program under ++ these conditions, and telling the user how to view a copy of this ++ License. (Exception: if the Program itself is interactive but ++ does not normally print such an announcement, your work based on ++ the Program is not required to print an announcement.) ++ ++These requirements apply to the modified work as a whole. If ++identifiable sections of that work are not derived from the Program, ++and can be reasonably considered independent and separate works in ++themselves, then this License, and its terms, do not apply to those ++sections when you distribute them as separate works. But when you ++distribute the same sections as part of a whole which is a work based ++on the Program, the distribution of the whole must be on the terms of ++this License, whose permissions for other licensees extend to the ++entire whole, and thus to each and every part regardless of who wrote it. ++ ++Thus, it is not the intent of this section to claim rights or contest ++your rights to work written entirely by you; rather, the intent is to ++exercise the right to control the distribution of derivative or ++collective works based on the Program. ++ ++In addition, mere aggregation of another work not based on the Program ++with the Program (or with a work based on the Program) on a volume of ++a storage or distribution medium does not bring the other work under ++the scope of this License. ++ ++ 3. You may copy and distribute the Program (or a work based on it, ++under Section 2) in object code or executable form under the terms of ++Sections 1 and 2 above provided that you also do one of the following: ++ ++ a) Accompany it with the complete corresponding machine-readable ++ source code, which must be distributed under the terms of Sections ++ 1 and 2 above on a medium customarily used for software interchange; or, ++ ++ b) Accompany it with a written offer, valid for at least three ++ years, to give any third party, for a charge no more than your ++ cost of physically performing source distribution, a complete ++ machine-readable copy of the corresponding source code, to be ++ distributed under the terms of Sections 1 and 2 above on a medium ++ customarily used for software interchange; or, ++ ++ c) Accompany it with the information you received as to the offer ++ to distribute corresponding source code. (This alternative is ++ allowed only for noncommercial distribution and only if you ++ received the program in object code or executable form with such ++ an offer, in accord with Subsection b above.) ++ ++The source code for a work means the preferred form of the work for ++making modifications to it. For an executable work, complete source ++code means all the source code for all modules it contains, plus any ++associated interface definition files, plus the scripts used to ++control compilation and installation of the executable. However, as a ++special exception, the source code distributed need not include ++anything that is normally distributed (in either source or binary ++form) with the major components (compiler, kernel, and so on) of the ++operating system on which the executable runs, unless that component ++itself accompanies the executable. ++ ++If distribution of executable or object code is made by offering ++access to copy from a designated place, then offering equivalent ++access to copy the source code from the same place counts as ++distribution of the source code, even though third parties are not ++compelled to copy the source along with the object code. ++ ++ 4. You may not copy, modify, sublicense, or distribute the Program ++except as expressly provided under this License. Any attempt ++otherwise to copy, modify, sublicense or distribute the Program is ++void, and will automatically terminate your rights under this License. ++However, parties who have received copies, or rights, from you under ++this License will not have their licenses terminated so long as such ++parties remain in full compliance. ++ ++ 5. You are not required to accept this License, since you have not ++signed it. However, nothing else grants you permission to modify or ++distribute the Program or its derivative works. These actions are ++prohibited by law if you do not accept this License. Therefore, by ++modifying or distributing the Program (or any work based on the ++Program), you indicate your acceptance of this License to do so, and ++all its terms and conditions for copying, distributing or modifying ++the Program or works based on it. ++ ++ 6. Each time you redistribute the Program (or any work based on the ++Program), the recipient automatically receives a license from the ++original licensor to copy, distribute or modify the Program subject to ++these terms and conditions. You may not impose any further ++restrictions on the recipients' exercise of the rights granted herein. ++You are not responsible for enforcing compliance by third parties to ++this License. ++ ++ 7. If, as a consequence of a court judgment or allegation of patent ++infringement or for any other reason (not limited to patent issues), ++conditions are imposed on you (whether by court order, agreement or ++otherwise) that contradict the conditions of this License, they do not ++excuse you from the conditions of this License. If you cannot ++distribute so as to satisfy simultaneously your obligations under this ++License and any other pertinent obligations, then as a consequence you ++may not distribute the Program at all. For example, if a patent ++license would not permit royalty-free redistribution of the Program by ++all those who receive copies directly or indirectly through you, then ++the only way you could satisfy both it and this License would be to ++refrain entirely from distribution of the Program. ++ ++If any portion of this section is held invalid or unenforceable under ++any particular circumstance, the balance of the section is intended to ++apply and the section as a whole is intended to apply in other ++circumstances. ++ ++It is not the purpose of this section to induce you to infringe any ++patents or other property right claims or to contest validity of any ++such claims; this section has the sole purpose of protecting the ++integrity of the free software distribution system, which is ++implemented by public license practices. Many people have made ++generous contributions to the wide range of software distributed ++through that system in reliance on consistent application of that ++system; it is up to the author/donor to decide if he or she is willing ++to distribute software through any other system and a licensee cannot ++impose that choice. ++ ++This section is intended to make thoroughly clear what is believed to ++be a consequence of the rest of this License. ++ ++ 8. If the distribution and/or use of the Program is restricted in ++certain countries either by patents or by copyrighted interfaces, the ++original copyright holder who places the Program under this License ++may add an explicit geographical distribution limitation excluding ++those countries, so that distribution is permitted only in or among ++countries not thus excluded. In such case, this License incorporates ++the limitation as if written in the body of this License. ++ ++ 9. The Free Software Foundation may publish revised and/or new versions ++of the General Public License from time to time. Such new versions will ++be similar in spirit to the present version, but may differ in detail to ++address new problems or concerns. ++ ++Each version is given a distinguishing version number. If the Program ++specifies a version number of this License which applies to it and "any ++later version", you have the option of following the terms and conditions ++either of that version or of any later version published by the Free ++Software Foundation. If the Program does not specify a version number of ++this License, you may choose any version ever published by the Free Software ++Foundation. ++ ++ 10. If you wish to incorporate parts of the Program into other free ++programs whose distribution conditions are different, write to the author ++to ask for permission. For software which is copyrighted by the Free ++Software Foundation, write to the Free Software Foundation; we sometimes ++make exceptions for this. Our decision will be guided by the two goals ++of preserving the free status of all derivatives of our free software and ++of promoting the sharing and reuse of software generally. ++ ++ NO WARRANTY ++ ++ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY ++FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN ++OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES ++PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED ++OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS ++TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE ++PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, ++REPAIR OR CORRECTION. ++ ++ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING ++WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR ++REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, ++INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING ++OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED ++TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY ++YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER ++PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE ++POSSIBILITY OF SUCH DAMAGES. ++ ++ END OF TERMS AND CONDITIONS ++ ++ How to Apply These Terms to Your New Programs ++ ++ If you develop a new program, and you want it to be of the greatest ++possible use to the public, the best way to achieve this is to make it ++free software which everyone can redistribute and change under these terms. ++ ++ To do so, attach the following notices to the program. It is safest ++to attach them to the start of each source file to most effectively ++convey the exclusion of warranty; and each file should have at least ++the "copyright" line and a pointer to where the full notice is found. ++ ++ ++ Copyright (C) 19yy ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++ ++Also add information on how to contact you by electronic and paper mail. ++ ++If the program is interactive, make it output a short notice like this ++when it starts in an interactive mode: ++ ++ Gnomovision version 69, Copyright (C) 19yy name of author ++ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. ++ This is free software, and you are welcome to redistribute it ++ under certain conditions; type `show c' for details. ++ ++The hypothetical commands `show w' and `show c' should show the appropriate ++parts of the General Public License. Of course, the commands you use may ++be called something other than `show w' and `show c'; they could even be ++mouse-clicks or menu items--whatever suits your program. ++ ++You should also get your employer (if you work as a programmer) or your ++school, if any, to sign a "copyright disclaimer" for the program, if ++necessary. Here is a sample; alter the names: ++ ++ Yoyodyne, Inc., hereby disclaims all copyright interest in the program ++ `Gnomovision' (which makes passes at compilers) written by James Hacker. ++ ++ , 1 April 1989 ++ Ty Coon, President of Vice ++ ++This General Public License does not permit incorporating your program into ++proprietary programs. If your program is a subroutine library, you may ++consider it more useful to permit linking proprietary applications with the ++library. If this is what you want to do, use the GNU Library General ++Public License instead of this License. +diff --git a/apps/conference/README b/apps/conference/README +new file mode 100644 +index 0000000..4c103ce +--- /dev/null ++++ b/apps/conference/README +@@ -0,0 +1,125 @@ ++Introduction ++ ++App_conference is a channel-independent conference application. ++It features efficient audio mixing algorithms as well as video selection ++support based on VAD, DTMF or CLI. ++ ++ ++Design goals ++ ++Appconference has several design goals which are different than Meetme: ++ ++ * It does not require a zap channel for timing. ++ * It is very efficient when used with channels which support DTX (silence ++ detection/discontinuous transmission). ++ ++ * It can do VAD on channels which do not support DTX (although this ++ is more expensive than just mixing them, but less expensive then ++ encoding; therefore it might still be a win). ++ * It presents messages on the Monitor interface for determine which ++ speakers are active. ++ ++Mixing design ++ ++ * Minimize encoding/decoding, minimize mixing. ++ * Minimize generational loss from trancoding. ++ * Usual cases are handled very efficiently: ++ o One speaker: That speaker's frame is sent directly to each ++ participant which uses the same codec. It is trancoded ++ _once_ for each additional codec type used by participants. ++ o Two speakers: Each speaker gets the other speaker's frames. ++ The two speaker's frames are decoded and mixed, and then ++ encoded _once_ for each codec type used by participants. ++ ++Video features ++ ++ * Video passthrough: video from selected member is passed to every ++ member of the conference. ++ * Multiple ways to select video ++ - VAD ++ - DTMF from conference members ++ - CLI ++ * Ability to set default video sources and to lock/unlock video sources. ++ ++ ++License ++ ++Naturally, app_conference is GPL. The SVN repository also includes parts of ++libspeex, which is distributed under a BSD-style license. See LICENSE for more ++details. ++ ++ ++Getting app_conference ++ ++app_conference is available via SVN from its own home on sourceforge: ++ ++ * http://sourceforge.net/projects/appconference ++ ++ ++Compiling app_conference ++ ++ * Checkout sources ++ * Modify Makefile to point to your Asterisk include directory ++ * make ++ * sudo make install ++ ++ ++Using app_conference ++ ++There is no configuration file. Conferences are created on-the-fly. ++ ++Dialplan syntax: Conference(ConferenceName/Flags/Priority[/VADSTART/VADCONTINUE]) ++ ++ * ConferenceName: Whatever you want to name the conference ++ * Flags: please see Flags.txt for a comprehensive list of dialplan flags ++ * Priority: Currently ignored; was to be a "speaking priority" so a ++ higher priority caller could "override" others. ++ * VADSTART: Optional: "probability" to use to detect start of speech. ++ * VADCONTINUE: Optional: "probability" to use to detect continuation ++ of speech. ++ ++ ++CLI Commands ++ ++Please look at CLI.txt for a comprehensive list of CLI commands and parameters. ++ ++ ++Manager Events ++ ++ ++app_conference generates several detailed manager events so that applications ++interfacing with the manager API can monitor conferences: ++ ++ * ConferenceState: sent as members begin/end speaking. ++ Channel: The channel ++ State: "speaking" or "silent" ++ ++ * ConferenceDTMF: sent when conference members send DTMF to the conference ++ Channel: The channel ++ Key: The DTMF key send [0-9*#] ++ ++ * ConferenceSoundComplete: send when the conference has finished playing ++ a sound to a user ++ Channel: The channel ++ Sound: The first 255 bytes of the file requested in conference play ++ sound CLI/Mgr command. ++ ++ ++Benchmarking ++ ++It would be nice to have solid benchmarks to present, but a good size ++machine should be able to handle many callers when either (a) they are ++using DTX, or (b) they are listen-only. It's used often with hundreds of ++simultaneous callers. ++ ++ ++Discussion ++ ++The appconference-devel mailing list is the place to discuss everything related ++to app_conference. The bug tracker on SourceForge gets a little bit of ++attention now and then. ++ ++ ++--- ++ ++app_conference is brought to you by the letter q, and the number e +diff --git a/apps/conference/README.videoswitch b/apps/conference/README.videoswitch +new file mode 100644 +index 0000000..2612194 +--- /dev/null ++++ b/apps/conference/README.videoswitch +@@ -0,0 +1,87 @@ ++VideoSwitch ++----------- ++(c) Vipadia Limited 2005-2006 ++Neil Stratford ++ ++Based on app_conference, see README. ++ ++Including contributions from John Martin ++ ++Example use: ++ ++exten => 2300,1,Videoswitch(test/RX) ++ ++This puts the user into a conference 'test' with the options SRX. ++ ++The options are the same as app_conference, except: ++ ++X - enable the caller to switch video stream using DTMF ++R - relay the DTMF to the management interface ++C - Mute video - no video from this client ++c - No Receive video - send no video to this client ++L - Mute audio - no audio from this client ++l - No Receive audio - send no audio to this client ++M - member is moderator - when they leave everyone else is kicked ++ ++Stream selection options: two integers, first is receive id, second is send id. ++Both are optional. ++ ++0-9 - Set initial receive stream to n ++0-9 - Set this stream id to n (will stop any other video with that id already) ++ ++eg: Videoswitch(test/01) will set our id to 1, and we will receive id 0's video ++ ++CLI commands (which may also be invoked from the manager interface ++using the Command action): ++ ++Most commands have two versions, which can either take a member number (obtained from 'videoswitch list') or a channel identifier (such as SIP/2304-1e82). ++ ++fidwell*CLI> help videoswitch ++ videoswitch debug enable debugging for a videoswitch ++ videoswitch kick kick member from a videoswitch ++ videoswitch list list members of a videoswitch ++ videoswitch mute mute member in a videoswitch ++ videoswitch mutechannel mute channel in a videoswitch ++ videoswitch show stats show videoswitch stats ++ videoswitch unmute unmute member in a videoswitch ++videoswitch unmutechannel unmute channel in a videoswitch ++ videoswitch viewchannel switch channel in a videoswitch ++ videoswitch viewstream switch view in a videoswitch ++ ++fidwell*CLI> help videoswitch debug ++usage: videoswitch debug [ on | off ] ++ enable debugging for a videoswitch ++ ++fidwell*CLI> help videoswitch kick ++usage: videoswitch kick ++ kick member form a videoswitch ++ ++fidwell*CLI> help videoswitch list ++usage: videoswitch list {} ++ list members of a videoswitch or list of videoswitches if no name ++ ++fidwell*CLI> help videoswitch mute ++usage: videoswitch mute ++ mute member in a videoswitch ++ ++fidwell*CLI> help videoswitch unmute ++usage: videoswitch unmute ++ unmute member in a videoswitch ++ ++fidwell*CLI> help videoswitch mutechannel ++usage: videoswitch mute ++ mute channel in a videoswitch ++ ++fidwell*CLI> help videoswitch unmutechannel ++usage: videoswitch unmute ++ unmute channel in a videoswitch ++ ++fidwell*CLI> help videoswitch viewchannel ++usage: videoswitch viewchannel ++ channel will receive video stream ++ ++fidwell*CLI> help videoswitch viewstream ++usage: videoswitch viewstream ++ member will receive video stream ++ ++ +diff --git a/apps/conference/TODO b/apps/conference/TODO +new file mode 100644 +index 0000000..121e83e +--- /dev/null ++++ b/apps/conference/TODO +@@ -0,0 +1,4 @@ ++Things we need to do for the next release ++- Enable speex based VAD for all members instead of just for telephone ++members (configurable) ++- Documentation! +diff --git a/apps/conference/app_conference.h b/apps/conference/app_conference.h +new file mode 100644 +index 0000000..17aca58 +--- /dev/null ++++ b/apps/conference/app_conference.h +@@ -0,0 +1,244 @@ ++ ++// $Id: app_conference.h 839 2007-01-17 22:32:03Z sbalea $ ++ ++/* ++ * app_conference ++ * ++ * A channel independent conference application for Asterisk ++ * ++ * Copyright (C) 2002, 2003 Junghanns.NET GmbH ++ * Copyright (C) 2003, 2004 HorizonLive.com, Inc. ++ * Copyright (C) 2005, 2006 HorizonWimba, Inc. ++ * Copyright (C) 2007 Wimba, Inc. ++ * ++ * Klaus-Peter Junghanns ++ * ++ * Video Conferencing support added by ++ * Neil Stratford ++ * Copyright (C) 2005, 2005 Vipadia Limited ++ * ++ * VAD driven video conferencing, text message support ++ * and miscellaneous enhancements added by ++ * Mihai Balea ++ * ++ * This program may be modified and distributed under the ++ * terms of the GNU General Public License. You should have received ++ * a copy of the GNU General Public License along with this ++ * program; if not, write to the Free Software Foundation, Inc. ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _ASTERISK_CONF_H ++#define _ASTERISK_CONF_H ++ ++ ++/* standard includes */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#include ++ ++/* asterisk includes */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++//#include ++#include ++ ++ ++#if (SILDET == 2) ++#include ++#endif ++ ++// ++// app_conference defines ++// ++ ++// debug logging level ++ ++// LOG_NOTICE for debugging, LOG_DEBUG for production ++#ifdef APP_CONFERENCE_DEBUG ++#define AST_CONF_DEBUG LOG_NOTICE ++#else ++#define AST_CONF_DEBUG LOG_DEBUG ++#endif ++ ++// ++// feature defines ++// ++ ++// number of times the last non-silent frame should be ++// repeated after silence starts ++#define AST_CONF_CACHE_LAST_FRAME 1 ++ ++// ++// debug defines ++// ++ ++//#define DEBUG_USE_TIMELOG ++ ++//#define DEBUG_FRAME_TIMESTAMPS ++ ++// #define DEBUG_OUTPUT_PCM ++ ++// ++// !!! THESE CONSTANTS SHOULD BE CLEANED UP AND CLARIFIED !!! ++// ++ ++// ++// sample information for AST_FORMAT_SLINEAR format ++// ++ ++#define AST_CONF_SAMPLE_RATE 8000 ++#define AST_CONF_SAMPLE_SIZE 16 ++#define AST_CONF_FRAME_INTERVAL 20 ++//neils#define AST_CONF_FRAME_INTERVAL 30 ++ ++// ++// so, since we cycle approximately every 20ms, ++// we can compute the following values: ++// ++// 160 samples per 20 ms frame -or- ++// ( 8000 samples-per-second * ( 20 ms / 1000 ms-per-second ) ) = 160 samples ++// ++// 320 bytes ( 2560 bits ) of data 20 ms frame -or- ++// ( 160 samples * 16 bits-per-sample / 8 bits-per-byte ) = 320 bytes ++// ++ ++// 160 samples 16-bit signed linear ++#define AST_CONF_BLOCK_SAMPLES 160 ++ ++// 2 bytes per sample ( i.e. 16-bit ) ++#define AST_CONF_BYTES_PER_SAMPLE 2 ++ ++// 320 bytes for each 160 sample frame of 16-bit audio ++#define AST_CONF_FRAME_DATA_SIZE 320 ++ ++// 1000 ms-per-second / 20 ms-per-frame = 50 frames-per-second ++#define AST_CONF_FRAMES_PER_SECOND ( 1000 / AST_CONF_FRAME_INTERVAL ) ++ ++ ++// ++// buffer and queue values ++// ++ ++// account for friendly offset when allocating buffer for frame ++#define AST_CONF_BUFFER_SIZE ( AST_CONF_FRAME_DATA_SIZE + AST_FRIENDLY_OFFSET ) ++ ++// maximum number of frames queued per member ++#define AST_CONF_MAX_QUEUE 100 ++ ++// max video frames in the queue ++#define AST_CONF_MAX_VIDEO_QUEUE 800 ++ ++// max dtmf frames in the queue ++#define AST_CONF_MAX_DTMF_QUEUE 8 ++ ++// max text frames in the queue ++#define AST_CONF_MAX_TEXT_QUEUE 8 ++ ++// minimum number of frames queued per member ++#define AST_CONF_MIN_QUEUE 0 ++ ++// number of queued frames before we start dropping ++#define AST_CONF_QUEUE_DROP_THRESHOLD 40 ++ ++// number of milliseconds between frame drops ++#define AST_CONF_QUEUE_DROP_TIME_LIMIT 750 ++ ++// ++// timer and sleep values ++// ++ ++// milliseconds we're willing to wait for a channel ++// event before we check for outgoing frames ++#define AST_CONF_WAITFOR_LATENCY 40 ++ ++// milliseconds to sleep before trying to process frames ++#define AST_CONF_CONFERENCE_SLEEP 40 ++ ++// milliseconds to wait between state notification updates ++#define AST_CONF_NOTIFICATION_SLEEP 200 ++ ++// ++// warning threshold values ++// ++ ++// number of frames behind before warning ++#define AST_CONF_OUTGOING_FRAMES_WARN 70 ++ ++// number of milliseconds off AST_CONF_FRAME_INTERVAL before warning ++#define AST_CONF_INTERVAL_WARNING 1000 ++ ++// ++// silence detection values ++// ++ ++// toggle silence detection ++#define ENABLE_SILENCE_DETECTION 1 ++ ++// silence threshold ++#define AST_CONF_SILENCE_THRESHOLD 128 ++ ++// speech tail (delay before dropping silent frames, in ms. ++// #define AST_CONF_SPEECH_TAIL 180 ++ ++// number of frames to ignore speex_preprocess() after speech detected ++#define AST_CONF_SKIP_SPEEX_PREPROCESS 20 ++ ++// our speex probability values ++#define AST_CONF_PROB_START 0.05 ++#define AST_CONF_PROB_CONTINUE 0.02 ++ ++ ++// ++// format translation values ++// ++#ifdef AC_USE_G729A ++ #define AC_SUPPORTED_FORMATS 6 ++ enum { AC_SLINEAR_INDEX = 0, AC_ULAW_INDEX, AC_ALAW_INDEX, AC_GSM_INDEX, AC_SPEEX_INDEX, AC_G729A_INDEX } ; ++#else ++ #define AC_SUPPORTED_FORMATS 5 ++ enum { AC_SLINEAR_INDEX = 0, AC_ULAW_INDEX, AC_ALAW_INDEX, AC_GSM_INDEX, AC_SPEEX_INDEX } ; ++#endif ++ ++// ++// VAD based video switching parameters ++// All time related values are in ms ++// ++ ++// Amount of silence required before we decide somebody stopped talking ++#define AST_CONF_VIDEO_STOP_TIMEOUT 2000 ++ ++// Amount of audio required before we decide somebody started talking ++#define AST_CONF_VIDEO_START_TIMEOUT 2000 ++ ++// ++// Text frame control protocol ++// ++#define AST_CONF_CONTROL_CAMERA_DISABLED "CONTROL:CAMERA_DISABLED" ++#define AST_CONF_CONTROL_CAMERA_ENABLED "CONTROL:CAMERA_ENABLED" ++#define AST_CONF_CONTROL_START_VIDEO "CONTROL:STARTVIDEO" ++#define AST_CONF_CONTROL_STOP_VIDEO "CONTROL:STOPVIDEO" ++#define AST_CONF_CONTROL_STOP_VIDEO_TRANSMIT "CONTROL:STOP_VIDEO_TRANSMIT" ++#define AST_CONF_CONTROL_START_VIDEO_TRANSMIT "CONTROL:START_VIDEO_TRANSMIT" ++ ++// utility functions ++void add_milliseconds( struct timeval* tv, long ms ) ; ++ ++#endif ++ ++ +diff --git a/apps/conference/cli.c b/apps/conference/cli.c +new file mode 100644 +index 0000000..3d88ddf +--- /dev/null ++++ b/apps/conference/cli.c +@@ -0,0 +1,1265 @@ ++ ++// $Id: cli.c 884 2007-06-27 14:56:21Z sbalea $ ++ ++/* ++ * app_conference ++ * ++ * A channel independent conference application for Asterisk ++ * ++ * Copyright (C) 2002, 2003 Junghanns.NET GmbH ++ * Copyright (C) 2003, 2004 HorizonLive.com, Inc. ++ * Copyright (C) 2005, 2006 HorizonWimba, Inc. ++ * Copyright (C) 2007 Wimba, Inc. ++ * ++ * Klaus-Peter Junghanns ++ * ++ * Video Conferencing support added by ++ * Neil Stratford ++ * Copyright (C) 2005, 2005 Vipadia Limited ++ * ++ * VAD driven video conferencing, text message support ++ * and miscellaneous enhancements added by ++ * Mihai Balea ++ * ++ * This program may be modified and distributed under the ++ * terms of the GNU General Public License. You should have received ++ * a copy of the GNU General Public License along with this ++ * program; if not, write to the Free Software Foundation, Inc. ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include "asterisk/autoconfig.h" ++#include "cli.h" ++ ++static char conference_restart_usage[] = ++ "usage: conference restart\n" ++ " kick all users in all conferences\n" ++; ++ ++static struct ast_cli_entry cli_restart = { ++ { "conference", "restart", NULL }, ++ conference_restart, ++ "restart a conference", ++ conference_restart_usage ++} ; ++ ++ ++int conference_restart( int fd, int argc, char *argv[] ) ++{ ++ if ( argc < 2 ) ++ return RESULT_SHOWUSAGE ; ++ ++ kick_all(); ++ return RESULT_SUCCESS ; ++} ++ ++ ++// ++// debug functions ++// ++ ++static char conference_debug_usage[] = ++ "usage: conference debug [ on | off ]\n" ++ " enable debugging for a conference\n" ++; ++ ++static struct ast_cli_entry cli_debug = { ++ { "conference", "debug", NULL }, ++ conference_debug, ++ "enable debugging for a conference", ++ conference_debug_usage ++} ; ++ ++ ++int conference_debug( int fd, int argc, char *argv[] ) ++{ ++ if ( argc < 3 ) ++ return RESULT_SHOWUSAGE ; ++ ++ // get the conference name ++ const char* name = argv[2] ; ++ ++ // get the new state ++ int state = 0 ; ++ ++ if ( argc == 3 ) ++ { ++ // no state specified, so toggle it ++ state = -1 ; ++ } ++ else ++ { ++ if ( strncasecmp( argv[3], "on", 4 ) == 0 ) ++ state = 1 ; ++ else if ( strncasecmp( argv[3], "off", 3 ) == 0 ) ++ state = 0 ; ++ else ++ return RESULT_SHOWUSAGE ; ++ } ++ ++ int new_state = set_conference_debugging( name, state ) ; ++ ++ if ( new_state == 1 ) ++ { ++ ast_cli( fd, "enabled conference debugging, name => %s, new_state => %d\n", ++ name, new_state ) ; ++ } ++ else if ( new_state == 0 ) ++ { ++ ast_cli( fd, "disabled conference debugging, name => %s, new_state => %d\n", ++ name, new_state ) ; ++ } ++ else ++ { ++ // error setting state ++ ast_cli( fd, "\nunable to set debugging state, name => %s\n\n", name ) ; ++ } ++ ++ return RESULT_SUCCESS ; ++} ++ ++// ++// stats functions ++// ++ ++static char conference_show_stats_usage[] = ++ "usage: conference show stats\n" ++ " display stats for active conferences.\n" ++; ++ ++static struct ast_cli_entry cli_show_stats = { ++ { "conference", "show", "stats", NULL }, ++ conference_show_stats, ++ "show conference stats", ++ conference_show_stats_usage ++} ; ++ ++int conference_show_stats( int fd, int argc, char *argv[] ) ++{ ++ if ( argc < 3 ) ++ return RESULT_SHOWUSAGE ; ++ ++ // get count of active conferences ++ int count = get_conference_count() ; ++ ++ ast_cli( fd, "\n\nCONFERENCE STATS, ACTIVE( %d )\n\n", count ) ; ++ ++ // if zero, go no further ++ if ( count <= 0 ) ++ return RESULT_SUCCESS ; ++ ++ // ++ // get the conference stats ++ // ++ ++ // array of stats structs ++ ast_conference_stats stats[ count ] ; ++ ++ // get stats structs ++ count = get_conference_stats( stats, count ) ; ++ ++ // make sure we were able to fetch some ++ if ( count <= 0 ) ++ { ++ ast_cli( fd, "!!! error fetching conference stats, available => %d !!!\n", count ) ; ++ return RESULT_SUCCESS ; ++ } ++ ++ // ++ // output the conference stats ++ // ++ ++ // output header ++ ast_cli( fd, "%-20.20s %-40.40s\n", "Name", "Stats") ; ++ ast_cli( fd, "%-20.20s %-40.40s\n", "----", "-----") ; ++ ++ ast_conference_stats* s = NULL ; ++ ++ int i; ++ ++ for ( i = 0 ; i < count ; ++i ) ++ { ++ s = &(stats[i]) ; ++ ++ // output this conferences stats ++ ast_cli( fd, "%-20.20s\n", (char*)( &(s->name) )) ; ++ } ++ ++ ast_cli( fd, "\n" ) ; ++ ++ // ++ // drill down to specific stats ++ // ++ ++ if ( argc == 4 ) ++ { ++ // show stats for a particular conference ++ conference_show_stats_name( fd, argv[3] ) ; ++ } ++ ++ return RESULT_SUCCESS ; ++} ++ ++int conference_show_stats_name( int fd, const char* name ) ++{ ++ // not implemented yet ++ return RESULT_SUCCESS ; ++} ++ ++static char conference_list_usage[] = ++ "usage: conference list {}\n" ++ " list members of a conference\n" ++; ++ ++static struct ast_cli_entry cli_list = { ++ { "conference", "list", NULL }, ++ conference_list, ++ "list members of a conference", ++ conference_list_usage ++} ; ++ ++ ++ ++int conference_list( int fd, int argc, char *argv[] ) ++{ ++ int index; ++ ++ if ( argc < 2 ) ++ return RESULT_SHOWUSAGE ; ++ ++ if (argc >= 3) ++ { ++ for (index = 2; index < argc; index++) ++ { ++ // get the conference name ++ const char* name = argv[index] ; ++ show_conference_list( fd, name ); ++ } ++ } ++ else ++ { ++ show_conference_stats(fd); ++ } ++ return RESULT_SUCCESS ; ++} ++ ++ ++int conference_kick( int fd, int argc, char *argv[] ) ++{ ++ if ( argc < 4 ) ++ return RESULT_SHOWUSAGE ; ++ ++ // get the conference name ++ const char* name = argv[2] ; ++ ++ int member_id; ++ sscanf(argv[3], "%d", &member_id); ++ ++ int res = kick_member( name, member_id ); ++ ++ if (res) ast_cli( fd, "User #: %d kicked\n", member_id) ; ++ ++ return RESULT_SUCCESS ; ++} ++ ++static char conference_kick_usage[] = ++ "usage: conference kick \n" ++ " kick member from conference \n" ++; ++ ++static struct ast_cli_entry cli_kick = { ++ { "conference", "kick", NULL }, ++ conference_kick, ++ "kick member from a conference", ++ conference_kick_usage ++} ; ++ ++int conference_kickchannel( int fd, int argc, char *argv[] ) ++{ ++ if ( argc < 4 ) ++ return RESULT_SHOWUSAGE ; ++ ++ const char *name = argv[2] ; ++ const char *channel = argv[3]; ++ ++ int res = kick_channel( name, channel ); ++ ++ if ( !res ) ++ { ++ ast_cli( fd, "Cannot kick channel %s in conference %s\n", channel, name); ++ return RESULT_FAILURE; ++ } ++ ++ return RESULT_SUCCESS ; ++} ++ ++static char conference_kickchannel_usage[] = ++ "usage: conference kickchannel \n" ++ " kick channel from conference\n" ++; ++ ++static struct ast_cli_entry cli_kickchannel = { ++ { "conference", "kickchannel", NULL }, ++ conference_kickchannel, ++ "kick channel from conference", ++ conference_kickchannel_usage ++} ; ++ ++int conference_exit( int fd, int argc, char *argv[] ) ++{ ++ if ( argc < 3 ) ++ return RESULT_SHOWUSAGE ; ++ ++ const char *channel = argv[2]; ++ ++ struct ast_conf_member *member = find_member(channel, 1); ++ if(!member) ++ { ++ ast_cli(fd, "Member %s not found\n", channel); ++ return RESULT_FAILURE; ++ } ++ const char * name = member->conf_name; ++ int res = kick_channel( name, channel ); ++ ++ if ( !res ) ++ { ++ ast_cli(fd, "Cannot exit channel %s from conference %s\n", channel, name); ++ ast_mutex_unlock(&member->lock); ++ return RESULT_FAILURE; ++ } ++ ++ ast_mutex_unlock( &member->lock ) ; ++ return RESULT_SUCCESS ; ++} ++ ++static char conference_exit_usage[] = ++ "usage: conference exit \n" ++ " exit channel from any conference where it in\n"; ++ ++static struct ast_cli_entry cli_exit = { ++ { "conference", "exit", NULL }, ++ conference_exit, ++ "exit channel from any conference where it in", ++ conference_exit_usage ++}; ++ ++int conference_mute( int fd, int argc, char *argv[] ) ++{ ++ if ( argc < 4 ) ++ return RESULT_SHOWUSAGE ; ++ ++ // get the conference name ++ const char* name = argv[2] ; ++ ++ int member_id; ++ sscanf(argv[3], "%d", &member_id); ++ ++ int res = mute_member( name, member_id ); ++ ++ if (res) ast_cli( fd, "User #: %d muted\n", member_id) ; ++ ++ return RESULT_SUCCESS ; ++} ++ ++static char conference_mute_usage[] = ++ "usage: conference mute \n" ++ " mute member in a conference\n" ++; ++ ++static struct ast_cli_entry cli_mute = { ++ { "conference", "mute", NULL }, ++ conference_mute, ++ "mute member in a conference", ++ conference_mute_usage ++} ; ++ ++int conference_mutechannel( int fd, int argc, char *argv[] ) ++{ ++ struct ast_conf_member *member; ++ char *channel; ++ ++ if ( argc < 3 ) ++ return RESULT_SHOWUSAGE ; ++ ++ channel = argv[2]; ++ ++ member = find_member(channel, 1); ++ if(!member) { ++ ast_cli(fd, "Member %s not found\n", channel); ++ return RESULT_FAILURE; ++ } ++ ++ member->mute_audio = 1; ++ ast_mutex_unlock( &member->lock ) ; ++ ++ ast_cli( fd, "Channel #: %s muted\n", argv[2]) ; ++ ++ return RESULT_SUCCESS ; ++} ++ ++static char conference_mutechannel_usage[] = ++ "usage: conference mutechannel \n" ++ " mute channel in a conference\n" ++; ++ ++static struct ast_cli_entry cli_mutechannel = { ++ { "conference", "mutechannel", NULL }, ++ conference_mutechannel, ++ "mute channel in a conference", ++ conference_mutechannel_usage ++} ; ++ ++int conference_viewstream( int fd, int argc, char *argv[] ) ++{ ++ int res; ++ ++ if ( argc < 5 ) ++ return RESULT_SHOWUSAGE ; ++ ++ // get the conference name ++ const char* switch_name = argv[2] ; ++ ++ int member_id, viewstream_id; ++ sscanf(argv[3], "%d", &member_id); ++ sscanf(argv[4], "%d", &viewstream_id); ++ ++ res = viewstream_switch( switch_name, member_id, viewstream_id ); ++ ++ if (res) ast_cli( fd, "User #: %d viewing %d\n", member_id, viewstream_id) ; ++ ++ return RESULT_SUCCESS ; ++} ++ ++static char conference_viewstream_usage[] = ++ "usage: conference viewstream \n" ++ " member will receive video stream \n" ++; ++ ++static struct ast_cli_entry cli_viewstream = { ++ { "conference", "viewstream", NULL }, ++ conference_viewstream, ++ "switch view in a conference", ++ conference_viewstream_usage ++} ; ++ ++int conference_viewchannel( int fd, int argc, char *argv[] ) ++{ ++ int res; ++ ++ if ( argc < 5 ) ++ return RESULT_SHOWUSAGE ; ++ ++ // get the conference name ++ const char* switch_name = argv[2] ; ++ ++ res = viewchannel_switch( switch_name, argv[3], argv[4] ); ++ ++ if (res) ast_cli( fd, "Channel #: %s viewing %s\n", argv[3], argv[4]) ; ++ ++ return RESULT_SUCCESS ; ++} ++ ++static char conference_viewchannel_usage[] = ++ "usage: conference viewchannel \n" ++ " channel will receive video stream \n" ++; ++ ++static struct ast_cli_entry cli_viewchannel = { ++ { "conference", "viewchannel", NULL }, ++ conference_viewchannel, ++ "switch channel in a conference", ++ conference_viewchannel_usage ++} ; ++ ++int conference_unmute( int fd, int argc, char *argv[] ) ++{ ++ if ( argc < 3 ) ++ return RESULT_SHOWUSAGE ; ++ ++ // get the conference name ++ const char* name = argv[2] ; ++ ++ int member_id; ++ sscanf(argv[3], "%d", &member_id); ++ ++ int res = unmute_member( name, member_id ); ++ ++ if (res) ast_cli( fd, "User #: %d unmuted\n", member_id) ; ++ ++ return RESULT_SUCCESS ; ++} ++ ++static char conference_unmute_usage[] = ++ "usage: conference unmute \n" ++ " unmute member in a conference\n" ++; ++ ++static struct ast_cli_entry cli_unmute = { ++ { "conference", "unmute", NULL }, ++ conference_unmute, ++ "unmute member in a conference", ++ conference_unmute_usage ++} ; ++ ++int conference_unmutechannel( int fd, int argc, char *argv[] ) ++{ ++ struct ast_conf_member *member; ++ char *channel; ++ ++ if ( argc < 3 ) ++ return RESULT_SHOWUSAGE ; ++ ++ channel = argv[2]; ++ ++ member = find_member(channel, 1); ++ if(!member) { ++ ast_cli(fd, "Member %s not found\n", channel); ++ return RESULT_FAILURE; ++ } ++ ++ member->mute_audio = 0; ++ ast_mutex_unlock( &member->lock ) ; ++ ++ ast_cli( fd, "Channel #: %s unmuted\n", argv[2]) ; ++ ++ return RESULT_SUCCESS ; ++} ++ ++static char conference_unmutechannel_usage[] = ++ "usage: conference unmutechannel \n" ++ " unmute channel in a conference\n" ++; ++ ++static struct ast_cli_entry cli_unmutechannel = { ++ { "conference", "unmutechannel", NULL }, ++ conference_unmutechannel, ++ "unmute channel in a conference", ++ conference_unmutechannel_usage ++} ; ++ ++// ++// play sound ++// ++static char conference_play_sound_usage[] = ++ "usage: conference play sound [mute]\n" ++ " play sound to conference member .\n" ++ " If mute is specified, all other audio is muted while the sound is played back.\n" ++; ++ ++static struct ast_cli_entry cli_play_sound = { ++ { "conference", "play", "sound", NULL }, ++ conference_play_sound, ++ "play a sound to a conference member", ++ conference_play_sound_usage ++} ; ++ ++int conference_play_sound( int fd, int argc, char *argv[] ) ++{ ++ char *channel, *file; ++ int mute = 0; ++ ++ if ( argc < 5 ) ++ return RESULT_SHOWUSAGE ; ++ ++ channel = argv[3]; ++ file = argv[4]; ++ ++ if(argc > 5 && !strcmp(argv[5], "mute")) ++ mute = 1; ++ ++ int res = play_sound_channel(fd, channel, file, mute); ++ ++ if ( !res ) ++ { ++ ast_cli(fd, "Sound playback failed failed\n"); ++ return RESULT_FAILURE; ++ } ++ return RESULT_SUCCESS ; ++} ++ ++// ++// stop sounds ++// ++ ++static char conference_stop_sounds_usage[] = ++ "usage: conference stop sounds \n" ++ " stop sounds for conference member .\n" ++; ++ ++static struct ast_cli_entry cli_stop_sounds = { ++ { "conference", "stop", "sounds", NULL }, ++ conference_stop_sounds, ++ "stop sounds for a conference member", ++ conference_stop_sounds_usage ++} ; ++ ++int conference_stop_sounds( int fd, int argc, char *argv[] ) ++{ ++ char *channel; ++ ++ if ( argc < 4 ) ++ return RESULT_SHOWUSAGE ; ++ ++ channel = argv[3]; ++ ++ int res = stop_sound_channel(fd, channel); ++ ++ if ( !res ) ++ { ++ ast_cli(fd, "Sound stop failed failed\n"); ++ return RESULT_FAILURE; ++ } ++ return RESULT_SUCCESS ; ++} ++ ++// ++// end conference ++// ++ ++static char conference_end_usage[] = ++ "usage: conference end \n" ++ " ends a conference.\n" ++; ++ ++static struct ast_cli_entry cli_end = { ++ { "conference", "end", NULL }, ++ conference_end, ++ "stops a conference", ++ conference_end_usage ++} ; ++ ++int conference_end( int fd, int argc, char *argv[] ) ++{ ++ // check the args length ++ if ( argc < 3 ) ++ return RESULT_SHOWUSAGE ; ++ ++ // conference name ++ const char* name = argv[2] ; ++ ++ // get the conference ++ if ( end_conference( name, 1 ) != 0 ) ++ { ++ ast_cli( fd, "unable to end the conference, name => %s\n", name ) ; ++ return RESULT_SHOWUSAGE ; ++ } ++ ++ return RESULT_SUCCESS ; ++} ++ ++// ++// E.BUU - Manager conference end. Additional option to just kick everybody out ++// without hangin up channels ++// ++int manager_conference_end(struct mansession *s, const struct message *m) ++{ ++ const char *confname = astman_get_header(m,"Conference"); ++ int hangup = 1; ++ ++ const char * h = astman_get_header(m, "Hangup"); ++ if (h) ++ { ++ hangup = atoi(h); ++ } ++ ++ ast_log( LOG_NOTICE, "Terminating conference %s on manager's request. Hangup: %s.\n", confname, hangup?"YES":"NO" ); ++ if ( end_conference( confname, hangup ) != 0 ) ++ { ++ ast_log( LOG_ERROR, "manager end conf: unable to terminate conference %s.\n", confname ); ++ astman_send_error(s, m, "Failed to terminate\r\n"); ++ return RESULT_FAILURE; ++ } ++ ++ astman_send_ack(s, m, "Conference terminated"); ++ return RESULT_SUCCESS; ++} ++// ++// lock conference to a video source ++// ++static char conference_lock_usage[] = ++ "usage: conference lock \n" ++ " locks incoming video stream for conference to member \n" ++; ++ ++static struct ast_cli_entry cli_lock = { ++ { "conference", "lock", NULL }, ++ conference_lock, ++ "locks incoming video to a member", ++ conference_lock_usage ++} ; ++ ++int conference_lock( int fd, int argc, char *argv[] ) ++{ ++ // check args ++ if ( argc < 4 ) ++ return RESULT_SHOWUSAGE; ++ ++ const char *conference = argv[2]; ++ int member; ++ sscanf(argv[3], "%d", &member); ++ ++ int res = lock_conference(conference, member); ++ ++ if ( !res ) ++ { ++ ast_cli(fd, "Locking failed\n"); ++ return RESULT_FAILURE; ++ } ++ ++ return RESULT_SUCCESS; ++} ++ ++// ++// lock conference to a video source channel ++// ++static char conference_lockchannel_usage[] = ++ "usage: conference lockchannel \n" ++ " locks incoming video stream for conference to channel \n" ++; ++ ++static struct ast_cli_entry cli_lockchannel = { ++ { "conference", "lockchannel", NULL }, ++ conference_lockchannel, ++ "locks incoming video to a channel", ++ conference_lockchannel_usage ++} ; ++ ++int conference_lockchannel( int fd, int argc, char *argv[] ) ++{ ++ // check args ++ if ( argc < 4 ) ++ return RESULT_SHOWUSAGE; ++ ++ const char *conference = argv[2]; ++ const char *channel = argv[3]; ++ ++ int res = lock_conference_channel(conference, channel); ++ ++ if ( !res ) ++ { ++ ast_cli(fd, "Locking failed\n"); ++ return RESULT_FAILURE; ++ } ++ ++ return RESULT_SUCCESS; ++} ++ ++// ++// unlock conference ++// ++static char conference_unlock_usage[] = ++ "usage: conference unlock \n" ++ " unlocks conference \n" ++; ++ ++static struct ast_cli_entry cli_unlock = { ++ { "conference", "unlock", NULL }, ++ conference_unlock, ++ "unlocks conference", ++ conference_unlock_usage ++} ; ++ ++int conference_unlock( int fd, int argc, char *argv[] ) ++{ ++ // check args ++ if ( argc < 3 ) ++ return RESULT_SHOWUSAGE; ++ ++ ++ const char *conference = argv[2]; ++ ++ int res = unlock_conference(conference); ++ ++ if ( !res ) ++ { ++ ast_cli(fd, "Unlocking failed\n"); ++ return RESULT_FAILURE; ++ } ++ ++ return RESULT_SUCCESS; ++} ++ ++// ++// Set conference default video source ++// ++static char conference_set_default_usage[] = ++ "usage: conference set default \n" ++ " sets the default video source for conference to member \n" ++ " Use a negative value for member if you want to clear the default\n" ++; ++ ++static struct ast_cli_entry cli_set_default = { ++ { "conference", "set", "default", NULL }, ++ conference_set_default, ++ "sets default video source", ++ conference_set_default_usage ++} ; ++ ++int conference_set_default(int fd, int argc, char *argv[] ) ++{ ++ // check args ++ if ( argc < 5 ) ++ return RESULT_SHOWUSAGE; ++ ++ const char *conference = argv[3]; ++ int member; ++ sscanf(argv[4], "%d", &member); ++ ++ int res = set_default_id(conference, member); ++ ++ if ( !res ) ++ { ++ ast_cli(fd, "Setting default video id failed\n"); ++ return RESULT_FAILURE; ++ } ++ ++ return RESULT_SUCCESS; ++} ++ ++// ++// Set conference default video source channel ++// ++static char conference_set_defaultchannel_usage[] = ++ "usage: conference set defaultchannel \n" ++ " sets the default video source channel for conference to channel \n" ++; ++ ++static struct ast_cli_entry cli_set_defaultchannel = { ++ { "conference", "set", "defaultchannel", NULL }, ++ conference_set_defaultchannel, ++ "sets default video source channel", ++ conference_set_defaultchannel_usage ++} ; ++ ++int conference_set_defaultchannel(int fd, int argc, char *argv[] ) ++{ ++ // check args ++ if ( argc < 5 ) ++ return RESULT_SHOWUSAGE; ++ ++ const char *conference = argv[3]; ++ const char *channel = argv[4]; ++ ++ int res = set_default_channel(conference, channel); ++ ++ if ( !res ) ++ { ++ ast_cli(fd, "Setting default video id failed\n"); ++ return RESULT_FAILURE; ++ } ++ ++ return RESULT_SUCCESS; ++} ++ ++// ++// Mute video from a member ++// ++static char conference_video_mute_usage[] = ++ "usage: conference video mute \n" ++ " mutes video from member in conference \n" ++; ++ ++static struct ast_cli_entry cli_video_mute = { ++ { "conference", "video", "mute", NULL }, ++ conference_video_mute, ++ "mutes video from a member", ++ conference_video_mute_usage ++} ; ++ ++int conference_video_mute(int fd, int argc, char *argv[] ) ++{ ++ // check args ++ if ( argc < 5 ) ++ return RESULT_SHOWUSAGE; ++ ++ const char *conference = argv[3]; ++ int member; ++ sscanf(argv[4], "%d", &member); ++ ++ int res = video_mute_member(conference, member); ++ ++ if ( !res ) ++ { ++ ast_cli(fd, "Muting video from member %d failed\n", member); ++ return RESULT_FAILURE; ++ } ++ ++ return RESULT_SUCCESS; ++} ++ ++// ++// Unmute video from a member ++// ++static char conference_video_unmute_usage[] = ++ "usage: conference video unmute \n" ++ " unmutes video from member in conference \n" ++; ++ ++static struct ast_cli_entry cli_video_unmute = { ++ { "conference", "video", "unmute", NULL }, ++ conference_video_unmute, ++ "unmutes video from a member", ++ conference_video_unmute_usage ++} ; ++ ++int conference_video_unmute(int fd, int argc, char *argv[] ) ++{ ++ // check args ++ if ( argc < 5 ) ++ return RESULT_SHOWUSAGE; ++ ++ const char *conference = argv[3]; ++ int member; ++ sscanf(argv[4], "%d", &member); ++ ++ int res = video_unmute_member(conference, member); ++ ++ if ( !res ) ++ { ++ ast_cli(fd, "Unmuting video from member %d failed\n", member); ++ return RESULT_FAILURE; ++ } ++ ++ return RESULT_SUCCESS; ++} ++ ++// ++// Mute video from a channel ++// ++static char conference_video_mutechannel_usage[] = ++ "usage: conference video mutechannel \n" ++ " mutes video from channel in conference \n" ++; ++ ++static struct ast_cli_entry cli_video_mutechannel = { ++ { "conference", "video", "mutechannel", NULL }, ++ conference_video_mutechannel, ++ "mutes video from a channel", ++ conference_video_mutechannel_usage ++} ; ++ ++int conference_video_mutechannel(int fd, int argc, char *argv[] ) ++{ ++ // check args ++ if ( argc < 5 ) ++ return RESULT_SHOWUSAGE; ++ ++ const char *conference = argv[3]; ++ const char *channel = argv[4]; ++ ++ int res = video_mute_channel(conference, channel); ++ ++ if ( !res ) ++ { ++ ast_cli(fd, "Muting video from channel %s failed\n", channel); ++ return RESULT_FAILURE; ++ } ++ ++ return RESULT_SUCCESS; ++} ++ ++// ++// Unmute video from a channel ++// ++static char conference_video_unmutechannel_usage[] = ++ "usage: conference video unmutechannel \n" ++ " unmutes video from channel in conference \n" ++; ++ ++static struct ast_cli_entry cli_video_unmutechannel = { ++ { "conference", "video", "unmutechannel", NULL }, ++ conference_video_unmutechannel, ++ "unmutes video from a channel", ++ conference_video_unmutechannel_usage ++} ; ++ ++int conference_video_unmutechannel(int fd, int argc, char *argv[] ) ++{ ++ // check args ++ if ( argc < 5 ) ++ return RESULT_SHOWUSAGE; ++ ++ const char *conference = argv[3]; ++ const char *channel = argv[4]; ++ ++ int res = video_unmute_channel(conference, channel); ++ ++ if ( !res ) ++ { ++ ast_cli(fd, "Unmuting video from channel %s failed\n", channel); ++ return RESULT_FAILURE; ++ } ++ ++ return RESULT_SUCCESS; ++} ++ ++ ++// ++// Text message functions ++// Send a text message to a member ++// ++static char conference_text_usage[] = ++ "usage: conference text \n" ++ " Sends text message to member in conference \n" ++; ++ ++static struct ast_cli_entry cli_text = { ++ { "conference", "text", NULL }, ++ conference_text, ++ "sends a text message to a member", ++ conference_text_usage ++} ; ++ ++int conference_text(int fd, int argc, char *argv[] ) ++{ ++ // check args ++ if ( argc < 5 ) ++ return RESULT_SHOWUSAGE; ++ ++ const char *conference = argv[2]; ++ int member; ++ sscanf(argv[3], "%d", &member); ++ const char *text = argv[4]; ++ ++ int res = send_text(conference, member, text); ++ ++ if ( !res ) ++ { ++ ast_cli(fd, "Sending a text message to member %d failed\n", member); ++ return RESULT_FAILURE; ++ } ++ ++ return RESULT_SUCCESS; ++} ++ ++// ++// Send a text message to a channel ++// ++static char conference_textchannel_usage[] = ++ "usage: conference textchannel \n" ++ " Sends text message to channel in conference \n" ++; ++ ++static struct ast_cli_entry cli_textchannel = { ++ { "conference", "textchannel", NULL }, ++ conference_textchannel, ++ "sends a text message to a channel", ++ conference_textchannel_usage ++} ; ++ ++int conference_textchannel(int fd, int argc, char *argv[] ) ++{ ++ // check args ++ if ( argc < 5 ) ++ return RESULT_SHOWUSAGE; ++ ++ const char *conference = argv[2]; ++ const char *channel = argv[3]; ++ const char *text = argv[4]; ++ ++ int res = send_text_channel(conference, channel, text); ++ ++ if ( !res ) ++ { ++ ast_cli(fd, "Sending a text message to channel %s failed\n", channel); ++ return RESULT_FAILURE; ++ } ++ ++ return RESULT_SUCCESS; ++} ++ ++// ++// Send a text message to all members in a conference ++// ++static char conference_textbroadcast_usage[] = ++ "usage: conference textbroadcast \n" ++ " Sends text message to all members in conference \n" ++; ++ ++static struct ast_cli_entry cli_textbroadcast = { ++ { "conference", "textbroadcast", NULL }, ++ conference_textbroadcast, ++ "sends a text message to all members in a conference", ++ conference_textbroadcast_usage ++} ; ++ ++int conference_textbroadcast(int fd, int argc, char *argv[] ) ++{ ++ // check args ++ if ( argc < 4 ) ++ return RESULT_SHOWUSAGE; ++ ++ const char *conference = argv[2]; ++ const char *text = argv[3]; ++ ++ int res = send_text_broadcast(conference, text); ++ ++ if ( !res ) ++ { ++ ast_cli(fd, "Sending a text broadcast to conference %s failed\n", conference); ++ return RESULT_FAILURE; ++ } ++ ++ return RESULT_SUCCESS; ++} ++ ++// ++// Associate two members ++// Audio from the source member will drive VAD based video switching for the destination member ++// If the destination member is missing or negative, break any existing association ++// ++static char conference_drive_usage[] = ++ "usage: conference drive [destination member]\n" ++ " Drives VAD video switching of using audio from in conference \n" ++ " If destination is missing or negative, break existing association\n" ++; ++ ++static struct ast_cli_entry cli_drive = { ++ { "conference", "drive", NULL }, ++ conference_drive, ++ "pairs two members to drive VAD-based video switching", ++ conference_drive_usage ++} ; ++ ++int conference_drive(int fd, int argc, char *argv[] ) ++{ ++ // check args ++ if ( argc < 4 ) ++ return RESULT_SHOWUSAGE; ++ ++ const char *conference = argv[2]; ++ int src_member = -1; ++ int dst_member = -1; ++ sscanf(argv[3], "%d", &src_member); ++ if ( argc > 4 ) ++ sscanf(argv[4], "%d", &dst_member); ++ ++ int res = drive(conference, src_member, dst_member); ++ ++ if ( !res ) ++ { ++ ast_cli(fd, "Pairing members %d and %d failed\n", src_member, dst_member); ++ return RESULT_FAILURE; ++ } ++ ++ return RESULT_SUCCESS; ++} ++ ++// ++// Associate two channels ++// Audio from the source channel will drive VAD based video switching for the destination channel ++// If the destination channel is missing, break any existing association ++// ++static char conference_drivechannel_usage[] = ++ "usage: conference drive [destination channel]\n" ++ " Drives VAD video switching of using audio from in conference \n" ++ " If destination is missing, break existing association\n" ++; ++ ++static struct ast_cli_entry cli_drivechannel = { ++ { "conference", "drivechannel", NULL }, ++ conference_drivechannel, ++ "pairs two channels to drive VAD-based video switching", ++ conference_drivechannel_usage ++} ; ++ ++int conference_drivechannel(int fd, int argc, char *argv[] ) ++{ ++ // check args ++ if ( argc < 4 ) ++ return RESULT_SHOWUSAGE; ++ ++ const char *conference = argv[2]; ++ const char *src_channel = argv[3]; ++ const char *dst_channel = NULL; ++ if ( argc > 4 ) ++ dst_channel = argv[4]; ++ ++ int res = drive_channel(conference, src_channel, dst_channel); ++ ++ if ( !res ) ++ { ++ ast_cli(fd, "Pairing channels %s and %s failed\n", src_channel, dst_channel); ++ return RESULT_FAILURE; ++ } ++ ++ return RESULT_SUCCESS; ++} ++ ++ ++// ++// cli initialization function ++// ++ ++void register_conference_cli( void ) ++{ ++ ast_cli_register( &cli_restart ); ++ ast_cli_register( &cli_debug ) ; ++ ast_cli_register( &cli_show_stats ) ; ++ ast_cli_register( &cli_list ); ++ ast_cli_register( &cli_kick ); ++ ast_cli_register( &cli_kickchannel ); ++ ast_cli_register( &cli_exit ); ++ ast_cli_register( &cli_mute ); ++ ast_cli_register( &cli_mutechannel ); ++ ast_cli_register( &cli_viewstream ); ++ ast_cli_register( &cli_viewchannel ); ++ ast_cli_register( &cli_unmute ); ++ ast_cli_register( &cli_unmutechannel ); ++ ast_cli_register( &cli_play_sound ) ; ++ ast_cli_register( &cli_stop_sounds ) ; ++ ast_cli_register( &cli_end ); ++ ast_cli_register( &cli_lock ); ++ ast_cli_register( &cli_lockchannel ); ++ ast_cli_register( &cli_unlock ); ++ ast_cli_register( &cli_set_default ); ++ ast_cli_register( &cli_set_defaultchannel ); ++ ast_cli_register( &cli_video_mute ) ; ++ ast_cli_register( &cli_video_unmute ) ; ++ ast_cli_register( &cli_video_mutechannel ) ; ++ ast_cli_register( &cli_video_unmutechannel ) ; ++ ast_cli_register( &cli_text ); ++ ast_cli_register( &cli_textchannel ); ++ ast_cli_register( &cli_textbroadcast ); ++ ast_cli_register( &cli_drive ); ++ ast_cli_register( &cli_drivechannel ); ++ ast_manager_register( "ConferenceList", 0, manager_conference_list, "Conference List" ); ++ ast_manager_register( "ConferenceEnd", EVENT_FLAG_CALL, manager_conference_end, "Terminate a conference" ); ++ ++} ++ ++void unregister_conference_cli( void ) ++{ ++ ast_cli_unregister( &cli_restart ); ++ ast_cli_unregister( &cli_debug ) ; ++ ast_cli_unregister( &cli_show_stats ) ; ++ ast_cli_unregister( &cli_list ); ++ ast_cli_unregister( &cli_kick ); ++ ast_cli_unregister( &cli_kickchannel ); ++ ast_cli_unregister( &cli_exit ); ++ ast_cli_unregister( &cli_mute ); ++ ast_cli_unregister( &cli_mutechannel ); ++ ast_cli_unregister( &cli_viewstream ); ++ ast_cli_unregister( &cli_viewchannel ); ++ ast_cli_unregister( &cli_unmute ); ++ ast_cli_unregister( &cli_unmutechannel ); ++ ast_cli_unregister( &cli_play_sound ) ; ++ ast_cli_unregister( &cli_stop_sounds ) ; ++ ast_cli_unregister( &cli_end ); ++ ast_cli_unregister( &cli_lock ); ++ ast_cli_unregister( &cli_lockchannel ); ++ ast_cli_unregister( &cli_unlock ); ++ ast_cli_unregister( &cli_set_default ); ++ ast_cli_unregister( &cli_set_defaultchannel ); ++ ast_cli_unregister( &cli_video_mute ) ; ++ ast_cli_unregister( &cli_video_unmute ) ; ++ ast_cli_unregister( &cli_video_mutechannel ) ; ++ ast_cli_unregister( &cli_video_unmutechannel ) ; ++ ast_cli_unregister( &cli_text ); ++ ast_cli_unregister( &cli_textchannel ); ++ ast_cli_unregister( &cli_textbroadcast ); ++ ast_cli_unregister( &cli_drive ); ++ ast_cli_unregister( &cli_drivechannel ); ++ ast_manager_unregister( "ConferenceList" ); ++ ast_manager_unregister( "ConferenceEnd" ); ++} +diff --git a/apps/conference/cli.h b/apps/conference/cli.h +new file mode 100644 +index 0000000..34b79d6 +--- /dev/null ++++ b/apps/conference/cli.h +@@ -0,0 +1,99 @@ ++ ++// $Id: cli.h 880 2007-04-25 15:23:59Z jpgrayson $ ++ ++/* ++ * app_conference ++ * ++ * A channel independent conference application for Asterisk ++ * ++ * Copyright (C) 2002, 2003 Junghanns.NET GmbH ++ * Copyright (C) 2003, 2004 HorizonLive.com, Inc. ++ * Copyright (C) 2005, 2006 HorizonWimba, Inc. ++ * Copyright (C) 2007 Wimba, Inc. ++ * ++ * Klaus-Peter Junghanns ++ * ++ * Video Conferencing support added by ++ * Neil Stratford ++ * Copyright (C) 2005, 2005 Vipadia Limited ++ * ++ * VAD driven video conferencing, text message support ++ * and miscellaneous enhancements added by ++ * Mihai Balea ++ * ++ * This program may be modified and distributed under the ++ * terms of the GNU General Public License. You should have received ++ * a copy of the GNU General Public License along with this ++ * program; if not, write to the Free Software Foundation, Inc. ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _APP_CONF_CLI_H ++#define _APP_CONF_CLI_H ++ ++// ++// includes ++// ++ ++#include "app_conference.h" ++#include "common.h" ++ ++// ++// function declarations ++// ++ ++int conference_show_stats( int fd, int argc, char *argv[] ) ; ++int conference_show_stats_name( int fd, const char* name ) ; ++ ++int conference_restart( int fd, int argc, char *argv[] ); ++ ++int conference_debug( int fd, int argc, char *argv[] ) ; ++int conference_no_debug( int fd, int argc, char *argv[] ) ; ++ ++int conference_list( int fd, int argc, char *argv[] ) ; ++int conference_kick( int fd, int argc, char *argv[] ) ; ++int conference_kickchannel( int fd, int argc, char *argv[] ) ; ++ ++int conference_exit( int fd, int argc, char *argv[] ) ; ++ ++int conference_mute( int fd, int argc, char *argv[] ) ; ++int conference_unmute( int fd, int argc, char *argv[] ) ; ++int conference_mutechannel( int fd, int argc, char *argv[] ) ; ++int conference_unmutechannel( int fd, int argc, char *argv[] ) ; ++int conference_viewstream( int fd, int argc, char *argv[] ) ; ++int conference_viewchannel( int fd, int argc, char *argv[] ) ; ++ ++int conference_play_sound( int fd, int argc, char *argv[] ) ; ++int conference_stop_sounds( int fd, int argc, char *argv[] ) ; ++ ++int conference_play_video( int fd, int argc, char *argv[] ) ; ++int conference_stop_videos( int fd, int argc, char *argv[] ) ; ++ ++int conference_end( int fd, int argc, char *argv[] ) ; ++ ++int conference_lock( int fd, int argc, char *argv[] ) ; ++int conference_lockchannel( int fd, int argc, char *argv[] ) ; ++int conference_unlock( int fd, int argc, char *argv[] ) ; ++ ++int conference_set_default(int fd, int argc, char *argv[] ) ; ++int conference_set_defaultchannel(int fd, int argc, char *argv[] ) ; ++ ++int conference_video_mute(int fd, int argc, char *argv[] ) ; ++int conference_video_mutechannel(int fd, int argc, char *argv[] ) ; ++int conference_video_unmute(int fd, int argc, char *argv[] ) ; ++int conference_video_unmutechannel(int fd, int argc, char *argv[] ) ; ++ ++int conference_text( int fd, int argc, char *argv[] ) ; ++int conference_textchannel( int fd, int argc, char *argv[] ) ; ++int conference_textbroadcast( int fd, int argc, char *argv[] ) ; ++ ++int conference_drive( int fd, int argc, char *argv[] ) ; ++int conference_drivechannel(int fd, int argc, char *argv[] ); ++ ++int manager_conference_end(struct mansession *s, const struct message *m); ++ ++void register_conference_cli( void ) ; ++void unregister_conference_cli( void ) ; ++ ++ ++#endif +diff --git a/apps/conference/common.h b/apps/conference/common.h +new file mode 100644 +index 0000000..989398c +--- /dev/null ++++ b/apps/conference/common.h +@@ -0,0 +1,63 @@ ++ ++// $Id: common.h 880 2007-04-25 15:23:59Z jpgrayson $ ++ ++/* ++ * app_conference ++ * ++ * A channel independent conference application for Asterisk ++ * ++ * Copyright (C) 2002, 2003 Junghanns.NET GmbH ++ * Copyright (C) 2003, 2004 HorizonLive.com, Inc. ++ * Copyright (C) 2005, 2006 HorizonWimba, Inc. ++ * Copyright (C) 2007 Wimba, Inc. ++ * ++ * Klaus-Peter Junghanns ++ * ++ * Video Conferencing support added by ++ * Neil Stratford ++ * Copyright (C) 2005, 2005 Vipadia Limited ++ * ++ * VAD driven video conferencing, text message support ++ * and miscellaneous enhancements added by ++ * Mihai Balea ++ * ++ * This program may be modified and distributed under the ++ * terms of the GNU General Public License. You should have received ++ * a copy of the GNU General Public License along with this ++ * program; if not, write to the Free Software Foundation, Inc. ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _APP_CONF_COMMON_H ++#define _APP_CONF_COMMON_H ++ ++#include ++ ++// typedef includes ++#include "conf_frame.h" ++ ++// function includesee ++//#include "member.h" ++#include "conference.h" ++#include "frame.h" ++#include "cli.h" ++ ++/* Utility functions */ ++ ++/* LOG the time taken to execute a function (like lock acquisition */ ++#if 1 ++#define TIMELOG(func,min,message) \ ++ do { \ ++ struct timeval t1, t2; \ ++ int diff; \ ++ t1 = ast_tvnow(); \ ++ func; \ ++ t2 = ast_tvnow(); \ ++ if ( (diff = ast_tvdiff_ms(t2, t1)) > min ) \ ++ ast_log( AST_CONF_DEBUG, "TimeLog: %s: %d ms\n", message, diff); \ ++ } while (0) ++#else ++#define TIMELOG(func,min,message) func ++#endif ++ ++#endif +diff --git a/apps/conference/conf_frame.h b/apps/conference/conf_frame.h +new file mode 100644 +index 0000000..e73e57a +--- /dev/null ++++ b/apps/conference/conf_frame.h +@@ -0,0 +1,73 @@ ++ ++// $Id: conf_frame.h 880 2007-04-25 15:23:59Z jpgrayson $ ++ ++/* ++ * app_conference ++ * ++ * A channel independent conference application for Asterisk ++ * ++ * Copyright (C) 2002, 2003 Junghanns.NET GmbH ++ * Copyright (C) 2003, 2004 HorizonLive.com, Inc. ++ * Copyright (C) 2005, 2006 HorizonWimba, Inc. ++ * Copyright (C) 2007 Wimba, Inc. ++ * ++ * Klaus-Peter Junghanns ++ * ++ * Video Conferencing support added by ++ * Neil Stratford ++ * Copyright (C) 2005, 2005 Vipadia Limited ++ * ++ * VAD driven video conferencing, text message support ++ * and miscellaneous enhancements added by ++ * Mihai Balea ++ * ++ * This program may be modified and distributed under the ++ * terms of the GNU General Public License. You should have received ++ * a copy of the GNU General Public License along with this ++ * program; if not, write to the Free Software Foundation, Inc. ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _APP_CONF_STRUCTS_H ++#define _APP_CONF_STRUCTS_H ++ ++// ++// includes ++// ++ ++#include "app_conference.h" ++#include "common.h" ++ ++// ++// struct declarations ++// ++ ++typedef struct conf_frame ++{ ++ // frame audio data ++ struct ast_frame* fr ; ++ ++ // array of converted versions for listeners ++ struct ast_frame* converted[ AC_SUPPORTED_FORMATS ] ; ++ ++ // pointer to the frame's owner ++ struct ast_conf_member* member ; // who sent this frame ++ ++ // frame meta data ++// struct timeval timestamp ; ++// unsigned long cycleid ; ++// int priority ; ++ ++ // linked-list pointers ++ struct conf_frame* next ; ++ struct conf_frame* prev ; ++ ++ // should this frame be preserved ++ short static_frame ; ++ ++ // pointer to mixing buffer ++ char* mixed_buffer ; ++} conf_frame ; ++ ++ ++#endif +diff --git a/apps/conference/conference.c b/apps/conference/conference.c +new file mode 100644 +index 0000000..72c71d9 +--- /dev/null ++++ b/apps/conference/conference.c +@@ -0,0 +1,2923 @@ ++ ++// $Id: conference.c 886 2007-08-06 14:33:34Z bcholew $ ++ ++/* ++ * app_conference ++ * ++ * A channel independent conference application for Asterisk ++ * ++ * Copyright (C) 2002, 2003 Junghanns.NET GmbH ++ * Copyright (C) 2003, 2004 HorizonLive.com, Inc. ++ * Copyright (C) 2005, 2006 HorizonWimba, Inc. ++ * Copyright (C) 2007 Wimba, Inc. ++ * ++ * Klaus-Peter Junghanns ++ * ++ * Video Conferencing support added by ++ * Neil Stratford ++ * Copyright (C) 2005, 2005 Vipadia Limited ++ * ++ * VAD driven video conferencing, text message support ++ * and miscellaneous enhancements added by ++ * Mihai Balea ++ * ++ * This program may be modified and distributed under the ++ * terms of the GNU General Public License. You should have received ++ * a copy of the GNU General Public License along with this ++ * program; if not, write to the Free Software Foundation, Inc. ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include "asterisk/autoconfig.h" ++#include "conference.h" ++#include "asterisk/utils.h" ++ ++// ++// static variables ++// ++ ++// single-linked list of current conferences ++struct ast_conference *conflist = NULL ; ++ ++// mutex for synchronizing access to conflist ++//static ast_mutex_t conflist_lock = AST_MUTEX_INITIALIZER ; ++AST_MUTEX_DEFINE_STATIC(conflist_lock); ++ ++static int conference_count = 0 ; ++ ++ ++// ++// main conference function ++// ++ ++void conference_exec( struct ast_conference *conf ) ++{ ++ ++ struct ast_conf_member *next_member; ++ struct ast_conf_member *member, *video_source_member, *dtmf_source_member;; ++ struct conf_frame *cfr, *spoken_frames, *send_frames; ++ ++ // count number of speakers, number of listeners ++ int speaker_count ; ++ int listener_count ; ++ ++ ast_log( AST_CONF_DEBUG, "Entered conference_exec, name => %s\n", conf->name ) ; ++ ++ // timer timestamps ++ struct timeval base, curr, notify ; ++ base = notify = ast_tvnow(); ++ ++ // holds differences of curr and base ++ long time_diff = 0 ; ++ long time_sleep = 0 ; ++ ++ int since_last_slept = 0 ; ++ ++ // ++ // variables for checking thread frequency ++ // ++ ++ // count to AST_CONF_FRAMES_PER_SECOND ++ int tf_count = 0 ; ++ long tf_diff = 0 ; ++ float tf_frequency = 0.0 ; ++ ++ struct timeval tf_base, tf_curr ; ++ tf_base = ast_tvnow(); ++ ++ // ++ // main conference thread loop ++ // ++ ++ ++ while ( 42 == 42 ) ++ { ++ // update the current timestamp ++ curr = ast_tvnow(); ++ ++ // calculate difference in timestamps ++ time_diff = ast_tvdiff_ms(curr, base); ++ ++ // calculate time we should sleep ++ time_sleep = AST_CONF_FRAME_INTERVAL - time_diff ; ++ ++ if ( time_sleep > 0 ) ++ { ++ // sleep for sleep_time ( as milliseconds ) ++ usleep( time_sleep * 1000 ) ; ++ ++ // reset since last slept counter ++ since_last_slept = 0 ; ++ ++ continue ; ++ } ++ else ++ { ++ // long sleep warning ++ if ( ++ since_last_slept == 0 ++ && time_diff > AST_CONF_CONFERENCE_SLEEP * 2 ++ ) ++ { ++ ast_log( ++ AST_CONF_DEBUG, ++ "long scheduling delay, time_diff => %ld, AST_CONF_FRAME_INTERVAL => %d\n", ++ time_diff, AST_CONF_FRAME_INTERVAL ++ ) ; ++ } ++ ++ // increment times since last slept ++ ++since_last_slept ; ++ ++ // sleep every other time ++ if ( since_last_slept % 2 ) ++ usleep( 0 ) ; ++ } ++ ++ // adjust the timer base ( it will be used later to timestamp outgoing frames ) ++ add_milliseconds( &base, AST_CONF_FRAME_INTERVAL ) ; ++ ++ // ++ // check thread frequency ++ // ++ ++ if ( ++tf_count >= AST_CONF_FRAMES_PER_SECOND ) ++ { ++ // update current timestamp ++ tf_curr = ast_tvnow(); ++ ++ // compute timestamp difference ++ tf_diff = ast_tvdiff_ms(tf_curr, tf_base); ++ ++ // compute sampling frequency ++ tf_frequency = ( float )( tf_diff ) / ( float )( tf_count ) ; ++ ++ if ( ++ ( tf_frequency <= ( float )( AST_CONF_FRAME_INTERVAL - 1 ) ) ++ || ( tf_frequency >= ( float )( AST_CONF_FRAME_INTERVAL + 1 ) ) ++ ) ++ { ++ ast_log( ++ LOG_WARNING, ++ "processed frame frequency variation, name => %s, tf_count => %d, tf_diff => %ld, tf_frequency => %2.4f\n", ++ conf->name, tf_count, tf_diff, tf_frequency ++ ) ; ++ } ++ ++ // reset values ++ tf_base = tf_curr ; ++ tf_count = 0 ; ++ } ++ ++ //-----------------// ++ // INCOMING FRAMES // ++ //-----------------// ++ ++ // ast_log( AST_CONF_DEBUG, "PROCESSING FRAMES, conference => %s, step => %d, ms => %ld\n", ++ // conf->name, step, ( base.tv_usec / 20000 ) ) ; ++ ++ // ++ // check if the conference is empty and if so ++ // remove it and break the loop ++ // ++ ++ // acquire the conference list lock ++ ast_mutex_lock(&conflist_lock); ++ ++ // acquire the conference mutex ++ ast_mutex_lock(&conf->lock); ++ ++ if ( conf->membercount == 0 ) ++ { ++ if (conf->debug_flag) ++ { ++ ast_log( LOG_NOTICE, "removing conference, count => %d, name => %s\n", conf->membercount, conf->name ) ; ++ } ++ remove_conf( conf ) ; // stop the conference ++ ++ // We don't need to release the conf mutex, since it was destroyed anyway ++ ++ // release the conference list lock ++ ast_mutex_unlock(&conflist_lock); ++ ++ break ; // break from main processing loop ++ } ++ ++ // release the conference mutex ++ ast_mutex_unlock(&conf->lock); ++ ++ // release the conference list lock ++ ast_mutex_unlock(&conflist_lock); ++ ++ ++ // ++ // Start processing frames ++ // ++ ++ // acquire conference mutex ++ TIMELOG(ast_mutex_lock( &conf->lock ),1,"conf thread conf lock"); ++ ++ if ( conf->membercount == 0 ) ++ { ++ // release the conference mutex ++ ast_mutex_unlock(&conf->lock); ++ continue; // We'll check again at the top of the loop ++ } ++ ++ // update the current delivery time ++ conf->delivery_time = base ; ++ ++ // ++ // loop through the list of members ++ // ( conf->memberlist is a single-linked list ) ++ // ++ ++ // ast_log( AST_CONF_DEBUG, "begin processing incoming audio, name => %s\n", conf->name ) ; ++ ++ // reset speaker and listener count ++ speaker_count = 0 ; ++ listener_count = 0 ; ++ ++ // get list of conference members ++ member = conf->memberlist ; ++ ++ // reset pointer lists ++ spoken_frames = NULL ; ++ ++ // reset video source ++ video_source_member = NULL; ++ ++ // reset dtmf source ++ dtmf_source_member = NULL; ++ ++ // loop over member list to retrieve queued frames ++ while ( member != NULL ) ++ { ++ // take note of next member - before it's too late ++ next_member = member->next; ++ ++ // this MIGHT delete member ++ member_process_spoken_frames(conf,member,&spoken_frames,time_diff, ++ &listener_count, &speaker_count); ++ ++ // adjust our pointer to the next inline ++ member = next_member; ++ } ++ ++ // ast_log( AST_CONF_DEBUG, "finished processing incoming audio, name => %s\n", conf->name ) ; ++ ++ ++ //---------------// ++ // MIXING FRAMES // ++ //---------------// ++ ++ // mix frames and get batch of outgoing frames ++ send_frames = mix_frames( spoken_frames, speaker_count, listener_count ) ; ++ ++ // accounting: if there are frames, count them as one incoming frame ++ if ( send_frames != NULL ) ++ { ++ // set delivery timestamp ++ //set_conf_frame_delivery( send_frames, base ) ; ++// ast_log ( LOG_WARNING, "base = %d,%d: conf->delivery_time = %d,%d\n",base.tv_sec,base.tv_usec, conf->delivery_time.tv_sec, conf->delivery_time.tv_usec); ++ ++ // ast_log( AST_CONF_DEBUG, "base => %ld.%ld %d\n", base.tv_sec, base.tv_usec, ( int )( base.tv_usec / 1000 ) ) ; ++ ++ conf->stats.frames_in++ ; ++ } ++ ++ //-----------------// ++ // OUTGOING FRAMES // ++ //-----------------// ++ ++ // ++ // loop over member list to queue outgoing frames ++ // ++ for ( member = conf->memberlist ; member != NULL ; member = member->next ) ++ { ++ member_process_outgoing_frames(conf, member, send_frames); ++ } ++ ++ //-------// ++ // VIDEO // ++ //-------// ++ ++ // loop over the incoming frames and send to all outgoing ++ // TODO: this is an O(n^2) algorithm. Can we speed it up without sacrificing per-member switching? ++ for (video_source_member = conf->memberlist; ++ video_source_member != NULL; ++ video_source_member = video_source_member->next) ++ { ++ while ((cfr = get_incoming_video_frame( video_source_member ))) ++ { ++ for (member = conf->memberlist; member != NULL; member = member->next) ++ { ++ // skip members that are not ready or are not supposed to receive video ++ if ( !member->ready_for_outgoing || member->norecv_video ) ++ continue ; ++ ++ if ( conf->video_locked ) ++ { ++ // Always send video from the locked source ++ if ( conf->current_video_source_id == video_source_member->id ) ++ queue_outgoing_video_frame(member, cfr->fr, conf->delivery_time); ++ } else ++ { ++ // If the member has vad switching disabled and dtmf switching enabled, use that ++ if ( member->dtmf_switch && ++ !member->vad_switch && ++ member->req_id == video_source_member->id ++ ) ++ { ++ queue_outgoing_video_frame(member, cfr->fr, conf->delivery_time); ++ } else ++ { ++ // If no dtmf switching, then do VAD switching ++ // The VAD switching decision code should make sure that our video source ++ // is legit ++ if ( (conf->current_video_source_id == video_source_member->id) || ++ (conf->current_video_source_id < 0 && ++ conf->default_video_source_id == video_source_member->id ++ ) ++ ) ++ { ++ queue_outgoing_video_frame(member, cfr->fr, conf->delivery_time); ++ } ++ } ++ ++ ++ } ++ } ++ // Garbage collection ++ delete_conf_frame(cfr); ++ } ++ } ++ ++ //------// ++ // DTMF // ++ //------// ++ ++ // loop over the incoming frames and send to all outgoing ++ for (dtmf_source_member = conf->memberlist; dtmf_source_member != NULL; dtmf_source_member = dtmf_source_member->next) ++ { ++ while ((cfr = get_incoming_dtmf_frame( dtmf_source_member ))) ++ { ++ for (member = conf->memberlist; member != NULL; member = member->next) ++ { ++ // skip members that are not ready ++ if ( member->ready_for_outgoing == 0 ) ++ { ++ continue ; ++ } ++ ++ if (member != dtmf_source_member) ++ { ++ // Send the latest frame ++ queue_outgoing_dtmf_frame(member, cfr->fr); ++ } ++ } ++ // Garbage collection ++ delete_conf_frame(cfr); ++ } ++ } ++ ++ //---------// ++ // CLEANUP // ++ //---------// ++ ++ // clean up send frames ++ while ( send_frames != NULL ) ++ { ++ // accouting: count all frames and mixed frames ++ if ( send_frames->member == NULL ) ++ conf->stats.frames_out++ ; ++ else ++ conf->stats.frames_mixed++ ; ++ ++ // delete the frame ++ send_frames = delete_conf_frame( send_frames ) ; ++ } ++ ++ // ++ // notify the manager of state changes every 100 milliseconds ++ // we piggyback on this for VAD switching logic ++ // ++ ++ if ( ( ast_tvdiff_ms(curr, notify) / AST_CONF_NOTIFICATION_SLEEP ) >= 1 ) ++ { ++ // Do VAD switching logic ++ // We need to do this here since send_state_change_notifications ++ // resets the flags ++ if ( !conf->video_locked ) ++ do_VAD_switching(conf); ++ ++ // send the notifications ++ send_state_change_notifications( conf->memberlist ) ; ++ ++ // increment the notification timer base ++ add_milliseconds( ¬ify, AST_CONF_NOTIFICATION_SLEEP ) ; ++ } ++ ++ // release conference mutex ++ ast_mutex_unlock( &conf->lock ) ; ++ ++ // !!! TESTING !!! ++ // usleep( 1 ) ; ++ } ++ // end while ( 42 == 42 ) ++ ++ // ++ // exit the conference thread ++ // ++ ++ ast_log( AST_CONF_DEBUG, "exit conference_exec\n" ) ; ++ ++ // exit the thread ++ pthread_exit( NULL ) ; ++ ++ return ; ++} ++ ++// ++// manange conference functions ++// ++ ++// called by app_conference.c:load_module() ++void init_conference( void ) ++{ ++ ast_mutex_init( &conflist_lock ) ; ++} ++ ++struct ast_conference* start_conference( struct ast_conf_member* member ) ++{ ++ // check input ++ if ( member == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to handle null member\n" ) ; ++ return NULL ; ++ } ++ ++ struct ast_conference* conf = NULL ; ++ ++ // acquire the conference list lock ++ ast_mutex_lock(&conflist_lock); ++ ++ ++ ++ // look for an existing conference ++ ast_log( AST_CONF_DEBUG, "attempting to find requested conference\n" ) ; ++ conf = find_conf( member->conf_name ) ; ++ ++ // unable to find an existing conference, try to create one ++ if ( conf == NULL ) ++ { ++ // create a new conference ++ ast_log( AST_CONF_DEBUG, "attempting to create requested conference\n" ) ; ++ ++ // create the new conference with one member ++ conf = create_conf( member->conf_name, member ) ; ++ ++ // return an error if create_conf() failed ++ if ( conf == NULL ) ++ ast_log( LOG_ERROR, "unable to find or create requested conference\n" ) ; ++ } ++ else ++ { ++ // ++ // existing conference found, add new member to the conference ++ // ++ // once we call add_member(), this thread ++ // is responsible for calling delete_member() ++ // ++ add_member( member, conf ) ; ++ } ++ ++ // release the conference list lock ++ ast_mutex_unlock(&conflist_lock); ++ ++ return conf ; ++} ++ ++// This function should be called with conflist_lock mutex being held ++struct ast_conference* find_conf( const char* name ) ++{ ++ // no conferences exist ++ if ( conflist == NULL ) ++ { ++ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", name ) ; ++ return NULL ; ++ } ++ ++ struct ast_conference *conf = conflist ; ++ ++ // loop through conf list ++ while ( conf != NULL ) ++ { ++ if ( strncasecmp( (char*)&(conf->name), name, 80 ) == 0 ) ++ { ++ // found conf name match ++ ast_log( AST_CONF_DEBUG, "found conference in conflist, name => %s\n", name ) ; ++ return conf; ++ } ++ conf = conf->next ; ++ } ++ ++ ast_log( AST_CONF_DEBUG, "unable to find conference in conflist, name => %s\n", name ) ; ++ return NULL; ++} ++ ++// This function should be called with conflist_lock held ++struct ast_conference* create_conf( char* name, struct ast_conf_member* member ) ++{ ++ ast_log( AST_CONF_DEBUG, "entered create_conf, name => %s\n", name ) ; ++ ++ // ++ // allocate memory for conference ++ // ++ ++ struct ast_conference *conf = malloc( sizeof( struct ast_conference ) ) ; ++ ++ if ( conf == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to malloc ast_conference\n" ) ; ++ return NULL ; ++ } ++ ++ // ++ // initialize conference ++ // ++ ++ conf->next = NULL ; ++ conf->memberlist = NULL ; ++ ++ conf->membercount = 0 ; ++ conf->conference_thread = -1 ; ++ ++ conf->debug_flag = 0 ; ++ ++ conf->id_count = 0; ++ ++ conf->default_video_source_id = -1; ++ conf->current_video_source_id = -1; ++ //conf->current_video_source_timestamp = ast_tvnow(); ++ conf->video_locked = 0; ++ ++ // zero stats ++ memset( &conf->stats, 0x0, sizeof( ast_conference_stats ) ) ; ++ ++ // record start time ++ conf->stats.time_entered = ast_tvnow(); ++ ++ // copy name to conference ++ strncpy( (char*)&(conf->name), name, sizeof(conf->name) - 1 ) ; ++ strncpy( (char*)&(conf->stats.name), name, sizeof(conf->name) - 1 ) ; ++ ++ // initialize mutexes ++ ast_mutex_init( &conf->lock ) ; ++ ++ // build translation paths ++ conf->from_slinear_paths[ AC_SLINEAR_INDEX ] = NULL ; ++ conf->from_slinear_paths[ AC_ULAW_INDEX ] = ast_translator_build_path( AST_FORMAT_ULAW, AST_FORMAT_SLINEAR ) ; ++ conf->from_slinear_paths[ AC_ALAW_INDEX ] = ast_translator_build_path( AST_FORMAT_ALAW, AST_FORMAT_SLINEAR ) ; ++ conf->from_slinear_paths[ AC_GSM_INDEX ] = ast_translator_build_path( AST_FORMAT_GSM, AST_FORMAT_SLINEAR ) ; ++ conf->from_slinear_paths[ AC_SPEEX_INDEX ] = ast_translator_build_path( AST_FORMAT_SPEEX, AST_FORMAT_SLINEAR ) ; ++#ifdef AC_USE_G729A ++ conf->from_slinear_paths[ AC_G729A_INDEX ] = ast_translator_build_path( AST_FORMAT_G729A, AST_FORMAT_SLINEAR ) ; ++#endif ++ ++ // add the initial member ++ add_member( member, conf ) ; ++ ++ ast_log( AST_CONF_DEBUG, "added new conference to conflist, name => %s\n", name ) ; ++ ++ // ++ // spawn thread for new conference, using conference_exec( conf ) ++ // ++ // acquire conference mutexes ++ ast_mutex_lock( &conf->lock ) ; ++ ++ if ( ast_pthread_create( &conf->conference_thread, NULL, (void*)conference_exec, conf ) == 0 ) ++ { ++ // detach the thread so it doesn't leak ++ pthread_detach( conf->conference_thread ) ; ++ ++ // prepend new conference to conflist ++ conf->next = conflist ; ++ conflist = conf ; ++ ++ // release conference mutexes ++ ast_mutex_unlock( &conf->lock ) ; ++ ++ ast_log( AST_CONF_DEBUG, "started conference thread for conference, name => %s\n", conf->name ) ; ++ } ++ else ++ { ++ ast_log( LOG_ERROR, "unable to start conference thread for conference %s\n", conf->name ) ; ++ ++ conf->conference_thread = -1 ; ++ ++ // release conference mutexes ++ ast_mutex_unlock( &conf->lock ) ; ++ ++ // clean up conference ++ free( conf ) ; ++ conf = NULL ; ++ } ++ ++ // count new conference ++ if ( conf != NULL ) ++ ++conference_count ; ++ ++ return conf ; ++} ++ ++//This function should be called with conflist_lock and conf->lock held ++void remove_conf( struct ast_conference *conf ) ++{ ++ int c; ++ ++ // ast_log( AST_CONF_DEBUG, "attempting to remove conference, name => %s\n", conf->name ) ; ++ ++ struct ast_conference *conf_current = conflist ; ++ struct ast_conference *conf_temp = NULL ; ++ ++ // loop through list of conferences ++ while ( conf_current != NULL ) ++ { ++ // if conf_current point to the passed conf, ++ if ( conf_current == conf ) ++ { ++ if ( conf_temp == NULL ) ++ { ++ // this is the first conf in the list, so we just point ++ // conflist past the current conf to the next ++ conflist = conf_current->next ; ++ } ++ else ++ { ++ // this is not the first conf in the list, so we need to ++ // point the preceeding conf to the next conf in the list ++ conf_temp->next = conf_current->next ; ++ } ++ ++ // ++ // do some frame clean up ++ // ++ ++ for ( c = 0 ; c < AC_SUPPORTED_FORMATS ; ++c ) ++ { ++ // free the translation paths ++ if ( conf_current->from_slinear_paths[ c ] != NULL ) ++ { ++ ast_translator_free_path( conf_current->from_slinear_paths[ c ] ) ; ++ conf_current->from_slinear_paths[ c ] = NULL ; ++ } ++ } ++ ++ // calculate time in conference ++ // total time converted to seconds ++ long tt = ast_tvdiff_ms(ast_tvnow(), ++ conf_current->stats.time_entered) / 1000; ++ ++ // report accounting information ++ if (conf->debug_flag) ++ { ++ ast_log( LOG_NOTICE, "conference accounting, fi => %ld, fo => %ld, fm => %ld, tt => %ld\n", ++ conf_current->stats.frames_in, conf_current->stats.frames_out, conf_current->stats.frames_mixed, tt ) ; ++ ++ ast_log( AST_CONF_DEBUG, "removed conference, name => %s\n", conf_current->name ) ; ++ } ++ ++ ast_mutex_unlock( &conf_current->lock ) ; ++ ++ free( conf_current ) ; ++ conf_current = NULL ; ++ ++ break ; ++ } ++ ++ // save a refence to the soon to be previous conf ++ conf_temp = conf_current ; ++ ++ // move conf_current to the next in the list ++ conf_current = conf_current->next ; ++ } ++ ++ // count new conference ++ --conference_count ; ++ ++ return ; ++} ++ ++int get_new_id( struct ast_conference *conf ) ++{ ++ // must have the conf lock when calling this ++ int newid; ++ struct ast_conf_member *othermember; ++ // get a video ID for this member ++ newid = 0; ++ othermember = conf->memberlist; ++ while (othermember) ++ { ++ if (othermember->id == newid) ++ { ++ newid++; ++ othermember = conf->memberlist; ++ } ++ else ++ { ++ othermember = othermember->next; ++ } ++ } ++ return newid; ++} ++ ++ ++int end_conference(const char *name, int hangup ) ++{ ++ struct ast_conference *conf; ++ ++ // acquire the conference list lock ++ ast_mutex_lock(&conflist_lock); ++ ++ conf = find_conf(name); ++ if ( conf == NULL ) ++ { ++ ast_log( LOG_WARNING, "could not find conference\n" ) ; ++ ++ // release the conference list lock ++ ast_mutex_unlock(&conflist_lock); ++ ++ return -1 ; ++ } ++ ++ // acquire the conference lock ++ ast_mutex_lock( &conf->lock ) ; ++ ++ // get list of conference members ++ struct ast_conf_member* member = conf->memberlist ; ++ ++ // loop over member list and request hangup ++ while ( member != NULL ) ++ { ++ // acquire member mutex and request hangup ++ // or just kick ++ ast_mutex_lock( &member->lock ) ; ++ if (hangup) ++ ast_softhangup( member->chan, 1 ) ; ++ else ++ member->kick_flag = 1; ++ ast_mutex_unlock( &member->lock ) ; ++ ++ // go on to the next member ++ // ( we have the conf lock, so we know this is okay ) ++ member = member->next ; ++ } ++ ++ // release the conference lock ++ ast_mutex_unlock( &conf->lock ) ; ++ ++ // release the conference list lock ++ ast_mutex_unlock(&conflist_lock); ++ ++ return 0 ; ++} ++ ++// ++// member-related functions ++// ++ ++// This function should be called with conflist_lock held ++void add_member( struct ast_conf_member *member, struct ast_conference *conf ) ++{ ++ int newid, last_id; ++ struct ast_conf_member *othermember; ++ int count; ++ ++ if ( conf == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to add member to NULL conference\n" ) ; ++ return ; ++ } ++ ++ // acquire the conference lock ++ ast_mutex_lock( &conf->lock ) ; ++ ++ if (member->id < 0) ++ { ++ // get an ID for this member ++ newid = get_new_id( conf ); ++ member->id = newid; ++ } else ++ { ++ // boot anyone who has this id already ++ othermember = conf->memberlist; ++ while (othermember) ++ { ++ if (othermember->id == member->id) ++ othermember->id = -1; ++ othermember = othermember->next; ++ } ++ } ++ ++ if ( member->mute_video ) ++ { ++ send_text_message_to_member(member, AST_CONF_CONTROL_STOP_VIDEO); ++ } ++ ++ // set a long term id ++ int new_initial_id = 0; ++ othermember = conf->memberlist; ++ while (othermember) ++ { ++ if (othermember->initial_id >= new_initial_id) ++ new_initial_id++; ++ ++ othermember = othermember->next; ++ } ++ member->initial_id = new_initial_id; ++ ++ ++ ast_log( AST_CONF_DEBUG, "new video id %d\n", newid) ; ++ ++ if (conf->memberlist) last_id = conf->memberlist->id; ++ else last_id = 0; ++ ++ if (member->req_id < 0) // otherwise pre-selected in create_member ++ { ++ // want to watch the last person to 0 or 1 (for now) ++ if (member->id > 0) member->req_id = 0; ++ else member->req_id = 1; ++ } ++ ++ member->next = conf->memberlist ; // next is now list ++ conf->memberlist = member ; // member is now at head of list ++ ++ // update conference stats ++ count = count_member( member, conf, 1 ) ; ++ ++ ast_log( AST_CONF_DEBUG, "member added to conference, name => %s\n", conf->name ) ; ++ ++ // release the conference lock ++ ast_mutex_unlock( &conf->lock ) ; ++ ++ return ; ++} ++ ++int remove_member( struct ast_conf_member* member, struct ast_conference* conf ) ++{ ++ // check for member ++ if ( member == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to remove NULL member\n" ) ; ++ return -1 ; ++ } ++ ++ // check for conference ++ if ( conf == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to remove member from NULL conference\n" ) ; ++ return -1 ; ++ } ++ ++ // ++ // loop through the member list looking ++ // for the requested member ++ // ++ ++ ast_mutex_lock( &conf->lock ); ++ ++ struct ast_conf_member *member_list = conf->memberlist ; ++ struct ast_conf_member *member_temp = NULL ; ++ ++ int count = -1 ; // default return code ++ ++ while ( member_list != NULL ) ++ { ++ // set conference to send no_video to anyone who was watching us ++ ast_mutex_lock( &member_list->lock ) ; ++ if (member_list->req_id == member->id) ++ { ++ member_list->conference = 1; ++ } ++ ast_mutex_unlock( &member_list->lock ) ; ++ member_list = member_list->next ; ++ } ++ ++ member_list = conf->memberlist ; ++ ++ int member_is_moderator = member->ismoderator; ++ ++ while ( member_list != NULL ) ++ { ++ // If member is driven by the currently visited member, break the association ++ if ( member_list->driven_member == member ) ++ { ++ // Acquire member mutex ++ ast_mutex_lock(&member_list->lock); ++ ++ member_list->driven_member = NULL; ++ ++ // Release member mutex ++ ast_mutex_unlock(&member_list->lock); ++ } ++ ++ if ( member_list == member ) ++ { ++ ++ // ++ // log some accounting information ++ // ++ ++ // calculate time in conference (in seconds) ++ long tt = ast_tvdiff_ms(ast_tvnow(), ++ member->time_entered) / 1000; ++ ++ if (conf->debug_flag) ++ { ++ ast_log( ++ LOG_NOTICE, ++ "member accounting, channel => %s, te => %ld, fi => %ld, fid => %ld, fo => %ld, fod => %ld, tt => %ld\n", ++ member->channel_name, ++ member->time_entered.tv_sec, member->frames_in, member->frames_in_dropped, ++ member->frames_out, member->frames_out_dropped, tt ++ ) ; ++ } ++ ++ // ++ // if this is the first member in the linked-list, ++ // skip over the first member in the list, else ++ // ++ // point the previous 'next' to the current 'next', ++ // thus skipping the current member in the list ++ // ++ if ( member_temp == NULL ) ++ conf->memberlist = member->next ; ++ else ++ member_temp->next = member->next ; ++ ++ // update conference stats ++ count = count_member( member, conf, 0 ) ; ++ ++ // Check if member is the default or current video source ++ if ( conf->current_video_source_id == member->id ) ++ { ++ if ( conf->video_locked ) ++ unlock_conference(conf->name); ++ do_video_switching(conf, conf->default_video_source_id, 0); ++ } else if ( conf->default_video_source_id == member->id ) ++ { ++ conf->default_video_source_id = -1; ++ } ++ ++ // output to manager... ++ manager_event( ++ EVENT_FLAG_CALL, ++ "ConferenceLeave", ++ "ConferenceName: %s\r\n" ++ "Member: %d\r\n" ++ "Channel: %s\r\n" ++ "CallerID: %s\r\n" ++ "CallerIDName: %s\r\n" ++ "Duration: %ld\r\n" ++ "Count: %d\r\n", ++ conf->name, ++ member->id, ++ member->channel_name, ++ member->callerid, ++ member->callername, ++ tt, count ++ ) ; ++ ++ // save a pointer to the current member, ++ // and then point to the next member in the list ++ member_list = member_list->next ; ++ ++ // leave member_temp alone. ++ // it already points to the previous (or NULL). ++ // it will still be the previous after member is deleted ++ ++ // notify others about leave ++ char * leave_snd = member->leave_snd; ++ if (conf->membercount && strcmp(leave_snd, "-")>1) ++ { ++ struct ast_conf_member *membertest = conf->memberlist; ++ while (membertest != NULL) ++ { ++ if (membertest != member) ++ { ++ // lock member for basic_play_sound ++ ast_mutex_lock(&membertest->lock); ++ ++ // basic_play_sound unlock member automatically. do not mute on enter message ++ if (!basic_play_sound (membertest, leave_snd, 0)) ++ { ++ ast_log(LOG_ERROR, "playing conference[%d] leave message <%s> FAILED on <%s>\n", conf->membercount, leave_snd, membertest->channel_name) ; ++ } ++ else ++ { ++ ast_log(LOG_NOTICE, "playing conference[%d] leave message <%s> on <%s>\n", conf->membercount, leave_snd, membertest->channel_name); ++ } ++ membertest = membertest->next; ++ } ++ } ++ } ++ ++ // delete the member ++ delete_member( member ) ; ++ ++ ast_log( AST_CONF_DEBUG, "removed member from conference, name => %s, remaining => %d\n", ++ conf->name, conf->membercount ) ; ++ ++ //break ; ++ } ++ else ++ { ++ // if member is a moderator, we end the conference when they leave ++ if ( member_is_moderator ) ++ { ++ ast_mutex_lock( &member_list->lock ) ; ++ member_list->kick_flag = 1; ++ ast_mutex_unlock( &member_list->lock ) ; ++ } ++ ++ // save a pointer to the current member, ++ // and then point to the next member in the list ++ member_temp = member_list ; ++ member_list = member_list->next ; ++ } ++ } ++ ast_mutex_unlock( &conf->lock ); ++ ++ // return -1 on error, or the number of members ++ // remaining if the requested member was deleted ++ return count ; ++} ++ ++int count_member( struct ast_conf_member* member, struct ast_conference* conf, short add_member ) ++{ ++ if ( member == NULL || conf == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to count member\n" ) ; ++ return -1 ; ++ } ++ ++ short delta = ( add_member == 1 ) ? 1 : -1 ; ++ ++ // increment member count ++ conf->membercount += delta ; ++ ++ return conf->membercount ; ++} ++ ++// ++// queue incoming frame functions ++// ++ ++ ++ ++ ++// ++// get conference stats ++// ++ ++// ++// returns: -1 => error, 0 => debugging off, 1 => debugging on ++// state: on => 1, off => 0, toggle => -1 ++// ++int set_conference_debugging( const char* name, int state ) ++{ ++ if ( name == NULL ) ++ return -1 ; ++ ++ // acquire mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ struct ast_conference *conf = conflist ; ++ int new_state = -1 ; ++ ++ // loop through conf list ++ while ( conf != NULL ) ++ { ++ if ( strncasecmp( (const char*)&(conf->name), name, 80 ) == 0 ) ++ { ++ // lock conference ++ // ast_mutex_lock( &(conf->lock) ) ; ++ ++ // toggle or set the state ++ if ( state == -1 ) ++ conf->debug_flag = ( conf->debug_flag == 0 ) ? 1 : 0 ; ++ else ++ conf->debug_flag = ( state == 0 ) ? 0 : 1 ; ++ ++ new_state = conf->debug_flag ; ++ ++ // unlock conference ++ // ast_mutex_unlock( &(conf->lock) ) ; ++ ++ break ; ++ } ++ ++ conf = conf->next ; ++ } ++ ++ // release mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return new_state ; ++} ++ ++ ++int get_conference_count( void ) ++{ ++ return conference_count ; ++} ++ ++int show_conference_stats ( int fd ) ++{ ++ // no conferences exist ++ if ( conflist == NULL ) ++ { ++ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized.\n") ; ++ return 0 ; ++ } ++ ++ // acquire mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ struct ast_conference *conf = conflist ; ++ ++ ast_cli( fd, "%-20.20s %-40.40s\n", "Name", "Members") ; ++ ++ // loop through conf list ++ while ( conf != NULL ) ++ { ++ ast_cli( fd, "%-20.20s %3d\n", conf->name, conf->membercount ) ; ++ conf = conf->next ; ++ } ++ ++ // release mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return 1 ; ++} ++ ++int show_conference_list ( int fd, const char *name ) ++{ ++ struct ast_conf_member *member; ++ ++ // no conferences exist ++ if ( conflist == NULL ) ++ { ++ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", name ) ; ++ return 0 ; ++ } ++ ++ // acquire mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ struct ast_conference *conf = conflist ; ++ ++ // loop through conf list ++ while ( conf != NULL ) ++ { ++ if ( strncasecmp( (const char*)&(conf->name), name, 80 ) == 0 ) ++ { ++ // acquire conference mutex ++ ast_mutex_lock(&conf->lock); ++ ++ // do the biz ++ member = conf->memberlist ; ++ while ( member != NULL ) ++ { ++ ast_cli( fd, "User #: %d ", member->id ) ; ++ ast_cli( fd, "Channel: %s ", member->channel_name ) ; ++ ++ ast_cli( fd, "Flags:"); ++ if ( member->mute_video ) ast_cli( fd, "C"); ++ if ( member->norecv_video ) ast_cli( fd, "c"); ++ if ( member->mute_audio ) ast_cli( fd, "L"); ++ if ( member->norecv_audio ) ast_cli( fd, "l"); ++ if ( member->vad_flag ) ast_cli( fd, "V"); ++ if ( member->denoise_flag ) ast_cli( fd, "D"); ++ if ( member->agc_flag ) ast_cli( fd, "A"); ++ if ( member->dtmf_switch ) ast_cli( fd, "X"); ++ if ( member->dtmf_relay ) ast_cli( fd, "R"); ++ if ( member->vad_switch ) ast_cli( fd, "S"); ++ if ( member->ismoderator ) ast_cli( fd, "M"); ++ if ( member->no_camera ) ast_cli( fd, "N"); ++ if ( member->does_text ) ast_cli( fd, "t"); ++ if ( member->via_telephone ) ast_cli( fd, "T"); ++ ast_cli( fd, " " ); ++ ++ if ( member->id == conf->default_video_source_id ) ++ ast_cli(fd, "Default "); ++ if ( member->id == conf->current_video_source_id ) ++ { ++ ast_cli(fd, "Showing "); ++ if ( conf->video_locked ) ++ ast_cli(fd, "Locked "); ++ } ++ if ( member->driven_member != NULL ) ++ { ++ ast_cli(fd, "Driving:%s(%d) ", member->driven_member->channel_name, member->driven_member->id); ++ } ++ ++ ast_cli( fd, "\n"); ++ member = member->next; ++ } ++ ++ // release conference mutex ++ ast_mutex_unlock(&conf->lock); ++ ++ break ; ++ } ++ ++ conf = conf->next ; ++ } ++ ++ // release mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return 1 ; ++} ++ ++/* Dump list of conference info */ ++int manager_conference_list( struct mansession *s, const struct message *m ) ++{ ++ const char *id = astman_get_header(m,"ActionID"); ++ const char *conffilter = astman_get_header(m,"Conference"); ++ char idText[256] = ""; ++ struct ast_conf_member *member; ++ ++ astman_send_ack(s, m, "Conference list will follow"); ++ ++ // no conferences exist ++ if ( conflist == NULL ) ++ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", conffilter );; ++ ++ if (!ast_strlen_zero(id)) { ++ snprintf(idText,256,"ActionID: %s\r\n",id); ++ } ++ ++ // acquire mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ struct ast_conference *conf = conflist ; ++ ++ // loop through conf list ++ while ( conf != NULL ) ++ { ++ if ( strncasecmp( (const char*)&(conf->name), conffilter, 80 ) == 0 ) ++ { ++ // do the biz ++ member = conf->memberlist ; ++ while (member != NULL) ++ { ++ astman_append(s, "Event: ConferenceEntry\r\n" ++ "ConferenceName: %s\r\n" ++ "Member: %d\r\n" ++ "Channel: %s\r\n" ++ "CallerID: %s\r\n" ++ "CallerIDName: %s\r\n" ++ "Muted: %s\r\n" ++ "VideoMuted: %s\r\n" ++ "Default: %s\r\n" ++ "Current: %s\r\n" ++ "%s" ++ "\r\n", ++ conf->name, ++ member->id, ++ member->channel_name, ++ member->chan->cid.cid_num ? member->chan->cid.cid_num : "unknown", ++ member->chan->cid.cid_name ? member->chan->cid.cid_name : "unknown", ++ member->mute_audio ? "YES" : "NO", ++ member->mute_video ? "YES" : "NO", ++ member->id == conf->default_video_source_id ? "YES" : "NO", ++ member->id == conf->current_video_source_id ? "YES" : "NO", ++ idText); ++ member = member->next; ++ } ++ break ; ++ } ++ ++ conf = conf->next ; ++ } ++ ++ // release mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ astman_append(s, ++ "Event: ConferenceListComplete\r\n" ++ "%s" ++ "\r\n",idText); ++ ++ return RESULT_SUCCESS; ++} ++ ++int kick_member ( const char* confname, int user_id) ++{ ++ struct ast_conf_member *member; ++ int res = 0; ++ ++ // no conferences exist ++ if ( conflist == NULL ) ++ { ++ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ; ++ return 0 ; ++ } ++ ++ // acquire mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ struct ast_conference *conf = conflist ; ++ ++ // loop through conf list ++ while ( conf != NULL ) ++ { ++ if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 ) ++ { ++ // do the biz ++ ast_mutex_lock( &conf->lock ) ; ++ member = conf->memberlist ; ++ while (member != NULL) ++ { ++ if (member->id == user_id) ++ { ++ ast_mutex_lock( &member->lock ) ; ++ member->kick_flag = 1; ++ //ast_soft_hangup(member->chan); ++ ast_mutex_unlock( &member->lock ) ; ++ ++ res = 1; ++ } ++ member = member->next; ++ } ++ ast_mutex_unlock( &conf->lock ) ; ++ break ; ++ } ++ ++ conf = conf->next ; ++ } ++ ++ // release mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return res ; ++} ++ ++int kick_channel ( const char *confname, const char *channel) ++{ ++ struct ast_conf_member *member; ++ int res = 0; ++ ++ // no conferences exist ++ if ( conflist == NULL ) ++ { ++ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ; ++ return 0 ; ++ } ++ ++ if ( confname == NULL || channel == NULL || strlen(confname) == 0 || strlen(channel) == 0 ) ++ return 0; ++ ++ // acquire mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ struct ast_conference *conf = conflist ; ++ ++ // loop through conf list ++ while ( conf != NULL ) ++ { ++ if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 ) ++ { ++ // do the biz ++ ast_mutex_lock( &conf->lock ) ; ++ member = conf->memberlist ; ++ while ( member != NULL ) ++ { ++ if ( strncasecmp( member->channel_name, channel, 80 ) == 0 ) ++ { ++ ast_mutex_lock( &member->lock ) ; ++ member->kick_flag = 1; ++ //ast_soft_hangup(member->chan); ++ ast_mutex_unlock( &member->lock ) ; ++ ++ res = 1; ++ } ++ member = member->next; ++ } ++ ast_mutex_unlock( &conf->lock ) ; ++ break ; ++ } ++ ++ conf = conf->next ; ++ } ++ ++ // release mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return res ; ++} ++ ++int kick_all ( void ) ++{ ++ struct ast_conf_member *member; ++ int res = 0; ++ ++ // no conferences exist ++ if ( conflist == NULL ) ++ { ++ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized\n" ) ; ++ return 0 ; ++ } ++ ++ // acquire mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ struct ast_conference *conf = conflist ; ++ ++ // loop through conf list ++ while ( conf != NULL ) ++ { ++ // do the biz ++ ast_mutex_lock( &conf->lock ) ; ++ member = conf->memberlist ; ++ while (member != NULL) ++ { ++ ast_mutex_lock( &member->lock ) ; ++ member->kick_flag = 1; ++ ast_mutex_unlock( &member->lock ) ; ++ member = member->next; ++ } ++ ast_mutex_unlock( &conf->lock ) ; ++ break ; ++ ++ conf = conf->next ; ++ } ++ ++ // release mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return res ; ++} ++ ++int mute_member ( const char* confname, int user_id) ++{ ++ struct ast_conf_member *member; ++ int res = 0; ++ ++ // no conferences exist ++ if ( conflist == NULL ) ++ { ++ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ; ++ return 0 ; ++ } ++ ++ // acquire mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ struct ast_conference *conf = conflist ; ++ ++ // loop through conf list ++ while ( conf != NULL ) ++ { ++ if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 ) ++ { ++ // do the biz ++ ast_mutex_lock( &conf->lock ) ; ++ member = conf->memberlist ; ++ while (member != NULL) ++ { ++ if (member->id == user_id) ++ { ++ ast_mutex_lock( &member->lock ) ; ++ member->mute_audio = 1; ++ ast_mutex_unlock( &member->lock ) ; ++ res = 1; ++ } ++ member = member->next; ++ } ++ ast_mutex_unlock( &conf->lock ) ; ++ break ; ++ } ++ ++ conf = conf->next ; ++ } ++ ++ // release mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return res ; ++} ++ ++int mute_channel ( const char* confname, const char* user_chan) ++{ ++ struct ast_conf_member *member; ++ int res = 0; ++ ++ // no conferences exist ++ if ( conflist == NULL ) ++ { ++ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ; ++ return 0 ; ++ } ++ ++ // acquire mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ struct ast_conference *conf = conflist ; ++ ++ // loop through conf list ++ while ( conf != NULL ) ++ { ++ if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 ) ++ { ++ // do the biz ++ ast_mutex_lock( &conf->lock ) ; ++ member = conf->memberlist ; ++ while (member != NULL) ++ { ++ if (strncasecmp( member->channel_name, user_chan, 80 ) == 0) ++ { ++ ast_mutex_lock( &member->lock ) ; ++ member->mute_audio = 1; ++ ast_mutex_unlock( &member->lock ) ; ++ res = 1; ++ } ++ member = member->next; ++ } ++ ast_mutex_unlock( &conf->lock ) ; ++ break ; ++ } ++ ++ conf = conf->next ; ++ } ++ ++ // release mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return res ; ++} ++ ++int unmute_member ( const char* confname, int user_id) ++{ ++ struct ast_conf_member *member; ++ int res = 0; ++ ++ // no conferences exist ++ if ( conflist == NULL ) ++ { ++ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ; ++ return 0 ; ++ } ++ ++ // acquire mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ struct ast_conference *conf = conflist ; ++ ++ // loop through conf list ++ while ( conf != NULL ) ++ { ++ if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 ) ++ { ++ // do the biz ++ ast_mutex_lock( &conf->lock ) ; ++ member = conf->memberlist ; ++ while (member != NULL) ++ { ++ if (member->id == user_id) ++ { ++ ast_mutex_lock( &member->lock ) ; ++ member->mute_audio = 0; ++ ast_mutex_unlock( &member->lock ) ; ++ res = 1; ++ } ++ member = member->next; ++ } ++ ast_mutex_unlock( &conf->lock ) ; ++ break ; ++ } ++ ++ conf = conf->next ; ++ } ++ ++ // release mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return res ; ++} ++ ++int unmute_channel (const char* confname, const char* user_chan) ++{ ++ struct ast_conf_member *member; ++ int res = 0; ++ ++ // no conferences exist ++ if ( conflist == NULL ) ++ { ++ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ; ++ return 0 ; ++ } ++ ++ // acquire mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ struct ast_conference *conf = conflist ; ++ ++ // loop through conf list ++ while ( conf != NULL ) ++ { ++ if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 ) ++ { ++ // do the biz ++ ast_mutex_lock( &conf->lock ) ; ++ member = conf->memberlist ; ++ while (member != NULL) ++ { ++ if (strncasecmp( member->channel_name, user_chan, 80 ) == 0) ++ { ++ ast_mutex_lock( &member->lock ) ; ++ member->mute_audio = 0; ++ ast_mutex_unlock( &member->lock ) ; ++ res = 1; ++ } ++ member = member->next; ++ } ++ ast_mutex_unlock( &conf->lock ) ; ++ break ; ++ } ++ ++ conf = conf->next ; ++ } ++ ++ // release mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return res ; ++} ++ ++int viewstream_switch ( const char* confname, int user_id, int stream_id ) ++{ ++ struct ast_conf_member *member; ++ int res = 0; ++ ++ // no conferences exist ++ if ( conflist == NULL ) ++ { ++ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ; ++ return 0 ; ++ } ++ ++ // acquire mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ struct ast_conference *conf = conflist ; ++ ++ // loop through conf list ++ while ( conf != NULL ) ++ { ++ if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 ) ++ { ++ // do the biz ++ ast_mutex_lock( &conf->lock ) ; ++ member = conf->memberlist ; ++ while (member != NULL) ++ { ++ if (member->id == user_id) ++ { ++ // switch the video ++ ast_mutex_lock( &member->lock ) ; ++ ++ member->req_id = stream_id; ++ member->conference = 1; ++ ++ ast_mutex_unlock( &member->lock ) ; ++ res = 1; ++ } ++ member = member->next; ++ } ++ ast_mutex_unlock( &conf->lock ) ; ++ break ; ++ } ++ ++ conf = conf->next ; ++ } ++ ++ // release mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return res ; ++} ++ ++int viewchannel_switch ( const char* confname, const char* userchan, const char* streamchan ) ++{ ++ struct ast_conf_member *member; ++ int res = 0; ++ int stream_id = -1; ++ ++ // no conferences exist ++ if ( conflist == NULL ) ++ { ++ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ; ++ return 0 ; ++ } ++ ++ // acquire mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ struct ast_conference *conf = conflist ; ++ ++ // loop through conf list ++ while ( conf != NULL ) ++ { ++ if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 ) ++ { ++ // do the biz ++ ast_mutex_lock( &conf->lock ) ; ++ member = conf->memberlist ; ++ while (member != NULL) ++ { ++ if (strncasecmp( member->channel_name, streamchan, 80 ) == 0) ++ { ++ stream_id = member->id; ++ } ++ member = member->next; ++ } ++ ast_mutex_unlock( &conf->lock ) ; ++ if (stream_id >= 0) ++ { ++ // do the biz ++ ast_mutex_lock( &conf->lock ) ; ++ member = conf->memberlist ; ++ while (member != NULL) ++ { ++ if (strncasecmp( member->channel_name, userchan, 80 ) == 0) ++ { ++ // switch the video ++ ast_mutex_lock( &member->lock ) ; ++ ++ member->req_id = stream_id; ++ member->conference = 1; ++ ++ ast_mutex_unlock( &member->lock ) ; ++ res = 1; ++ } ++ member = member->next; ++ } ++ ast_mutex_unlock( &conf->lock ) ; ++ } ++ break ; ++ } ++ ++ conf = conf->next ; ++ } ++ ++ // release mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return res ; ++} ++ ++int get_conference_stats( ast_conference_stats* stats, int requested ) ++{ ++ // no conferences exist ++ if ( conflist == NULL ) ++ { ++ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialize\n" ) ; ++ return 0 ; ++ } ++ ++ // acquire mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ // compare the number of requested to the number of available conferences ++ requested = ( get_conference_count() < requested ) ? get_conference_count() : requested ; ++ ++ // ++ // loop through conf list ++ // ++ ++ struct ast_conference* conf = conflist ; ++ int count = 0 ; ++ ++ while ( count <= requested && conf != NULL ) ++ { ++ // copy stats struct to array ++ stats[ count ] = conf->stats ; ++ ++ conf = conf->next ; ++ ++count ; ++ } ++ ++ // release mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return count ; ++} ++ ++int get_conference_stats_by_name( ast_conference_stats* stats, const char* name ) ++{ ++ // no conferences exist ++ if ( conflist == NULL ) ++ { ++ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", name ) ; ++ return 0 ; ++ } ++ ++ // make sure stats is null ++ stats = NULL ; ++ ++ // acquire mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ struct ast_conference *conf = conflist ; ++ ++ // loop through conf list ++ while ( conf != NULL ) ++ { ++ if ( strncasecmp( (const char*)&(conf->name), name, 80 ) == 0 ) ++ { ++ // copy stats for found conference ++ *stats = conf->stats ; ++ break ; ++ } ++ ++ conf = conf->next ; ++ } ++ ++ // release mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return ( stats == NULL ) ? 0 : 1 ; ++} ++ ++struct ast_conf_member *find_member (const char *chan, int lock) ++{ ++ struct ast_conf_member *found = NULL; ++ struct ast_conf_member *member; ++ struct ast_conference *conf; ++ ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ conf = conflist; ++ ++ // loop through conf list ++ while ( conf != NULL && !found ) ++ { ++ // lock conference ++ ast_mutex_lock( &conf->lock ); ++ ++ member = conf->memberlist ; ++ ++ while (member != NULL) ++ { ++ if(!strcmp(member->channel_name, chan)) { ++ found = member; ++ if(lock) ++ ast_mutex_lock(&member->lock); ++ break; ++ } ++ member = member->next; ++ } ++ ++ // unlock conference ++ ast_mutex_unlock( &conf->lock ); ++ ++ conf = conf->next ; ++ } ++ ++ // release mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return found; ++} ++ ++// All the VAD-based video switching magic happens here ++// This function should be called inside conference_exec ++// The conference mutex should be locked, we don't have to do it here ++void do_VAD_switching(struct ast_conference *conf) ++{ ++ struct ast_conf_member *member; ++ struct timeval current_time; ++ long longest_speaking; ++ struct ast_conf_member *longest_speaking_member; ++ int current_silent, current_no_camera, current_video_mute; ++ int default_no_camera, default_video_mute; ++ ++ current_time = ast_tvnow(); ++ ++ // Scan the member list looking for the longest speaking member ++ // We also check if the currently speaking member has been silent for a while ++ // Also, we check for camera disabled or video muted members ++ // We say that a member is speaking after his speaking state has been on for ++ // at least AST_CONF_VIDEO_START_TIMEOUT ms ++ // We say that a member is silent after his speaking state has been off for ++ // at least AST_CONF_VIDEO_STOP_TIMEOUT ms ++ longest_speaking = 0; ++ longest_speaking_member = NULL; ++ current_silent = 0; ++ current_no_camera = 0; ++ current_video_mute = 0; ++ default_no_camera = 0; ++ default_video_mute = 0; ++ for ( member = conf->memberlist ; ++ member != NULL ; ++ member = member->next ) ++ { ++ // Has the state changed since last time through this loop? Notify! ++ if ( member->speaking_state_notify ) ++ { ++/* fprintf(stderr, "Mihai: member %d, channel %s has changed state to %s\n", ++ member->id, ++ member->channel_name, ++ ((member->speaking_state == 1 ) ? "speaking" : "silent") ++ ); */ ++ } ++ ++ // If a member connects via telephone, they don't have video ++ if ( member->via_telephone ) ++ continue; ++ ++ // We check for no VAD switching, video-muted or camera disabled ++ // If yes, this member will not be considered as a candidate for switching ++ // If this is the currently speaking member, then mark it so we force a switch ++ if ( !member->vad_switch ) ++ continue; ++ ++ if ( member->mute_video ) ++ { ++ if ( member->id == conf->default_video_source_id ) ++ default_video_mute = 1; ++ if ( member->id == conf->current_video_source_id ) ++ current_video_mute = 1; ++ else ++ continue; ++ } ++ ++ if ( member->no_camera ) ++ { ++ if ( member->id == conf->default_video_source_id ) ++ default_no_camera = 1; ++ if ( member->id == conf->current_video_source_id ) ++ current_no_camera = 1; ++ else ++ continue; ++ } ++ ++ // Check if current speaker has been silent for a while ++ if ( member->id == conf->current_video_source_id && ++ member->speaking_state == 0 && ++ ast_tvdiff_ms(current_time, member->last_state_change) > AST_CONF_VIDEO_STOP_TIMEOUT ) ++ { ++ current_silent = 1; ++ } ++ ++ // Find a candidate to switch to by looking for the longest speaking member ++ // We exclude the current video source from the search ++ if ( member->id != conf->current_video_source_id && member->speaking_state == 1 ) ++ { ++ long tmp = ast_tvdiff_ms(current_time, member->last_state_change); ++ if ( tmp > AST_CONF_VIDEO_START_TIMEOUT && tmp > longest_speaking ) ++ { ++ longest_speaking = tmp; ++ longest_speaking_member = member; ++ } ++ } ++ } ++ ++ // We got our results, now let's make a decision ++ // If the currently speaking member has been marked as silent, then we take the longest ++ // speaking member. If no member is speaking, we go to default ++ // As a policy we don't want to switch away from a member that is speaking ++ // however, we might need to refine this to avoid a situation when a member has a ++ // low noise threshold or its VAD is simply stuck ++ if ( current_silent || current_no_camera || current_video_mute || conf->current_video_source_id < 0 ) ++ { ++ if ( longest_speaking_member != NULL ) ++ { ++ do_video_switching(conf, longest_speaking_member->id, 0); ++ } else ++ { ++ // If there's nobody speaking and we have a default that can send video, switch to it ++ // If not, then switch to empty (-1) ++ if ( conf->default_video_source_id >= 0 && ++ !default_no_camera && ++ !default_video_mute ++ ) ++ do_video_switching(conf, conf->default_video_source_id, 0); ++ else ++ do_video_switching(conf, -1, 0); ++ } ++ } ++} ++ ++int lock_conference(const char *conference, int member_id) ++{ ++ struct ast_conference *conf; ++ struct ast_conf_member *member; ++ int res; ++ ++ if ( conference == NULL || member_id < 0 ) ++ return -1 ; ++ ++ // acquire mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ // Look for conference ++ res = 0; ++ for ( conf = conflist ; conf != NULL ; conf = conf->next ) ++ { ++ if ( strcmp(conference, conf->name) == 0 ) ++ { ++ // Search member list for our member ++ // acquire conference mutex ++ ast_mutex_lock( &conf->lock ); ++ ++ for ( member = conf->memberlist ; member != NULL ; member = member->next ) ++ { ++ if ( member->id == member_id && !member->mute_video ) ++ { ++ do_video_switching(conf, member_id, 0); ++ conf->video_locked = 1; ++ res = 1; ++ ++ manager_event(EVENT_FLAG_CALL, "ConferenceLock", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name); ++ break; ++ } ++ } ++ ++ // Release conference mutex ++ ast_mutex_unlock( &conf->lock ); ++ break; ++ } ++ } ++ ++ // release mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return res; ++} ++ ++int lock_conference_channel(const char *conference, const char *channel) ++{ ++ struct ast_conference *conf; ++ struct ast_conf_member *member; ++ int res; ++ ++ if ( conference == NULL || channel == NULL ) ++ return -1 ; ++ ++ // acquire mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ // Look for conference ++ res = 0; ++ for ( conf = conflist ; conf != NULL ; conf = conf->next ) ++ { ++ if ( strcmp(conference, conf->name) == 0 ) ++ { ++ // Search member list for our member ++ // acquire conference mutex ++ ast_mutex_lock( &conf->lock ); ++ ++ for ( member = conf->memberlist ; member != NULL ; member = member->next ) ++ { ++ if ( strcmp(channel, member->channel_name) == 0 && !member->mute_video ) ++ { ++ do_video_switching(conf, member->id, 0); ++ conf->video_locked = 1; ++ res = 1; ++ ++ manager_event(EVENT_FLAG_CALL, "ConferenceLock", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name); ++ break; ++ } ++ } ++ ++ // Release conference mutex ++ ast_mutex_unlock( &conf->lock ); ++ break; ++ } ++ } ++ ++ // release mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return res; ++} ++ ++int unlock_conference(const char *conference) ++{ ++ struct ast_conference *conf; ++ int res; ++ ++ if ( conference == NULL ) ++ return -1; ++ ++ // acquire conference list mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ // Look for conference ++ res = 0; ++ for ( conf = conflist ; conf != NULL ; conf = conf->next ) ++ { ++ if ( strcmp(conference, conf->name) == 0 ) ++ { ++ conf->video_locked = 0; ++ manager_event(EVENT_FLAG_CALL, "ConferenceUnlock", "ConferenceName: %s\r\n", conf->name); ++ do_video_switching(conf, conf->default_video_source_id, 0); ++ res = 1; ++ ++ break; ++ } ++ } ++ ++ // release conference list mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return res; ++} ++ ++int set_default_id(const char *conference, int member_id) ++{ ++ struct ast_conference *conf; ++ struct ast_conf_member *member; ++ int res; ++ ++ if ( conference == NULL ) ++ return -1 ; ++ ++ // acquire conference list mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ // Look for conference ++ res = 0; ++ for ( conf = conflist ; conf != NULL ; conf = conf->next ) ++ { ++ if ( strcmp(conference, conf->name) == 0 ) ++ { ++ if ( member_id < 0 ) ++ { ++ conf->default_video_source_id = -1; ++ manager_event(EVENT_FLAG_CALL, "ConferenceDefault", "ConferenceName: %s\r\nChannel: empty\r\n", conf->name); ++ res = 1; ++ break; ++ } else ++ { ++ // Search member list for our member ++ // acquire conference mutex ++ ast_mutex_lock( &conf->lock ); ++ ++ for ( member = conf->memberlist ; member != NULL ; member = member->next ) ++ { ++ // We do not allow video muted members or members that do not support ++ // VAD switching to become defaults ++ if ( member->id == member_id && ++ !member->mute_video && ++ member->vad_switch ++ ) ++ { ++ conf->default_video_source_id = member_id; ++ res = 1; ++ ++ manager_event(EVENT_FLAG_CALL, "ConferenceDefault", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name); ++ break; ++ } ++ } ++ ++ // Release conference mutex ++ ast_mutex_unlock( &conf->lock ); ++ break; ++ } ++ } ++ } ++ ++ // release conference list mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return res; ++ ++} ++ ++int set_default_channel(const char *conference, const char *channel) ++{ ++ struct ast_conference *conf; ++ struct ast_conf_member *member; ++ int res; ++ ++ if ( conference == NULL || channel == NULL ) ++ return -1 ; ++ ++ // acquire conference list mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ // Look for conference ++ res = 0; ++ for ( conf = conflist ; conf != NULL ; conf = conf->next ) ++ { ++ if ( strcmp(conference, conf->name) == 0 ) ++ { ++ // Search member list for our member ++ // acquire conference mutex ++ ast_mutex_lock( &conf->lock ); ++ ++ for ( member = conf->memberlist ; member != NULL ; member = member->next ) ++ { ++ // We do not allow video muted members or members that do not support ++ // VAD switching to become defaults ++ if ( strcmp(channel, member->channel_name) == 0 && ++ !member->mute_video && ++ member->vad_switch ++ ) ++ { ++ conf->default_video_source_id = member->id; ++ res = 1; ++ ++ manager_event(EVENT_FLAG_CALL, "ConferenceDefault", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name); ++ break; ++ } ++ } ++ ++ // Release conference mutex ++ ast_mutex_unlock( &conf->lock ); ++ break; ++ } ++ } ++ ++ // release conference list mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return res; ++} ++ ++int video_mute_member(const char *conference, int member_id) ++{ ++ struct ast_conference *conf; ++ struct ast_conf_member *member; ++ int res; ++ ++ if ( conference == NULL || member_id < 0 ) ++ return -1; ++ ++ res = 0; ++ ++ // acquire conference list mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ for ( conf = conflist ; conf != NULL ; conf = conf->next ) ++ { ++ if ( strcmp(conference, conf->name) == 0 ) ++ { ++ // acquire conference mutex ++ ast_mutex_lock( &conf->lock ); ++ ++ for ( member = conf->memberlist ; member != NULL ; member = member->next ) ++ { ++ if ( member->id == member_id ) ++ { ++ // acquire member mutex ++ ast_mutex_lock( &member->lock ); ++ ++ member->mute_video = 1; ++ ++ // release member mutex ++ ast_mutex_unlock( &member->lock ); ++ ++ manager_event(EVENT_FLAG_CALL, "ConferenceVideoMute", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name); ++ ++ if ( member->id == conf->current_video_source_id ) ++ { ++ do_video_switching(conf, conf->default_video_source_id, 0); ++ } ++ ++ res = 1; ++ break; ++ } ++ } ++ ++ // release conference mutex ++ ast_mutex_unlock( &conf->lock ); ++ ++ break; ++ } ++ } ++ ++ // release conference list mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return res; ++} ++ ++int video_unmute_member(const char *conference, int member_id) ++{ ++ struct ast_conference *conf; ++ struct ast_conf_member *member; ++ int res; ++ ++ if ( conference == NULL || member_id < 0 ) ++ return -1; ++ ++ res = 0; ++ ++ // acquire conference list mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ for ( conf = conflist ; conf != NULL ; conf = conf->next ) ++ { ++ if ( strcmp(conference, conf->name) == 0 ) ++ { ++ // acquire conference mutex ++ ast_mutex_lock( &conf->lock ); ++ ++ for ( member = conf->memberlist ; member != NULL ; member = member->next ) ++ { ++ if ( member->id == member_id ) ++ { ++ // acquire member mutex ++ ast_mutex_lock( &member->lock ); ++ ++ member->mute_video = 0; ++ ++ // release member mutex ++ ast_mutex_unlock( &member->lock ); ++ ++ manager_event(EVENT_FLAG_CALL, "ConferenceVideoUnmute", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name); ++ ++ res = 1; ++ break; ++ } ++ } ++ ++ // release conference mutex ++ ast_mutex_unlock( &conf->lock ); ++ ++ break; ++ } ++ } ++ ++ // release conference list mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return res; ++} ++ ++int video_mute_channel(const char *conference, const char *channel) ++{ ++ struct ast_conference *conf; ++ struct ast_conf_member *member; ++ int res; ++ ++ if ( conference == NULL || channel == NULL ) ++ return -1; ++ ++ res = 0; ++ ++ // acquire conference list mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ for ( conf = conflist ; conf != NULL ; conf = conf->next ) ++ { ++ if ( strcmp(conference, conf->name) == 0 ) ++ { ++ // acquire conference mutex ++ ast_mutex_lock( &conf->lock ); ++ ++ for ( member = conf->memberlist ; member != NULL ; member = member->next ) ++ { ++ if ( strcmp(channel, member->channel_name) == 0 ) ++ { ++ // acquire member mutex ++ ast_mutex_lock( &member->lock ); ++ ++ member->mute_video = 1; ++ ++ // release member mutex ++ ast_mutex_unlock( &member->lock ); ++ ++ manager_event(EVENT_FLAG_CALL, "ConferenceVideoMute", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name); ++ ++ if ( member->id == conf->current_video_source_id ) ++ { ++ do_video_switching(conf, conf->default_video_source_id, 0); ++ } ++ ++ res = 1; ++ break; ++ } ++ } ++ ++ // release conference mutex ++ ast_mutex_unlock( &conf->lock ); ++ ++ break; ++ } ++ } ++ ++ // release conference list mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return res; ++} ++ ++int video_unmute_channel(const char *conference, const char *channel) ++{ ++ struct ast_conference *conf; ++ struct ast_conf_member *member; ++ int res; ++ ++ if ( conference == NULL || channel == NULL ) ++ return -1; ++ ++ res = 0; ++ ++ // acquire conference list mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ for ( conf = conflist ; conf != NULL ; conf = conf->next ) ++ { ++ if ( strcmp(conference, conf->name) == 0 ) ++ { ++ // acquire conference mutex ++ ast_mutex_lock( &conf->lock ); ++ ++ for ( member = conf->memberlist ; member != NULL ; member = member->next ) ++ { ++ if ( strcmp(channel, member->channel_name) == 0 ) ++ { ++ // acquire member mutex ++ ast_mutex_lock( &member->lock ); ++ ++ member->mute_video = 0; ++ ++ // release member mutex ++ ast_mutex_unlock( &member->lock ); ++ ++ manager_event(EVENT_FLAG_CALL, "ConferenceVideoUnmute", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name); ++ ++ res = 1; ++ break; ++ } ++ } ++ ++ // release conference mutex ++ ast_mutex_unlock( &conf->lock ); ++ ++ break; ++ } ++ } ++ ++ // release conference list mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return res; ++} ++ ++// ++// Text message functions ++// ++int send_text(const char *conference, int member_id, const char *text) ++{ ++ struct ast_conference *conf; ++ struct ast_conf_member *member; ++ int res; ++ ++ if ( conference == NULL || member_id < 0 || text == NULL ) ++ return -1; ++ ++ res = 0; ++ ++ // acquire conference list mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ for ( conf = conflist ; conf != NULL ; conf = conf->next ) ++ { ++ if ( strcmp(conference, conf->name) == 0 ) ++ { ++ // acquire conference mutex ++ ast_mutex_lock( &conf->lock ); ++ ++ for ( member = conf->memberlist ; member != NULL ; member = member->next ) ++ { ++ if ( member->id == member_id ) ++ { ++ res = send_text_message_to_member(member, text) == 0; ++ break; ++ } ++ } ++ ++ // release conference mutex ++ ast_mutex_unlock( &conf->lock ); ++ ++ break; ++ } ++ } ++ ++ // release conference list mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ return res; ++} ++ ++int send_text_channel(const char *conference, const char *channel, const char *text) ++{ ++ struct ast_conference *conf; ++ struct ast_conf_member *member; ++ int res; ++ ++ if ( conference == NULL || channel == NULL || text == NULL ) ++ return -1; ++ ++ res = 0; ++ ++ // acquire conference list mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ for ( conf = conflist ; conf != NULL ; conf = conf->next ) ++ { ++ if ( strcmp(conference, conf->name) == 0 ) ++ { ++ // acquire conference mutex ++ ast_mutex_lock( &conf->lock ); ++ ++ for ( member = conf->memberlist ; member != NULL ; member = member->next ) ++ { ++ if ( strcmp(member->channel_name, channel) == 0 ) ++ { ++ res = send_text_message_to_member(member, text) == 0; ++ break; ++ } ++ } ++ ++ // release conference mutex ++ ast_mutex_unlock( &conf->lock ); ++ ++ break; ++ } ++ } ++ ++ // release conference list mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return res; ++} ++ ++int send_text_broadcast(const char *conference, const char *text) ++{ ++ struct ast_conference *conf; ++ struct ast_conf_member *member; ++ int res; ++ ++ if ( conference == NULL || text == NULL ) ++ return -1; ++ ++ res = 0; ++ ++ // acquire conference list mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ for ( conf = conflist ; conf != NULL ; conf = conf->next ) ++ { ++ if ( strcmp(conference, conf->name) == 0 ) ++ { ++ // acquire conference mutex ++ ast_mutex_lock( &conf->lock ); ++ ++ for ( member = conf->memberlist ; member != NULL ; member = member->next ) ++ { ++ if ( send_text_message_to_member(member, text) == 0 ) ++ res = res || 1; ++ } ++ ++ // release conference mutex ++ ast_mutex_unlock( &conf->lock ); ++ ++ break; ++ } ++ } ++ ++ // release conference list mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return res; ++} ++ ++// Creates a text frame and sends it to a given member ++// Returns 0 on success, -1 on failure ++int send_text_message_to_member(struct ast_conf_member *member, const char *text) ++{ ++ struct ast_frame *f; ++ ++ if ( member == NULL || text == NULL ) return -1; ++ ++ if ( member->does_text ) ++ { ++ f = create_text_frame(text, 1); ++ if ( f == NULL || queue_outgoing_text_frame(member, f) != 0) return -1; ++ ast_frfree(f); ++ } ++ ++ return 0; ++} ++ ++// Associates two members ++// Drives VAD-based video switching of dst_member from audio from src_member ++// This can be used when a member participates in a video conference but ++// talks using a telephone (simulcast) connection ++int drive(const char *conference, int src_member_id, int dst_member_id) ++{ ++ int res; ++ struct ast_conference *conf; ++ struct ast_conf_member *member; ++ struct ast_conf_member *src; ++ struct ast_conf_member *dst; ++ ++ if ( conference == NULL || src_member_id < 0 ) ++ return -1; ++ ++ res = 0; ++ ++ // acquire conference list mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ for ( conf = conflist ; conf != NULL ; conf = conf->next ) ++ { ++ if ( strcmp(conference, conf->name) == 0 ) ++ { ++ // acquire conference mutex ++ ast_mutex_lock( &conf->lock ); ++ ++ src = NULL; ++ dst = NULL; ++ for ( member = conf->memberlist ; member != NULL ; member = member->next ) ++ { ++ if ( member->id == src_member_id ) ++ src = member; ++ if ( member->id == dst_member_id ) ++ dst = member; ++ } ++ if ( src != NULL ) ++ { ++ // acquire member mutex ++ ast_mutex_lock(&src->lock); ++ ++ if ( dst != NULL ) ++ { ++ src->driven_member = dst; ++ // Make sure the driven member's speaker count is correct ++ if ( src->speaking_state == 1 ) ++ increment_speaker_count(src->driven_member, 1); ++ res = 1; ++ } else ++ { ++ if ( dst_member_id < 0 ) ++ { ++ // Make sure the driven member's speaker count is correct ++ if ( src->speaking_state == 1 ) ++ decrement_speaker_count(src->driven_member, 1); ++ src->driven_member = NULL; ++ res = 1; ++ } ++ } ++ ++ // release member mutex ++ ast_mutex_unlock(&src->lock); ++ } ++ ++ // release conference mutex ++ ast_mutex_unlock( &conf->lock ); ++ ++ break; ++ } ++ } ++ ++ // release conference list mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return res; ++} ++ ++// Associates two channels ++// Drives VAD-based video switching of dst_channel from audio from src_channel ++// This can be used when a member participates in a video conference but ++// talks using a telephone (simulcast) connection ++int drive_channel(const char *conference, const char *src_channel, const char *dst_channel) ++{ ++ int res; ++ struct ast_conference *conf; ++ struct ast_conf_member *member; ++ struct ast_conf_member *src; ++ struct ast_conf_member *dst; ++ ++ if ( conference == NULL || src_channel == NULL ) ++ return -1; ++ ++ res = 0; ++ ++ // acquire conference list mutex ++ ast_mutex_lock( &conflist_lock ) ; ++ ++ for ( conf = conflist ; conf != NULL ; conf = conf->next ) ++ { ++ if ( strcmp(conference, conf->name) == 0 ) ++ { ++ // acquire conference mutex ++ ast_mutex_lock( &conf->lock ); ++ ++ src = NULL; ++ dst = NULL; ++ for ( member = conf->memberlist ; member != NULL ; member = member->next ) ++ { ++ if ( strcmp(src_channel, member->channel_name) == 0 ) ++ src = member; ++ if ( dst_channel != NULL && strcmp(dst_channel, member->channel_name) == 0 ) ++ dst = member; ++ } ++ if ( src != NULL ) ++ { ++ // acquire member mutex ++ ast_mutex_lock(&src->lock); ++ ++ if ( dst != NULL ) ++ { ++ src->driven_member = dst; ++ // Make sure the driven member's speaker count is correct ++ if ( src->speaking_state == 1 ) ++ increment_speaker_count(src->driven_member, 1); ++ res = 1; ++ } else ++ { ++ if ( dst_channel == NULL ) ++ { ++ // Make sure the driven member's speaker count is correct ++ if ( src->speaking_state == 1 ) ++ decrement_speaker_count(src->driven_member, 1); ++ src->driven_member = NULL; ++ res = 1; ++ } ++ } ++ ++ // release member mutex ++ ast_mutex_unlock(&src->lock); ++ } ++ ++ // release conference mutex ++ ast_mutex_unlock( &conf->lock ); ++ ++ break; ++ } ++ } ++ ++ // release conference list mutex ++ ast_mutex_unlock( &conflist_lock ) ; ++ ++ return res; ++} ++ ++// Switches video source ++// Sends a manager event as well as ++// a text message notifying members of a video switch ++// The notification is sent to the current member and to the new member ++// The function locks the conference mutex as required ++void do_video_switching(struct ast_conference *conf, int new_id, int lock) ++{ ++ struct ast_conf_member *member; ++ struct ast_conf_member *new_member = NULL; ++ ++ if ( conf == NULL ) return; ++ ++ if ( lock ) ++ { ++ // acquire conference mutex ++ ast_mutex_lock( &conf->lock ); ++ } ++ ++ //fprintf(stderr, "Mihai: video switch from %d to %d\n", conf->current_video_source_id, new_id); ++ ++ // No need to do anything if the current member is the same as the new member ++ if ( new_id != conf->current_video_source_id ) ++ { ++ for ( member = conf->memberlist ; member != NULL ; member = member->next ) ++ { ++ if ( member->id == conf->current_video_source_id ) ++ { ++ send_text_message_to_member(member, AST_CONF_CONTROL_STOP_VIDEO); ++ } ++ if ( member->id == new_id ) ++ { ++ send_text_message_to_member(member, AST_CONF_CONTROL_START_VIDEO); ++ new_member = member; ++ } ++ } ++ ++ conf->current_video_source_id = new_id; ++ ++ if ( new_member != NULL ) ++ { ++ manager_event(EVENT_FLAG_CALL, ++ "ConferenceVideoSwitch", ++ "ConferenceName: %s\r\nChannel: %s\r\n", ++ conf->name, ++ new_member->channel_name); ++ } else ++ { ++ manager_event(EVENT_FLAG_CALL, ++ "ConferenceVideoSwitch", ++ "ConferenceName: %s\r\nChannel: empty\r\n", ++ conf->name); ++ } ++ } ++ ++ if ( lock ) ++ { ++ // release conference mutex ++ ast_mutex_unlock( &conf->lock ); ++ } ++} ++ ++int basic_play_sound(struct ast_conf_member *member, const char *file, int mute) ++{ ++ struct ast_conf_soundq *newsound; ++ struct ast_conf_soundq **q; ++ ++ newsound = calloc(1, sizeof(struct ast_conf_soundq)); ++ newsound->stream = ast_openstream(member->chan, file, NULL); ++ if( !newsound->stream ) ++ { ++ free(newsound); ++ ast_mutex_unlock(&member->lock); ++ return 0; ++ } ++ member->chan->stream = NULL; ++ ++ newsound->muted = mute; ++ ast_copy_string(newsound->name, file, sizeof(newsound->name)); ++ ++ // append sound to the end of the list. ++ for ( q=&member->soundq; *q; q = &((*q)->next) ) ; ++ *q = newsound; ++ ++ ast_mutex_unlock(&member->lock); ++ ++ return 1 ; ++} ++ ++int play_sound_channel(int fd, const char *channel, const char *file, int mute) ++{ ++ struct ast_conf_member *member; ++ ++ member = find_member(channel, 1); ++ if( !member ) ++ { ++ ast_cli(fd, "Member %s not found\n", channel); ++ return 0; ++ } ++ ++ if (! basic_play_sound(member, file, mute)) ++ { ++ ast_cli(fd, "Sound %s not found\n", file); ++ return 0; ++ } ++ ++ ast_cli(fd, "Playing sound %s to member %s %s\n", ++ file, channel, mute ? "with mute" : ""); ++ ++ return 1 ; ++} ++ ++int stop_sound_channel(int fd, const char *channel) ++{ ++ struct ast_conf_member *member; ++ struct ast_conf_soundq *sound; ++ struct ast_conf_soundq *next; ++ ++ member = find_member(channel, 1); ++ if ( !member ) ++ { ++ ast_cli(fd, "Member %s not found\n", channel); ++ return 0; ++ } ++ ++ // clear all sounds ++ sound = member->soundq; ++ member->soundq = NULL; ++ ++ while ( sound ) ++ { ++ next = sound->next; ++ ast_closestream(sound->stream); ++ free(sound); ++ sound = next; ++ } ++ ++ // reset write format, since we're done playing the sound ++ if ( ast_set_write_format( member->chan, member->write_format ) < 0 ) ++ { ++ ast_log( LOG_ERROR, "unable to set write format to %d\n", ++ member->write_format ) ; ++ } ++ ++ ast_mutex_unlock(&member->lock); ++ ++ ast_cli( fd, "Stopped sounds to member %s\n", channel); ++ return 1; ++} +diff --git a/apps/conference/conference.h b/apps/conference/conference.h +new file mode 100644 +index 0000000..0f7e248 +--- /dev/null ++++ b/apps/conference/conference.h +@@ -0,0 +1,194 @@ ++ ++// $Id: conference.h 884 2007-06-27 14:56:21Z sbalea $ ++ ++/* ++ * app_conference ++ * ++ * A channel independent conference application for Asterisk ++ * ++ * Copyright (C) 2002, 2003 Junghanns.NET GmbH ++ * Copyright (C) 2003, 2004 HorizonLive.com, Inc. ++ * Copyright (C) 2005, 2006 HorizonWimba, Inc. ++ * Copyright (C) 2007 Wimba, Inc. ++ * ++ * Klaus-Peter Junghanns ++ * ++ * Video Conferencing support added by ++ * Neil Stratford ++ * Copyright (C) 2005, 2005 Vipadia Limited ++ * ++ * VAD driven video conferencing, text message support ++ * and miscellaneous enhancements added by ++ * Mihai Balea ++ * ++ * This program may be modified and distributed under the ++ * terms of the GNU General Public License. You should have received ++ * a copy of the GNU General Public License along with this ++ * program; if not, write to the Free Software Foundation, Inc. ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _APP_CONF_CONFERENCE_H ++#define _APP_CONF_CONFERENCE_H ++ ++// ++// includes ++// ++ ++#include "app_conference.h" ++#include "common.h" ++ ++// ++// struct declarations ++// ++ ++typedef struct ast_conference_stats ++{ ++ // conference name ( copied for ease of use ) ++ char name[128] ; ++ ++ // type of connection ++ unsigned short phone ; ++ unsigned short iaxclient ; ++ unsigned short sip ; ++ ++ // type of users ++ unsigned short moderators ; ++ unsigned short listeners ; ++ ++ // accounting data ++ unsigned long frames_in ; ++ unsigned long frames_out ; ++ unsigned long frames_mixed ; ++ ++ struct timeval time_entered ; ++ ++} ast_conference_stats ; ++ ++ ++struct ast_conference ++{ ++ // conference name ++ char name[128] ; ++ ++ // single-linked list of members in conference ++ struct ast_conf_member* memberlist ; ++ int membercount ; ++ int id_count; ++ ++ // id of the default video source ++ // If nobody is talking and video is unlocked, we use this source ++ int default_video_source_id; ++ ++ // id of the current video source ++ // this changes according to VAD rules and lock requests ++ int current_video_source_id; ++ ++ // timestamp of when the current source has started talking ++ // TODO: do we really need this? ++ //struct timeval current_video_source_timestamp; ++ ++ // Video source locked flag, 1 -> locked, 0 -> unlocked ++ short video_locked; ++ ++ // conference thread id ++ pthread_t conference_thread ; ++ ++ // conference data mutex ++ ast_mutex_t lock ; ++ ++ // pointer to next conference in single-linked list ++ struct ast_conference* next ; ++ ++ // pointer to translation paths ++ struct ast_trans_pvt* from_slinear_paths[ AC_SUPPORTED_FORMATS ] ; ++ ++ // conference stats ++ ast_conference_stats stats ; ++ ++ // keep track of current delivery time ++ struct timeval delivery_time ; ++ ++ // 1 => on, 0 => off ++ short debug_flag ; ++} ; ++ ++ ++#include "member.h" ++ ++// ++// function declarations ++// ++ ++struct ast_conference* start_conference( struct ast_conf_member* member ) ; ++ ++void conference_exec( struct ast_conference* conf ) ; ++ ++struct ast_conference* find_conf( const char* name ) ; ++struct ast_conference* create_conf( char* name, struct ast_conf_member* member ) ; ++void remove_conf( struct ast_conference* conf ) ; ++int end_conference( const char *name, int hangup ) ; ++ ++// find a particular member, locking if requested. ++struct ast_conf_member *find_member ( const char *chan, int lock) ; ++ ++int queue_frame_for_listener( struct ast_conference* conf, struct ast_conf_member* member, conf_frame* frame ) ; ++int queue_frame_for_speaker( struct ast_conference* conf, struct ast_conf_member* member, conf_frame* frame ) ; ++int queue_silent_frame( struct ast_conference* conf, struct ast_conf_member* member ) ; ++ ++int get_new_id( struct ast_conference *conf ); ++void add_member( struct ast_conf_member* member, struct ast_conference* conf ) ; ++int remove_member( struct ast_conf_member* member, struct ast_conference* conf ) ; ++int count_member( struct ast_conf_member* member, struct ast_conference* conf, short add_member ) ; ++ ++void do_VAD_switching(struct ast_conference *conf); ++int send_text_message_to_member(struct ast_conf_member *member, const char *text); ++void do_video_switching(struct ast_conference *conf, int new_id, int lock); ++ ++// called by app_confernce.c:load_module() ++void init_conference( void ) ; ++ ++int get_conference_count( void ) ; ++ ++int show_conference_list ( int fd, const char* name ); ++int manager_conference_list( struct mansession *s, const struct message *m); ++int show_conference_stats ( int fd ); ++int kick_member ( const char* confname, int user_id); ++int kick_channel ( const char *confname, const char *channel); ++int kick_all ( void ); ++int mute_member ( const char* confname, int user_id); ++int unmute_member ( const char* confname, int user_id); ++int mute_channel ( const char* confname, const char* user_chan); ++int unmute_channel ( const char* confname, const char* user_chan); ++int viewstream_switch ( const char* confname, int user_id, int stream_id); ++int viewchannel_switch ( const char* confname, const char* user_chan, const char* stream_chan); ++ ++int get_conference_stats( ast_conference_stats* stats, int requested ) ; ++int get_conference_stats_by_name( ast_conference_stats* stats, const char* name ) ; ++ ++int lock_conference(const char *conference, int member_id); ++int lock_conference_channel(const char *conference, const char *channel); ++int unlock_conference(const char *conference); ++ ++int set_default_id(const char *conference, int member_id); ++int set_default_channel(const char *conference, const char *channel); ++ ++int video_mute_member(const char *conference, int member_id); ++int video_unmute_member(const char *conference, int member_id); ++int video_mute_channel(const char *conference, const char *channel); ++int video_unmute_channel(const char *conference, const char *channel); ++ ++int send_text(const char *conference, int member, const char *text); ++int send_text_channel(const char *conference, const char *channel, const char *text); ++int send_text_broadcast(const char *conference, const char *text); ++ ++int drive(const char *conference, int src_member_id, int dst_member_id); ++int drive_channel(const char *conference, const char *src_channel, const char *dst_channel); ++ ++int basic_play_sound(struct ast_conf_member *member, const char *file, int mute); ++int play_sound_channel(int fd, const char *channel, const char *file, int mute); ++int stop_sound_channel(int fd, const char *channel); ++ ++int set_conference_debugging( const char* name, int state ) ; ++ ++#endif +diff --git a/apps/conference/frame.c b/apps/conference/frame.c +new file mode 100644 +index 0000000..24e04bd +--- /dev/null ++++ b/apps/conference/frame.c +@@ -0,0 +1,679 @@ ++ ++// $Id: frame.c 751 2006-12-11 22:08:45Z sbalea $ ++ ++/* ++ * app_conference ++ * ++ * A channel independent conference application for Asterisk ++ * ++ * Copyright (C) 2002, 2003 Junghanns.NET GmbH ++ * Copyright (C) 2003, 2004 HorizonLive.com, Inc. ++ * Copyright (C) 2005, 2006 HorizonWimba, Inc. ++ * Copyright (C) 2007 Wimba, Inc. ++ * ++ * Klaus-Peter Junghanns ++ * ++ * Video Conferencing support added by ++ * Neil Stratford ++ * Copyright (C) 2005, 2005 Vipadia Limited ++ * ++ * VAD driven video conferencing, text message support ++ * and miscellaneous enhancements added by ++ * Mihai Balea ++ * ++ * This program may be modified and distributed under the ++ * terms of the GNU General Public License. You should have received ++ * a copy of the GNU General Public License along with this ++ * program; if not, write to the Free Software Foundation, Inc. ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include "asterisk/autoconfig.h" ++#include "frame.h" ++ ++conf_frame* mix_frames( conf_frame* frames_in, int speaker_count, int listener_count ) ++{ ++ if ( frames_in == NULL ) ++ return NULL ; ++ ++ conf_frame* frames_out = NULL ; ++ ++ if ( speaker_count > 1 ) ++ { ++ if ( speaker_count == 2 && listener_count == 0 ) ++ { ++ // optimize here also? ++ frames_out = mix_multiple_speakers( frames_in, speaker_count, listener_count ) ; ++ } ++ else ++ { ++ // mix spoken frames for sending ++ // ( note: this call also releases us from free'ing spoken_frames ) ++ frames_out = mix_multiple_speakers( frames_in, speaker_count, listener_count ) ; ++ } ++ } ++ else if ( speaker_count == 1 ) ++ { ++ // pass-through frames ++ frames_out = mix_single_speaker( frames_in ) ; ++ //printf("mix single speaker\n"); ++ } ++ else ++ { ++ // no frames to send, leave frames_out null ++ } ++ ++ return frames_out ; ++} ++ ++conf_frame* mix_single_speaker( conf_frame* frames_in ) ++{ ++#ifdef APP_CONFERENCE_DEBUG ++ // ast_log( AST_CONF_DEBUG, "returning single spoken frame\n" ) ; ++ ++ // ++ // check input ++ // ++ ++ if ( frames_in == NULL ) ++ { ++ ast_log( AST_CONF_DEBUG, "unable to mix single spoken frame with null frame\n" ) ; ++ return NULL ; ++ } ++ ++ if ( frames_in->fr == NULL ) ++ { ++ ast_log( AST_CONF_DEBUG, "unable to mix single spoken frame with null data\n" ) ; ++ return NULL ; ++ } ++ ++ if ( frames_in->member == NULL ) ++ { ++ ast_log( AST_CONF_DEBUG, "unable to mix single spoken frame with null member\n" ) ; ++ return NULL ; ++ } ++#endif // APP_CONFERENCE_DEBUG ++ ++ // ++ // 'mix' the frame ++ // ++ ++ // copy orignal frame to converted array so listeners don't need to re-encode it ++ frames_in->converted[ frames_in->member->read_format_index ] = ast_frdup( frames_in->fr ) ; ++ ++ // convert frame to slinear, if we have a path ++ frames_in->fr = convert_frame_to_slinear( ++ frames_in->member->to_slinear, ++ frames_in->fr ++ ) ; ++ ++ // set the frame's member to null ( i.e. all listeners ) ++ frames_in->member = NULL ; ++ ++ return frames_in ; ++} ++ ++// { ++ // ++ // a little optimization for testing only: ++ // when two speakers ( of the same type ) and no listeners ++ // are in a conference, we just swamp the frame's member pointers ++ // ++/* ++ if ( ++ listeners == 0 ++ && speakers == 2 ++ && cf_spokenFrames->member->read_format == cf_spokenFrames->next->member->write_format ++ && cf_spokenFrames->member->write_format == cf_spokenFrames->next->member->read_format ++ ) ++ { ++ struct ast_conf_member* m = NULL ; ++ m = cf_spokenFrames->member ; ++ cf_spokenFrames->member = cf_spokenFrames->next->member ; ++ cf_spokenFrames->next->member = m ; ++ return cf_spokenFrames ; ++ } ++*/ ++// } ++ ++void set_conf_frame_delivery( conf_frame* frame, struct timeval time ) ++{ ++ for ( ; frame != NULL ; frame = frame->next ) ++ { ++ if ( frame->fr != NULL ) ++ { ++ // copy passed timeval to frame's delivery timeval ++ frame->fr->delivery = time ; ++ } ++ } ++ ++ return ; ++} ++ ++conf_frame* mix_multiple_speakers( ++ conf_frame* frames_in, ++ int speakers, ++ int listeners ++) ++{ ++#ifdef APP_CONFERENCE_DEBUG ++ // ++ // check input ++ // ++ ++ // no frames to mix ++ if ( ( frames_in == NULL ) || ( frames_in->fr == NULL ) ) ++ { ++ ast_log( AST_CONF_DEBUG, "passed spoken frame list was NULL\n" ) ; ++ return NULL ; ++ } ++ ++ // if less than two speakers, then no frames to mix ++ if ( speakers < 2 ) ++ { ++ ast_log( AST_CONF_DEBUG, "mix_multiple_speakers() called with less than two speakers\n" ) ; ++ return NULL ; ++ } ++#endif // APP_CONFERENCE_DEBUG ++ ++ // ++ // at this point we know that there is more than one frame, ++ // and that the frames need to be converted to pcm to be mixed ++ // ++ // now, if there are only two frames and two members, ++ // we can swap them. ( but we'll get to that later. ) ++ // ++ ++ // ++ // loop through the spoken frames, making a list of spoken members, ++ // and converting gsm frames to slinear frames so we can mix them. ++ // ++ ++ // pointer to the spoken frames list ++ conf_frame* cf_spoken = frames_in ; ++ ++ // pointer to the new list of mixed frames ++ conf_frame* cf_sendFrames = NULL ; ++ ++ while ( cf_spoken != NULL ) ++ { ++ // ++ // while we're looping through the spoken frames, we'll ++ // convert the frame to a format suitable for mixing ++ // ++ // if the frame fails to convert, drop it and treat ++ // the speaking member like a listener by not adding ++ // them to the cf_sendFrames list ++ // ++ ++ if ( cf_spoken->member == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to determine frame member\n" ) ; ++ } ++ else ++ { ++ // ast_log( AST_CONF_DEBUG, "converting frame to slinear, channel => %s\n", cf_spoken->member->channel_name ) ; ++ cf_spoken->fr = convert_frame_to_slinear( ++ cf_spoken->member->to_slinear, ++ cf_spoken->fr ++ ) ; ++ ++ if ( cf_spoken->fr == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to convert frame to slinear\n" ) ; ++ } ++ else ++ { ++ // create new conf frame with last frame as 'next' ++ cf_sendFrames = create_conf_frame( cf_spoken->member, cf_sendFrames, NULL ) ; ++ } ++ } ++ ++ // point to the next spoken frame ++ cf_spoken = cf_spoken->next ; ++ } ++ ++ // if necessary, add a frame with a null member pointer. ++ // this frame will hold the audio mixed for all listeners ++ if ( listeners > 0 ) ++ { ++ cf_sendFrames = create_conf_frame( NULL, cf_sendFrames, NULL ) ; ++ } ++ ++ // ++ // mix the audio ++ // ++ ++ // convenience pointer that skips over the friendly offset ++ char* cp_listenerData ; ++ ++ // pointer to the send frames list ++ conf_frame* cf_send = NULL ; ++ ++ for ( cf_send = cf_sendFrames ; cf_send != NULL ; cf_send = cf_send->next ) ++ { ++ // allocate a mix buffer which fill large enough memory to ++ // hold a frame, and reset it's memory so we don't get noise ++ char* cp_listenerBuffer = malloc( AST_CONF_BUFFER_SIZE ) ; ++ memset( cp_listenerBuffer, 0x0, AST_CONF_BUFFER_SIZE ) ; ++ ++ // point past the friendly offset right to the data ++ cp_listenerData = cp_listenerBuffer + AST_FRIENDLY_OFFSET ; ++ ++ // reset the spoken list pointer ++ cf_spoken = frames_in ; ++ ++ // really mix the audio ++ for ( ; cf_spoken != NULL ; cf_spoken = cf_spoken->next ) ++ { ++ // ++ // if the members are equal, and they ++ // are not null, do not mix them. ++ // ++ if ( ++ ( cf_send->member == cf_spoken->member ) ++ && ( cf_send->member != NULL ) ++ ) ++ { ++ // don't mix this frame ++ } ++ else if ( cf_spoken->fr == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to mix conf_frame with null ast_frame\n" ) ; ++ } ++ else ++ { ++ // mix the new frame in with the existing buffer ++ mix_slinear_frames( cp_listenerData, (char*)( cf_spoken->fr->data ), AST_CONF_BLOCK_SAMPLES);//XXX NAS cf_spoken->fr->samples ) ; ++ } ++ } ++ ++ // copy a pointer to the frame data to the conf_frame ++ cf_send->mixed_buffer = cp_listenerData ; ++ } ++ ++ // ++ // copy the mixed buffer to a new frame ++ // ++ ++ // reset the send list pointer ++ cf_send = cf_sendFrames ; ++ ++ while ( cf_send != NULL ) ++ { ++ cf_send->fr = create_slinear_frame( cf_send->mixed_buffer ) ; ++ cf_send = cf_send->next ; ++ } ++ ++ // ++ // clean up the spoken frames we were passed ++ // ( caller will only be responsible for free'ing returns frames ) ++ // ++ ++ // reset the spoken list pointer ++ cf_spoken = frames_in ; ++ ++ while ( cf_spoken != NULL ) ++ { ++ // delete the frame ++ cf_spoken = delete_conf_frame( cf_spoken ) ; ++ } ++ ++ // return the list of frames for sending ++ return cf_sendFrames ; ++} ++ ++ ++struct ast_frame* convert_frame_to_slinear( struct ast_trans_pvt* trans, struct ast_frame* fr ) ++{ ++ // check for null frame ++ if ( fr == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to translate null frame to slinear\n" ) ; ++ return NULL ; ++ } ++ ++ // we don't need to duplicate this frame since ++ // the normal translation would free it anyway, so ++ // we'll just pretend we free'd and malloc'd a new one. ++ if ( fr->subclass == AST_FORMAT_SLINEAR ) ++ return fr ; ++ ++ // check for null translator ( after we've checked that we need to translate ) ++ if ( trans == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to translate frame with null translation path\n" ) ; ++ return fr ; ++ } ++ ++ // return the converted frame ++ return convert_frame( trans, fr ) ; ++} ++ ++struct ast_frame* convert_frame_from_slinear( struct ast_trans_pvt* trans, struct ast_frame* fr ) ++{ ++ // check for null translator ( after we've checked that we need to translate ) ++ if ( trans == NULL ) ++ { ++ //ast_log( LOG_ERROR, "unable to translate frame with null translation path\n" ) ; ++ return fr ; ++ } ++ ++ // check for null frame ++ if ( fr == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to translate null slinear frame\n" ) ; ++ return NULL ; ++ } ++ ++ // if the frame is not slinear, return an error ++ if ( fr->subclass != AST_FORMAT_SLINEAR ) ++ { ++ ast_log( LOG_ERROR, "unable to translate non-slinear frame\n" ) ; ++ return NULL ; ++ } ++ ++ // return the converted frame ++ return convert_frame( trans, fr ) ; ++} ++ ++struct ast_frame* convert_frame( struct ast_trans_pvt* trans, struct ast_frame* fr ) ++{ ++ if ( trans == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to convert frame with null translator\n" ) ; ++ return NULL ; ++ } ++ ++ if ( fr == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to convert null frame\n" ) ; ++ return NULL ; ++ } ++ ++ // convert the frame ++ struct ast_frame* translated_frame = ast_translate( trans, fr, 1 ) ; ++ ++ // check for errors ++ if ( translated_frame == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to translate frame\n" ) ; ++ return NULL ; ++ } ++ ++ // return the translated frame ++ return translated_frame ; ++} ++ ++conf_frame* delete_conf_frame( conf_frame* cf ) ++{ ++ int c; ++ // check for null frames ++ if ( cf == NULL ) ++ { ++ ast_log( AST_CONF_DEBUG, "unable to delete null conf frame\n" ) ; ++ return NULL ; ++ } ++ ++ // check for frame marked as static ++ if ( cf->static_frame == 1 ) ++ return NULL ; ++ ++ if ( cf->fr != NULL ) ++ { ++ ast_frfree( cf->fr ) ; ++ cf->fr = NULL ; ++ } ++ ++ // make sure converted frames are set to null ++ for ( c = 0 ; c < AC_SUPPORTED_FORMATS ; ++c ) ++ { ++ if ( cf->converted[ c ] != NULL ) ++ { ++ ast_frfree( cf->converted[ c ] ) ; ++ cf->converted[ c ] = NULL ; ++ } ++ } ++ ++ // get a pointer to the next frame ++ // in the list so we can return it ++ conf_frame* nf = cf->next ; ++ ++ free( cf ) ; ++ cf = NULL ; ++ ++ return nf ; ++} ++ ++conf_frame* create_conf_frame( struct ast_conf_member* member, conf_frame* next, const struct ast_frame* fr ) ++{ ++ // pointer to list of mixed frames ++ conf_frame* cf = malloc( sizeof( struct conf_frame ) ) ; ++ ++ if ( cf == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to allocate memory for conf frame\n" ) ; ++ return NULL ; ++ } ++ ++ // ++ // init with some defaults ++ // ++ ++ // make sure converted frames are set to null ++// for ( int c = 0 ; c < AC_SUPPORTED_FORMATS ; ++c ) ++// { ++// cf->converted[ c ] = NULL ; ++// } ++ ++ memset( (struct ast_frame*)( cf->converted ), 0x0, ( sizeof( struct ast_frame* ) * AC_SUPPORTED_FORMATS ) ) ; ++ ++ cf->member = member ; ++ // cf->priority = 0 ; ++ ++ cf->prev = NULL ; ++ cf->next = next ; ++ ++ cf->static_frame = 0 ; ++ ++ // establish relationship to 'next' ++ if ( next != NULL ) next->prev = cf ; ++ ++ // this holds the ast_frame pointer ++ cf->fr = ( fr == NULL ) ? NULL : ast_frdup( ( struct ast_frame* )( fr ) ) ; ++ ++ // this holds the temporu mix buffer ++ cf->mixed_buffer = NULL ; ++ ++ return cf ; ++} ++ ++conf_frame* copy_conf_frame( conf_frame* src ) ++{ ++ // ++ // check inputs ++ // ++ ++ if ( src == NULL ) ++ { ++ ast_log( AST_CONF_DEBUG, "unable to copy null conf frame\n" ) ; ++ return NULL ; ++ } ++ ++ // ++ // copy the frame ++ // ++ ++ struct conf_frame *cfr = NULL ; ++ ++ // create a new conf frame ++ cfr = create_conf_frame( src->member, NULL, src->fr ) ; ++ ++ if ( cfr == NULL ) ++ { ++ ast_log( AST_CONF_DEBUG, "unable to create new conf frame for copy\n" ) ; ++ return NULL ; ++ } ++ ++ return cfr ; ++} ++ ++// ++// Create a TEXT frame based on a given string ++// ++struct ast_frame* create_text_frame(const char *text, int copy) ++{ ++ struct ast_frame *f; ++ char *t; ++ ++ f = calloc(1, sizeof(struct ast_frame)); ++ if ( f == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to allocate memory for text frame\n" ) ; ++ return NULL ; ++ } ++ if ( copy ) ++ { ++ t = calloc(strlen(text) + 1, 1); ++ if ( t == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to allocate memory for text data\n" ) ; ++ free(f); ++ return NULL ; ++ } ++ strncpy(t, text, strlen(text)); ++ } else ++ { ++ t = (char *)text; ++ } ++ ++ f->frametype = AST_FRAME_TEXT; ++ f->offset = 0; ++ f->mallocd = AST_MALLOCD_HDR; ++ if ( copy ) f->mallocd |= AST_MALLOCD_DATA; ++ f->datalen = strlen(t) + 1; ++ f->data = t; ++ f->src = NULL; ++ ++ return f; ++} ++ ++// ++// slinear frame functions ++// ++ ++struct ast_frame* create_slinear_frame( char* data ) ++{ ++ struct ast_frame* f ; ++ ++ f = calloc( 1, sizeof( struct ast_frame ) ) ; ++ if ( f == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to allocate memory for slinear frame\n" ) ; ++ return NULL ; ++ } ++ ++ f->frametype = AST_FRAME_VOICE ; ++ f->subclass = AST_FORMAT_SLINEAR ; ++ f->samples = AST_CONF_BLOCK_SAMPLES ; ++ f->offset = AST_FRIENDLY_OFFSET ; ++ f->mallocd = AST_MALLOCD_HDR | AST_MALLOCD_DATA ; ++ ++ f->datalen = AST_CONF_FRAME_DATA_SIZE ; ++ f->data = data ; ++ ++ f->src = NULL ; ++ ++ return f ; ++} ++ ++void mix_slinear_frames( char *dst, const char *src, int samples ) ++{ ++ if ( dst == NULL ) return ; ++ if ( src == NULL ) return ; ++ ++ int i, val ; ++ ++ for ( i = 0 ; i < samples ; ++i ) ++ { ++ val = ( (short*)dst )[i] + ( (short*)src )[i] ; ++ ++ if ( val > 0x7fff ) ++ { ++ ( (short*)dst )[i] = 0x7fff - 1 ; ++ continue ; ++ } ++ else if ( val < -0x7fff ) ++ { ++ ( (short*)dst )[i] = -0x7fff + 1 ; ++ continue ; ++ } ++ else ++ { ++ ( (short*)dst )[i] = val ; ++ continue ; ++ } ++ } ++ ++ return ; ++} ++ ++// ++// silent frame functions ++// ++ ++conf_frame* get_silent_frame( void ) ++{ ++ static conf_frame* static_silent_frame = NULL ; ++ ++ // we'll let this leak until the application terminates ++ if ( static_silent_frame == NULL ) ++ { ++ // ast_log( AST_CONF_DEBUG, "creating cached silent frame\n" ) ; ++ struct ast_frame* fr = get_silent_slinear_frame() ; ++ ++ static_silent_frame = create_conf_frame( NULL, NULL, fr ) ; ++ ++ if ( static_silent_frame == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to create cached silent frame\n" ) ; ++ return NULL ; ++ } ++ ++ // init the 'converted' slinear silent frame ++ static_silent_frame->converted[ AC_SLINEAR_INDEX ] = get_silent_slinear_frame() ; ++ ++ // mark frame as static so it's not deleted ++ static_silent_frame->static_frame = 1 ; ++ } ++ ++ return static_silent_frame ; ++} ++ ++struct ast_frame* get_silent_slinear_frame( void ) ++{ ++ static struct ast_frame* f = NULL ; ++ ++ // we'll let this leak until the application terminates ++ if ( f == NULL ) ++ { ++ char* data = malloc( AST_CONF_BUFFER_SIZE ) ; ++ memset( data, 0x0, AST_CONF_BUFFER_SIZE ) ; ++ f = create_slinear_frame( data ) ; ++ } ++ ++ return f; ++} ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/apps/conference/frame.h b/apps/conference/frame.h +new file mode 100644 +index 0000000..2b77805 +--- /dev/null ++++ b/apps/conference/frame.h +@@ -0,0 +1,75 @@ ++ ++// $Id: frame.h 746 2006-12-11 20:12:12Z sbalea $ ++ ++/* ++ * app_conference ++ * ++ * A channel independent conference application for Asterisk ++ * ++ * Copyright (C) 2002, 2003 Junghanns.NET GmbH ++ * Copyright (C) 2003, 2004 HorizonLive.com, Inc. ++ * Copyright (C) 2005, 2006 HorizonWimba, Inc. ++ * Copyright (C) 2007 Wimba, Inc. ++ * ++ * Klaus-Peter Junghanns ++ * ++ * Video Conferencing support added by ++ * Neil Stratford ++ * Copyright (C) 2005, 2005 Vipadia Limited ++ * ++ * VAD driven video conferencing, text message support ++ * and miscellaneous enhancements added by ++ * Mihai Balea ++ * ++ * This program may be modified and distributed under the ++ * terms of the GNU General Public License. You should have received ++ * a copy of the GNU General Public License along with this ++ * program; if not, write to the Free Software Foundation, Inc. ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _APP_CONF_FRAME_H ++#define _APP_CONF_FRAME_H ++ ++// ++// includes ++// ++ ++#include "app_conference.h" ++#include "common.h" ++ ++// ++// function declarations ++// ++ ++// mixing ++conf_frame* mix_frames( conf_frame* frames_in, int speaker_count, int listener_count ) ; ++ ++conf_frame* mix_multiple_speakers( conf_frame* frames_in, int speakers, int listeners ) ; ++conf_frame* mix_single_speaker( conf_frame* frames_in ) ; ++ ++// frame creation and deletion ++conf_frame* create_conf_frame( struct ast_conf_member* member, conf_frame* next, const struct ast_frame* fr ) ; ++conf_frame* delete_conf_frame( conf_frame* cf ) ; ++conf_frame* copy_conf_frame( conf_frame* src ) ; ++ ++// convert frame functions ++struct ast_frame* convert_frame_to_slinear( struct ast_trans_pvt* trans, struct ast_frame* fr ) ; ++struct ast_frame* convert_frame_from_slinear( struct ast_trans_pvt* trans, struct ast_frame* fr ) ; ++struct ast_frame* convert_frame( struct ast_trans_pvt* trans, struct ast_frame* fr ) ; ++ ++// text frame function(s) ++struct ast_frame* create_text_frame(const char *text, int copy); ++ ++// slinear frame functions ++struct ast_frame* create_slinear_frame( char* data ) ; ++void mix_slinear_frames( char* dst, const char* src, int samples ) ; ++ ++// silent frame functions ++conf_frame* get_silent_frame( void ) ; ++struct ast_frame* get_silent_slinear_frame( void ) ; ++ ++// set delivery timestamp for frames ++void set_conf_frame_delivery( conf_frame* frame, struct timeval time ) ; ++ ++#endif +diff --git a/apps/conference/member.c b/apps/conference/member.c +new file mode 100644 +index 0000000..d7f98af +--- /dev/null ++++ b/apps/conference/member.c +@@ -0,0 +1,3332 @@ ++ ++// $Id: member.c 885 2007-06-27 15:41:18Z sbalea $ ++ ++/* ++ * app_conference ++ * ++ * A channel independent conference application for Asterisk ++ * ++ * Copyright (C) 2002, 2003 Junghanns.NET GmbH ++ * Copyright (C) 2003, 2004 HorizonLive.com, Inc. ++ * Copyright (C) 2005, 2006 HorizonWimba, Inc. ++ * Copyright (C) 2007 Wimba, Inc. ++ * ++ * Klaus-Peter Junghanns ++ * ++ * Video Conferencing support added by ++ * Neil Stratford ++ * Copyright (C) 2005, 2005 Vipadia Limited ++ * ++ * VAD driven video conferencing, text message support ++ * and miscellaneous enhancements added by ++ * Mihai Balea ++ * ++ * This program may be modified and distributed under the ++ * terms of the GNU General Public License. You should have received ++ * a copy of the GNU General Public License along with this ++ * program; if not, write to the Free Software Foundation, Inc. ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include "asterisk/channel.h" ++#include "asterisk/app.h" ++#include "asterisk/autoconfig.h" ++#include "member.h" ++ ++ ++// process an incoming frame. Returns 0 normally, 1 if hangup was received. ++static int process_incoming(struct ast_conf_member *member, struct ast_conference *conf, struct ast_frame *f) ++{ ++ int silent_frame = 0; ++ struct ast_conf_member *src_member ; ++ ++ // In Asterisk 1.4 AST_FRAME_DTMF is equivalent to AST_FRAME_DTMF_END ++ if (f->frametype == AST_FRAME_DTMF) ++ { ++ if (member->dtmf_switch) ++ { ++ ast_mutex_lock( &member->lock ) ; ++ switch (f->subclass) { ++ case '0' :member->req_id=0; ++ break; ++ case '1' :member->req_id=1; ++ break; ++ case '2' :member->req_id=2; ++ break; ++ case '3' :member->req_id=3; ++ break; ++ case '4' :member->req_id=4; ++ break; ++ case '5' :member->req_id=5; ++ break; ++ case '6' :member->req_id=6; ++ break; ++ case '7' :member->req_id=7; ++ break; ++ case '8' :member->req_id=8; ++ break; ++ case '9' :member->req_id=9; ++ break; ++ case '*' : ++ if (member->mute_video == 0 && member->mute_audio == 0) ++ { ++ member->mute_video = 1; ++ member->mute_audio = 1; ++ } ++ else if (member->mute_video == 1 && member->mute_audio == 1) ++ { ++ member->mute_video = 0; ++ member->mute_audio = 0; ++ } ++ break; ++ } ++ member->conference = 1; // switch me ++ ast_mutex_unlock( &member->lock ) ; ++ } ++ if (member->dtmf_relay) ++ { ++ // output to manager... ++ manager_event( ++ EVENT_FLAG_CALL, ++ "ConferenceDTMF", ++ "ConferenceName: %s\r\n" ++ "Channel: %s\r\n" ++ "CallerID: %s\r\n" ++ "CallerIDName: %s\r\n" ++ "Key: %c\r\n", ++ conf->name, ++ member->channel_name, ++ member->chan->cid.cid_num ? member->chan->cid.cid_num : "unknown", ++ member->chan->cid.cid_name ? member->chan->cid.cid_name : "unknown", ++ f->subclass ++ ) ; ++ ++ } ++ if (!member->dtmf_switch && !member->dtmf_relay) ++ { ++ // relay this to the listening channels ++ queue_incoming_dtmf_frame( member, f ); ++ } ++ } else if (f->frametype == AST_FRAME_DTMF_BEGIN) ++ { ++ if (!member->dtmf_switch && !member->dtmf_relay) ++ { ++ // relay this to the listening channels ++ queue_incoming_dtmf_frame( member, f ); ++ } ++ } ++ ++ ast_mutex_lock( &member->lock ) ; ++ // Handle a local or remote conference ++ if (member->conference) ++ { ++ int req_id = member->req_id; ++ ast_mutex_unlock( &member->lock ); ++ // this will return NULL or a locked member ++ src_member = check_active_video(req_id,conf); ++ // Stream a picture to the recipient if no active video ++ if (!src_member) ++ { ++ // Mihai: we don't want to send video here, we cannot negotiate codec ++ // and we don't know what codec the conference is using ++ //if (member->norecv_video == 0) ++ //{ ++ // if(!ast_streamfile(member->chan,"novideo",member->chan->language)) ++ // { ++ // ast_waitstream(member->chan,""); ++ // } ++ //} ++ } ++ else ++ { ++ // Send a FIR to the new sender ++ ast_indicate(src_member->chan,AST_CONTROL_VIDUPDATE); ++ // we will have locked in check_active_video() ++ ast_mutex_unlock( &src_member->lock); ++ } ++ ast_mutex_lock( &member->lock ); ++ member->conference = 0; ++ } ++ ast_mutex_unlock( &member->lock ); ++ ++ ++ if ((f->frametype == AST_FRAME_VOICE && member->mute_audio == 1) || (f->frametype == AST_FRAME_VIDEO && member->mute_video == 1)) ++ { ++ // this is a listen-only user, ignore the frame ++ //ast_log( AST_CONF_DEBUG, "Listen only user frame"); ++ ast_frfree( f ) ; ++ f = NULL ; ++ } ++ else if ( f->frametype == AST_FRAME_VOICE ) ++ { //ast_log( AST_CONF_DEBUG, "Got voice frame"); ++ // reset silence detection flag ++ silent_frame = 0 ; ++ ++ // accounting: count the incoming frame ++ member->frames_in++ ; ++ ++#if ( SILDET == 2 ) ++ // ++ // make sure we have a valid dsp and frame type ++ // ++ if ( ++ member->dsp != NULL ++ && f->subclass == AST_FORMAT_SLINEAR ++ && f->datalen == AST_CONF_FRAME_DATA_SIZE ++ ) ++ { ++ // send the frame to the preprocessor ++ int spx_ret; ++ spx_ret = speex_preprocess( member->dsp, f->data, NULL ); ++#ifdef DEBUG_USE_TIMELOG ++ TIMELOG(spx_ret, 3, "speex_preprocess"); ++#endif ++ if ( spx_ret == 0 ) ++ { ++ // ++ // we ignore the preprocessor's outcome if we've seen voice frames ++ // in within the last AST_CONF_SKIP_SPEEX_PREPROCESS frames ++ // ++ if ( member->ignore_speex_count > 0 ) ++ { ++ // ast_log( AST_CONF_DEBUG, "ignore_speex_count => %d\n", ignore_speex_count ) ; ++ ++ // skip speex_preprocess(), and decrement counter ++ --member->ignore_speex_count ; ++ } ++ else ++ { ++ // set silent_frame flag ++ silent_frame = 1 ; ++ } ++ } ++ else ++ { ++ // voice detected, reset skip count ++ member->ignore_speex_count = AST_CONF_SKIP_SPEEX_PREPROCESS ; ++ } ++ } ++#endif ++ if ( !silent_frame ) ++ queue_incoming_frame( member, f ); ++ ++ // free the original frame ++ ast_frfree( f ) ; ++ f = NULL ; ++ ++ } ++ else if (f->frametype == AST_FRAME_VIDEO) ++ { ++ queue_incoming_video_frame( member, f ); ++ ++ // free the original frame ++ ast_frfree( f ) ; ++ f = NULL ; ++ ++ } ++ else if ( ++ f->frametype == AST_FRAME_CONTROL ++ && f->subclass == AST_CONTROL_HANGUP ++ ) ++ { ++ // hangup received ++ ++ // free the frame ++ ast_frfree( f ) ; ++ f = NULL ; ++ ++ // break out of the while ( 42 == 42 ) ++ return 1; ++ } ++ else if ( ++ f->frametype == AST_FRAME_CONTROL ++ && f->subclass == AST_CONTROL_VIDUPDATE ++ ) ++ { ++ // say we have switched to cause a FIR to ++ // be sent to the sender ++ ast_mutex_lock( &member->lock ) ; ++ member->conference = 1; ++ ast_mutex_unlock( &member->lock ) ; ++ ++ // free the original frame ++ ast_frfree( f ) ; ++ f = NULL ; ++ } ++ else if ( f->frametype == AST_FRAME_TEXT && member->does_text ) ++ { ++ if ( strncmp(f->data, AST_CONF_CONTROL_CAMERA_DISABLED, strlen(AST_CONF_CONTROL_CAMERA_DISABLED)) == 0 ) ++ { ++ ast_mutex_lock(&member->lock); ++ manager_event(EVENT_FLAG_CALL, ++ "ConferenceCameraDisabled", ++ "ConferenceName: %s\r\nChannel: %s\r\n", ++ conf->name, ++ member->channel_name); ++ member->no_camera = 1; ++ ast_mutex_unlock(&member->lock); ++ } else if ( strncmp(f->data, AST_CONF_CONTROL_CAMERA_ENABLED, strlen(AST_CONF_CONTROL_CAMERA_ENABLED)) == 0 ) ++ { ++ ast_mutex_lock(&member->lock); ++ manager_event(EVENT_FLAG_CALL, ++ "ConferenceCameraEnabled", ++ "ConferenceName: %s\r\nChannel: %s\r\n", ++ conf->name, ++ member->channel_name); ++ member->no_camera = 0; ++ ast_mutex_unlock(&member->lock); ++ } else if ( strncmp(f->data, AST_CONF_CONTROL_STOP_VIDEO_TRANSMIT, strlen(AST_CONF_CONTROL_STOP_VIDEO_TRANSMIT)) == 0 ) ++ { ++ ast_mutex_lock(&member->lock); ++ manager_event(EVENT_FLAG_CALL, ++ "ConferenceStopVideoTransmit", ++ "ConferenceName: %s\r\nChannel: %s\r\n", ++ conf->name, ++ member->channel_name); ++ member->norecv_video = 1; ++ ast_mutex_unlock(&member->lock); ++ } else if ( strncmp(f->data, AST_CONF_CONTROL_START_VIDEO_TRANSMIT, strlen(AST_CONF_CONTROL_START_VIDEO_TRANSMIT)) == 0 ) ++ { ++ ast_mutex_lock(&member->lock); ++ manager_event(EVENT_FLAG_CALL, ++ "ConferenceStartVideoTransmit", ++ "ConferenceName: %s\r\nChannel: %s\r\n", ++ conf->name, ++ member->channel_name); ++ member->norecv_video = 0; ++ ast_mutex_unlock(&member->lock); ++ } ++ ast_frfree(f); ++ f = NULL; ++ } else ++ { ++ // undesirables ++ ast_frfree( f ) ; ++ f = NULL ; ++ } ++ ++ return 0; ++} ++ ++// get the next frame from the soundq; must be called with member locked. ++static struct ast_frame *get_next_soundframe(struct ast_conf_member *member, struct ast_frame ++ *exampleframe) { ++ struct ast_frame *f; ++ ++again: ++ f=ast_readframe(member->soundq->stream); ++ ++ if(!f) { // we're done with this sound; remove it from the queue, and try again ++ struct ast_conf_soundq *toboot = member->soundq; ++ ++ ast_closestream(toboot->stream); ++ member->soundq = toboot->next; ++ ++ //ast_log( LOG_WARNING, "finished playing a sound, next = %x\n", member->soundq); ++ // notify applications via mgr interface that this sound has been played ++ manager_event( ++ EVENT_FLAG_CALL, ++ "ConferenceSoundComplete", ++ "Channel: %s\r\n" ++ "Sound: %s\r\n", ++ member->channel_name, ++ toboot->name ++ ); ++ ++ free(toboot); ++ if(member->soundq) goto again; ++ ++ // if we get here, we've gotten to the end of the queue; reset write format ++ if ( ast_set_write_format( member->chan, member->write_format ) < 0 ) ++ { ++ ast_log( LOG_ERROR, "unable to set write format to %d\n", ++ member->write_format ) ; ++ } ++ } else { ++ // copy delivery from exampleframe ++ f->delivery = exampleframe->delivery; ++ } ++ ++ return f; ++} ++ ++ ++// process outgoing frames for the channel, playing either normal conference audio, ++// or requested sounds ++static int process_outgoing(struct ast_conf_member *member) ++{ ++ conf_frame* cf ; // frame read from the output queue ++ struct ast_frame *f; ++ struct ast_frame *realframe = NULL; ++ ++ for(;;) ++ { ++ // acquire member mutex and grab a frame. ++ ast_mutex_lock( &member->lock ) ; ++ cf = get_outgoing_frame( member ) ; ++ ++ // if there's no frames exit the loop. ++ if ( !cf ) ++ { ++ ast_mutex_unlock( &member->lock ) ; ++ break; ++ } ++ ++ ++ f = cf->fr; ++ ++ // if we're playing sounds, we can just replace the frame with the ++ // next sound frame, and send it instead ++ if ( member->soundq ) ++ { ++ realframe = f; ++ f = get_next_soundframe(member, f); ++ if ( !f ) ++ { ++ // if we didn't get anything, just revert to "normal" ++ f = realframe; ++ realframe = NULL; ++ } else ++ { ++ // We have a sound frame now, but we need to make sure it's the same ++ // format as our channel write format ++ int wf = member->chan->writeformat & AST_FORMAT_AUDIO_MASK; ++ if ( f->frametype == AST_FRAME_VOICE && !(wf & f->subclass) ) ++ { ++ // We need to change our channel's write format ++ ast_set_write_format(member->chan, f->subclass); ++ } ++ } ++ } ++ ++ ast_mutex_unlock(&member->lock); ++ ++ ++#ifdef DEBUG_FRAME_TIMESTAMPS ++ // !!! TESTING !!! ++ int delivery_diff = usecdiff( &f->delivery, &member->lastsent_timeval ) ; ++ if ( delivery_diff != AST_CONF_FRAME_INTERVAL ) ++ { ++ ast_log( AST_CONF_DEBUG, "unanticipated delivery time, delivery_diff => %d, delivery.tv_usec => %ld\n", ++ delivery_diff, f->delivery.tv_usec ) ; ++ } ++ ++ // !!! TESTING !!! ++ if ( ++ f->delivery.tv_sec < member->lastsent_timeval.tv_sec ++ || ( ++ f->delivery.tv_sec == member->lastsent_timeval.tv_sec ++ && f->delivery.tv_usec <= member->lastsent_timeval.tv_usec ++ ) ++ ) ++ { ++ ast_log( LOG_WARNING, "queued frame timestamped in the past, %ld.%ld <= %ld.%ld\n", ++ f->delivery.tv_sec, f->delivery.tv_usec, ++ member->lastsent_timeval.tv_sec, member->lastsent_timeval.tv_usec ) ; ++ } ++ member->lastsent_timeval = f->delivery ; ++#endif ++ ++#ifdef DEBUG_USE_TIMELOG ++ TIMELOG( ast_write( member->chan, f ), 10, "member: ast_write"); ++#else ++ ++ // send the voice frame ++ if ( ast_write( member->chan, f ) == 0 ) ++ { ++ struct timeval tv = ast_tvnow(); ++ ast_log( AST_CONF_DEBUG, "SENT VOICE FRAME, channel => %s, frames_out => %ld, s => %ld, ms => %ld\n", ++ member->channel_name, member->frames_out, tv.tv_sec, tv.tv_usec ) ; ++ } ++ else ++ { ++ // log 'dropped' outgoing frame ++ ast_log( LOG_ERROR, "unable to write voice frame to channel, channel => %s\n", member->channel_name ) ; ++ ++ // accounting: count dropped outgoing frames ++ member->frames_out_dropped++ ; ++ } ++#endif ++ // clean up frame ++ delete_conf_frame( cf ) ; ++ ++ } ++ ++ // Do the same for video, suck it dry ++ for(;;) ++ { ++ // grab a frame. ++ cf = get_outgoing_video_frame( member ) ; ++ ++ // if there's no frames exit the loop. ++ if(!cf){ ++ break; ++ } ++ ++ f = cf->fr; ++ ++ // send the video frame ++ if ( ast_write_video( member->chan, f ) == 1 ) ++ { ++ struct timeval tv = ast_tvnow(); ++ ast_log( AST_CONF_DEBUG, "SENT VIDEO FRAME, channel => %s, frames_out => %ld, s => %ld, ms => %ld\n", ++ member->channel_name, member->frames_out, tv.tv_sec, tv.tv_usec ) ; ++ } ++ else ++ { ++ // log 'dropped' outgoing frame ++ ast_log( AST_CONF_DEBUG, "unable to write video frame to channel, channel => %s\n", member->channel_name ) ; ++ ++ // accounting: count dropped outgoing frames ++ member->video_frames_out_dropped++ ; ++ } ++ ++ // clean up frame ++ delete_conf_frame( cf ) ; ++ ++ } ++ ++ // Do the same for dtmf, suck it dry ++ for(;;) ++ { ++ // acquire member mutex and grab a frame. ++ cf = get_outgoing_dtmf_frame( member ) ; ++ ++ // if there's no frames exit the loop. ++ if(!cf) break; ++ ++ // send the dtmf frame ++ if ( ast_write( member->chan, cf->fr ) == 0 ) ++ { ++ struct timeval tv = ast_tvnow(); ++ ast_log( AST_CONF_DEBUG, "SENT DTMF FRAME, channel => %s, frames_out => %ld, s => %ld, ms => %ld\n", ++ member->channel_name, member->frames_out, tv.tv_sec, tv.tv_usec ) ; ++ ++ } ++ else ++ { ++ // log 'dropped' outgoing frame ++ ast_log( AST_CONF_DEBUG, "unable to write dtmf frame to channel, channel => %s\n", member->channel_name ) ; ++ ++ // accounting: count dropped outgoing frames ++ member->dtmf_frames_out_dropped++ ; ++ } ++ ++ // clean up frame ++ delete_conf_frame( cf ) ; ++ } ++ ++ // Do the same for text, hell, why not? ++ for(;;) ++ { ++ // acquire member mutex and grab a frame. ++ cf = get_outgoing_text_frame( member ) ; ++ ++ // if there's no frames exit the loop. ++ if(!cf) break; ++ ++ // send the text frame ++ if ( ast_write( member->chan, cf->fr ) == 0 ) ++ { ++ struct timeval tv = ast_tvnow(); ++ ast_log( AST_CONF_DEBUG, "SENT TEXT FRAME, channel => %s, frames_out => %ld, s => %ld, ms => %ld\n", ++ member->channel_name, member->frames_out, tv.tv_sec, tv.tv_usec ) ; ++ ++ } ++ else ++ { ++ // log 'dropped' outgoing frame ++ ast_log( AST_CONF_DEBUG, "unable to write text frame to channel, channel => %s\n", member->channel_name ) ; ++ ++ // accounting: count dropped outgoing frames ++ member->text_frames_out_dropped++ ; ++ } ++ ++ // clean up frame ++ delete_conf_frame( cf ) ; ++ } ++ ++ ++ return 0; ++} ++ ++static int member_checkkick( struct ast_conf_member *member ) ++{ ++ int kick; ++ ast_mutex_lock( &member->lock ) ; ++ kick = member->kick_flag; ++ ast_mutex_unlock( &member->lock ) ; ++ return kick; ++} ++ ++// ++// main member thread function ++// ++ ++int member_exec( struct ast_channel* chan, void* data ) ++{ ++// struct timeval start, end ; ++// start = ast_tvnow(); ++ ++ struct ast_conference *conf ; ++ struct ast_conf_member *member ; ++ ++ struct ast_frame *f ; // frame received from ast_read() ++ ++ int left = 0 ; ++ int res; ++ ++ ast_log( AST_CONF_DEBUG, "Begin processing member thread, channel => %s\n", chan->name ) ; ++ ++ // ++ // If the call has not yet been answered, answer the call ++ // Note: asterisk apps seem to check _state, but it seems like it's safe ++ // to just call ast_answer. It will just do nothing if it is up. ++ // it will also return -1 if the channel is a zombie, or has hung up. ++ // ++ ++ res = ast_answer( chan ) ; ++ if ( res ) ++ { ++ ast_log( LOG_ERROR, "unable to answer call\n" ) ; ++ return -1 ; ++ } ++ ++ // ++ // create a new member for the conference ++ // ++ ++// ast_log( AST_CONF_DEBUG, "creating new member, id => %s, flags => %s, p => %s\n", ++// id, flags, priority ) ; ++ ++ member = create_member( chan, (const char*)( data ) ) ; // flags, atoi( priority ) ) ; ++ ++ // unable to create member, return an error ++ if ( member == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to create member\n" ) ; ++ return -1 ; ++ } ++ ++ // ++ // setup asterisk read/write formats ++ // ++#if 0 ++ ast_log( AST_CONF_DEBUG, "CHANNEL INFO, CHANNEL => %s, DNID => %s, CALLER_ID => %s, ANI => %s\n", ++ chan->name, chan->dnid, chan->callerid, chan->ani ) ; ++ ++ ast_log( AST_CONF_DEBUG, "CHANNEL CODECS, CHANNEL => %s, NATIVE => %d, READ => %d, WRITE => %d\n", ++ chan->name, chan->nativeformats, member->read_format, member->write_format ) ; ++#endif ++ if ( ast_set_read_format( chan, member->read_format ) < 0 ) ++ { ++ ast_log( LOG_ERROR, "unable to set read format to signed linear\n" ) ; ++ delete_member( member ) ; ++ return -1 ; ++ } ++ ++ if ( ast_set_write_format( chan, member->write_format ) < 0 ) // AST_FORMAT_SLINEAR, chan->nativeformats ++ { ++ ast_log( LOG_ERROR, "unable to set write format to signed linear\n" ) ; ++ delete_member( member ) ; ++ return -1 ; ++ } ++ ++ // ++ // setup a conference for the new member ++ // ++ ++ conf = start_conference( member ) ; ++ ++ if ( conf == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to setup member conference\n" ) ; ++ delete_member( member) ; ++ return -1 ; ++ } ++ ++ ++ manager_event( ++ EVENT_FLAG_CALL, ++ "ConferenceJoin", ++ "ConferenceName: %s\r\n" ++ "Member: %d\r\n" ++ "Channel: %s\r\n" ++ "CallerID: %s\r\n" ++ "CallerIDName: %s\r\n" ++ "Count: %d\r\n", ++ conf->name, ++ member->id, ++ member->channel_name, ++ member->chan->cid.cid_num ? member->chan->cid.cid_num : "unknown", ++ member->chan->cid.cid_name ? member->chan->cid.cid_name: "unknown", ++ conf->membercount ++ ) ; ++ ++ // Store the CID information ++ if ( member->chan->cid.cid_num ) ++ { ++ if ( (member->callerid = malloc(strlen(member->chan->cid.cid_num)+1)) ) ++ memcpy(member->callerid,member->chan->cid.cid_num, strlen(member->chan->cid.cid_num)+1); ++ } else ++ member->callerid = NULL; ++ ++ if ( member->chan->cid.cid_name ) ++ { ++ if ( (member->callername = malloc(strlen(member->chan->cid.cid_name)+1)) ) ++ memcpy(member->callername, member->chan->cid.cid_name, strlen(member->chan->cid.cid_name)+1); ++ } else ++ member->callername = NULL; ++ ++ ++ // ++ // process loop for new member ( this runs in it's own thread ) ++ // ++ ++ ast_log( AST_CONF_DEBUG, "begin member event loop, channel => %s\n", chan->name ) ; ++ ++ // timer timestamps ++ struct timeval base, curr ; ++ base = ast_tvnow(); ++ ++ // tell conference_exec we're ready for frames ++ member->ready_for_outgoing = 1 ; ++ ++ // notify others about enter ++ char * enter_snd = member->enter_snd; ++ if (strcmp(enter_snd, "-")!=0) ++ { ++ // lock conference ++ ast_mutex_lock( &conf->lock ); ++ ++ /*if (conf->membercount < 2) ++ { ++ enter_snd = "conf-onlyperson"; ++ }*/ ++ ++ struct ast_conf_member *membertest = conf->memberlist; ++ while (membertest != NULL) ++ { ++ // lock member for basic_play_sound ++ ast_mutex_lock(&membertest->lock); ++ ++ // basic_play_sound unlock member automatically. do not mute on enter message ++ if (!basic_play_sound ( membertest, enter_snd, 0 )) ++ { ++ ast_log(LOG_ERROR, "playing conference[%d] entry message <%s> FAILED on <%s>\n", conf->membercount, enter_snd, membertest->channel_name); ++ } ++ else ++ { ++ ast_log(LOG_NOTICE, "playing conference[%d] entry message <%s> on <%s>\n", conf->membercount, enter_snd, membertest->channel_name); ++ } ++ ++ membertest = membertest->next; ++ } ++ ++ // unlock conference ++ ast_mutex_unlock( &conf->lock ); ++ } ++ ++ while ( 42 == 42 ) ++ { ++ // make sure we have a channel to process ++ if ( chan == NULL ) ++ { ++ ast_log( LOG_NOTICE, "member channel has closed\n" ) ; ++ break ; ++ } ++ ++ //-----------------// ++ // INCOMING FRAMES // ++ //-----------------// ++ ++ // wait for an event on this channel ++ left = ast_waitfor( chan, AST_CONF_WAITFOR_LATENCY ) ; ++ ++ //ast_log( AST_CONF_DEBUG, "received event on channel, name => %s, left => %d\n", chan->name, left ) ; ++ ++ if ( left < 0 ) ++ { ++ // an error occured ++ ast_log( ++ LOG_NOTICE, ++ "an error occured waiting for a frame, channel => %s, error => %d\n", ++ chan->name, left ++ ) ; ++ break; // out of the 42==42 ++ } ++ else if ( left == 0 ) ++ { ++ // no frame has arrived yet ++ // ast_log( LOG_NOTICE, "no frame available from channel, channel => %s\n", chan->name ) ; ++ } ++ else if ( left > 0 ) ++ { ++ // a frame has come in before the latency timeout ++ // was reached, so we process the frame ++ ++ f = ast_read( chan ) ; ++ ++ if ( f == NULL ) ++ { ++ if (conf->debug_flag) ++ { ++ ast_log( LOG_NOTICE, "unable to read from channel, channel => %s\n", chan->name ) ; ++ // They probably want to hangup... ++ } ++ break ; ++ } ++ ++ // actually process the frame: break if we got hangup. ++ if(process_incoming(member, conf, f)) break; ++ ++ } ++ ++ if (member_checkkick(member)) break; ++ ++ //-----------------// ++ // OUTGOING FRAMES // ++ //-----------------// ++ ++ // update the current timestamps ++ curr = ast_tvnow(); ++ ++ process_outgoing(member); ++ // back to process incoming frames ++ continue ; ++ } ++ ++ ast_log( AST_CONF_DEBUG, "end member event loop, time_entered => %ld\n", member->time_entered.tv_sec ) ; ++ ++ // ++ // clean up ++ // ++ ++#ifdef DEBUG_OUTPUT_PCM ++ // !!! TESTING !!! ++ if ( incoming_fh != NULL ) ++ fclose( incoming_fh ) ; ++#endif ++ ++ // If we're driving another member, make sure its speaker count is correct ++ if ( member != NULL ) member->remove_flag = 1 ; ++ ++// end = ast_tvnow(); ++// int expected_frames = ( int )( floor( (double)( msecdiff( &end, &start ) / AST_CONF_FRAME_INTERVAL ) ) ) ; ++// ast_log( AST_CONF_DEBUG, "expected_frames => %d\n", expected_frames ) ; ++ ++ return 0 ; ++} ++ ++ ++ ++struct ast_conf_member *check_active_video( int id, struct ast_conference *conf ) ++{ ++ struct ast_conf_member *member; ++ ++ // acquire the conference lock ++ ast_mutex_lock( &conf->lock ) ; ++ ++ member = conf->memberlist; ++ while (member) ++ { ++ if (member->id == id) ++ { ++ // lock this member ++ ast_mutex_lock( &member->lock ) ; ++ ast_mutex_unlock( &conf->lock ) ; ++ return member; ++ } ++ member = member->next; ++ } ++ ast_mutex_unlock( &conf->lock ) ; ++ return NULL; ++} ++ ++// ++// manange member functions ++// ++ ++struct ast_conf_member* create_member( struct ast_channel *chan, const char* data ) ++{ ++ char *parse; ++ AST_DECLARE_APP_ARGS(args, ++ AST_APP_ARG(conf_name); ++ AST_APP_ARG(flags); ++ AST_APP_ARG(enter_snd); ++ AST_APP_ARG(leave_snd); ++ AST_APP_ARG(priority); ++ AST_APP_ARG(vad_prob_start); ++ AST_APP_ARG(vad_prob_continue); ++ ); ++ ++ // ++ // check input ++ // ++ ++ if ( chan == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to create member with null channel\n" ) ; ++ return NULL ; ++ } ++ ++ if ( chan->name == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to create member with null channel name\n" ) ; ++ return NULL ; ++ } ++ ++ if (ast_strlen_zero(data)) { ++ ast_log(LOG_ERROR, "Conference requires an argument (conf_name[|flags[|enter_snd[|leave_snd[|priority[vad_prob_start[|vad_prob_continue]]]]]]))\n"); ++ return NULL; ++ } ++ ++ // ++ // allocate memory for new conference member ++ // ++ ++ struct ast_conf_member *member = calloc( 1, sizeof( struct ast_conf_member ) ) ; ++ ++ if ( member == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to malloc ast_conf_member\n" ) ; ++ return NULL ; ++ } ++ ++ // initialize mutex ++ ast_mutex_init( &member->lock ) ; ++ ++ // ++ // initialize member with passed data values ++ // ++ ++ parse = ast_strdupa(data); ++ ++ AST_STANDARD_APP_ARGS(args, parse); ++ ++ // parse the id ++ if (ast_strlen_zero(args.conf_name)) ++ { ++ ast_log( LOG_ERROR, "unable to parse member id\n" ) ; ++ free( member ) ; ++ return NULL ; ++ } ++ else ++ member->conf_name = ast_strdup(args.conf_name); ++ ++ // parse the flags ++ if (!ast_strlen_zero(args.flags)) ++ member->flags = ast_strdup(args.flags); ++ else ++ member->flags = ast_strdup(""); ++ ++ // parse enter sound ++ if (!ast_strlen_zero(args.enter_snd)) ++ member->enter_snd = ast_strdup(args.enter_snd); ++ else ++ member->enter_snd = ast_strdup("enter"); ++ ++ // parse leave sound ++ if (!ast_strlen_zero(args.leave_snd)) ++ member->leave_snd = ast_strdup(args.leave_snd); ++ else ++ member->leave_snd = ast_strdup("leave"); ++ ++ if (!ast_strlen_zero(args.priority)) ++ // parse the priority ++ member->priority = atoi(args.priority); ++ else ++ member->priority = 0; ++ ++ if (!ast_strlen_zero(args.vad_prob_start)) ++ member->vad_prob_start = atof(args.vad_prob_start); ++ else ++ member->vad_prob_start = AST_CONF_PROB_START; ++ ++ if (!ast_strlen_zero(args.vad_prob_continue)) ++ member->vad_prob_continue = atof(args.vad_prob_continue); ++ else ++ member->vad_prob_continue = AST_CONF_PROB_CONTINUE; ++ ++ // debugging ++ ast_log( ++ AST_CONF_DEBUG, ++ "parsed data params, id => %s, flags => %s, priority => %d, vad_prob_start => %f, vad_prob_continue => %f\n", ++ member->conf_name, member->flags, member->priority, member->vad_prob_start, member->vad_prob_continue ++ ) ; ++ ++ // ++ // initialize member with default values ++ // ++ ++ // keep pointer to member's channel ++ member->chan = chan ; ++ ++ // copy the channel name ++ member->channel_name = malloc( strlen( chan->name ) + 1 ) ; ++ strcpy( member->channel_name, chan->name ) ; ++ ++ // ( default can be overridden by passed flags ) ++ member->mute_audio = 0; ++ member->mute_video = 0; ++ member->norecv_audio = 0; ++ member->norecv_video = 0; ++ member->no_camera = 0; ++ ++ // moderator? ++ member->ismoderator = 0; ++ ++ // ready flag ++ member->ready_for_outgoing = 0 ; ++ ++ // incoming frame queue ++ member->inFrames = NULL ; ++ member->inFramesTail = NULL ; ++ member->inFramesCount = 0 ; ++ ++ member->inVideoFrames = NULL ; ++ member->inVideoFramesTail = NULL ; ++ member->inVideoFramesCount = 0 ; ++ ++ member->inDTMFFrames = NULL ; ++ member->inDTMFFramesTail = NULL ; ++ member->inDTMFFramesCount = 0 ; ++ ++ member->inTextFrames = NULL ; ++ member->inTextFramesTail = NULL ; ++ member->inTextFramesCount = 0 ; ++ ++ member->conference = 1; // we have switched req_id ++ member->dtmf_switch = 0; // no dtmf switch by default ++ member->dtmf_relay = 0; // no dtmf relay by default ++ ++ // start of day video ids ++ member->req_id = -1; ++ member->id = -1; ++ ++ member->first_frame_received = 0; // cause a FIR after NAT delay ++ ++ // last frame caching ++ member->inFramesRepeatLast = 0 ; ++ member->inFramesLast = NULL ; ++ member->okayToCacheLast = 0 ; ++ ++ // outgoing frame queue ++ member->outFrames = NULL ; ++ member->outFramesTail = NULL ; ++ member->outFramesCount = 0 ; ++ ++ member->outVideoFrames = NULL ; ++ member->outVideoFramesTail = NULL ; ++ member->outVideoFramesCount = 0 ; ++ ++ member->outDTMFFrames = NULL ; ++ member->outDTMFFramesTail = NULL ; ++ member->outDTMFFramesCount = 0 ; ++ ++ member->outTextFrames = NULL ; ++ member->outTextFramesTail = NULL ; ++ member->outTextFramesCount = 0 ; ++ ++ // ( not currently used ) ++ // member->samplesperframe = AST_CONF_BLOCK_SAMPLES ; ++ ++ // used for determining need to mix frames ++ // and for management interface notification ++ // and for VAD based video switching ++ member->speaking_state_notify = 0 ; ++ member->speaking_state = 0 ; ++ member->local_speaking_state = 0; ++ member->speaker_count = 0; ++ member->driven_member = NULL; ++ ++ // linked-list pointer ++ member->next = NULL ; ++ ++ // account data ++ member->frames_in = 0 ; ++ member->frames_in_dropped = 0 ; ++ member->frames_out = 0 ; ++ member->frames_out_dropped = 0 ; ++ member->video_frames_in = 0 ; ++ member->video_frames_in_dropped = 0 ; ++ member->video_frames_out = 0 ; ++ member->video_frames_out_dropped = 0 ; ++ member->dtmf_frames_in = 0 ; ++ member->dtmf_frames_in_dropped = 0 ; ++ member->dtmf_frames_out = 0 ; ++ member->dtmf_frames_out_dropped = 0 ; ++ member->text_frames_in = 0 ; ++ member->text_frames_in_dropped = 0 ; ++ member->text_frames_out = 0 ; ++ member->text_frames_out_dropped = 0 ; ++ ++ // for counting sequentially dropped frames ++ member->sequential_drops = 0 ; ++ member->since_dropped = 0 ; ++ ++ // flags ++ member->remove_flag = 0 ; ++ member->kick_flag = 0; ++ ++ // record start time ++ // init dropped frame timestamps ++ // init state change timestamp ++ member->time_entered = ++ member->last_in_dropped = ++ member->last_out_dropped = ++ member->last_state_change = ast_tvnow(); ++ ++ // ++ // parse passed flags ++ // ++ ++ // silence detection flags w/ defaults ++ member->vad_flag = 0 ; ++ member->denoise_flag = 0 ; ++ member->agc_flag = 0 ; ++ ++ // is this member using the telephone? ++ member->via_telephone = 0 ; ++ ++ // temp pointer to flags string ++ char* flags = member->flags ; ++ ++ int i; ++ ++ for ( i = 0 ; i < strlen( flags ) ; ++i ) ++ { ++ ++ if (flags[i] >= (int)'0' && flags[i] <= (int)'9') ++ { ++ if (member->req_id < 0) ++ { ++ member->req_id = flags[i] - (int)'0'; ++ } ++ else ++ { ++ int newid = flags[i] - (int)'0'; ++ // need to boot anyone with this id already ++ // will happen in add_member ++ member->id = newid; ++ } ++ } ++ else ++ { ++ // allowed flags are C, c, L, l, V, D, A, C, X, R, T, t, M, S ++ // mute/no_recv options ++ switch ( flags[i] ) ++ { ++ case 'C': ++ member->mute_video = 1; ++ break ; ++ case 'c': ++ member->norecv_video = 1; ++ break ; ++ case 'L': ++ member->mute_audio = 1; ++ break ; ++ case 'l': ++ member->norecv_audio = 1; ++ break; ++ ++ // speex preprocessing options ++ case 'V': ++ member->vad_flag = 1 ; ++ break ; ++ case 'D': ++ member->denoise_flag = 1 ; ++ break ; ++ case 'A': ++ member->agc_flag = 1 ; ++ break ; ++ ++ // dtmf/moderator/video switching options ++ case 'X': ++ member->dtmf_switch = 1; ++ break; ++ case 'R': ++ member->dtmf_relay = 1; ++ break; ++ case 'S': ++ member->vad_switch = 1; ++ break; ++ case 'M': ++ member->ismoderator = 1; ++ break; ++ case 'N': ++ member->no_camera = 1; ++ break; ++ case 't': ++ member->does_text = 1; ++ break; ++ ++ //Telephone connection ++ case 'T': ++ member->via_telephone = 1; ++ break; ++ ++ default: ++ ast_log( LOG_WARNING, "received invalid flag, chan => %s, flag => %c\n", ++ chan->name, flags[i] ); ++ break ; ++ } ++ } ++ } ++ ++ // set the dsp to null so silence detection is disabled by default ++ member->dsp = NULL ; ++ ++#if ( SILDET == 2 ) ++ // ++ // configure silence detection and preprocessing ++ // if the user is coming in via the telephone, ++ // and is not listen-only ++ // ++ if ( ++ member->via_telephone == 1 ++ && member->type != 'L' ++ ) ++ { ++ // create a speex preprocessor ++ member->dsp = speex_preprocess_state_init( AST_CONF_BLOCK_SAMPLES, AST_CONF_SAMPLE_RATE ) ; ++ ++ if ( member->dsp == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to initialize member dsp, channel => %s\n", chan->name ) ; ++ } ++ else ++ { ++ ast_log( LOG_NOTICE, "member dsp initialized, channel => %s, v => %d, d => %d, a => %d\n", ++ chan->name, member->vad_flag, member->denoise_flag, member->agc_flag ) ; ++ ++ // set speex preprocessor options ++ speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_VAD, &(member->vad_flag) ) ; ++ speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_DENOISE, &(member->denoise_flag) ) ; ++ speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_AGC, &(member->agc_flag) ) ; ++ ++ speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_PROB_START, &member->vad_prob_start ) ; ++ speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_PROB_CONTINUE, &member->vad_prob_continue ) ; ++ ++ speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_GET_PROB_START, &member->vad_prob_start ) ; ++ speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_GET_PROB_CONTINUE, &member->vad_prob_continue ) ; ++ ++ ast_log( AST_CONF_DEBUG, "speech_prob_start => %f, speech_prob_continue => %f\n", ++ member->vad_prob_start, member->vad_prob_continue ) ; ++ } ++ } ++#endif ++ ++ // ++ // set connection type ++ // ++ ++ if ( member->via_telephone == 1 ) ++ { ++ member->connection_type = 'T' ; ++ } ++ else if ( strncmp( member->channel_name, "SIP", 3 ) == 0 ) ++ { ++ member->connection_type = 'S' ; ++ } ++ else // default to iax ++ { ++ member->connection_type = 'X' ; ++ } ++ ++ // ++ // read, write, and translation options ++ // ++ ++ // set member's audio formats, taking dsp preprocessing into account ++ // ( chan->nativeformats, AST_FORMAT_SLINEAR, AST_FORMAT_ULAW, AST_FORMAT_GSM ) ++ member->read_format = ( member->dsp == NULL ) ? chan->nativeformats : AST_FORMAT_SLINEAR ; ++ ++ member->write_format = chan->nativeformats; ++ ++ // 1.2 or 1.3+ ++#ifdef AST_FORMAT_AUDIO_MASK ++ ++ member->read_format &= AST_FORMAT_AUDIO_MASK; ++ member->write_format &= AST_FORMAT_AUDIO_MASK; ++#endif ++ ++ // translation paths ( ast_translator_build_path() returns null if formats match ) ++ member->to_slinear = ast_translator_build_path( AST_FORMAT_SLINEAR, member->read_format ) ; ++ member->from_slinear = ast_translator_build_path( member->write_format, AST_FORMAT_SLINEAR ) ; ++ ++ ast_log( AST_CONF_DEBUG, "AST_FORMAT_SLINEAR => %d\n", AST_FORMAT_SLINEAR ) ; ++ ++ // index for converted_frames array ++ switch ( member->write_format ) ++ { ++ case AST_FORMAT_SLINEAR: ++ member->write_format_index = AC_SLINEAR_INDEX ; ++ break ; ++ ++ case AST_FORMAT_ULAW: ++ member->write_format_index = AC_ULAW_INDEX ; ++ break ; ++ ++ case AST_FORMAT_ALAW: ++ member->write_format_index = AC_ALAW_INDEX ; ++ break ; ++ ++ case AST_FORMAT_GSM: ++ member->write_format_index = AC_GSM_INDEX ; ++ break ; ++ ++ case AST_FORMAT_SPEEX: ++ member->write_format_index = AC_SPEEX_INDEX; ++ break; ++ ++#ifdef AC_USE_G729A ++ case AST_FORMAT_G729A: ++ member->write_format_index = AC_G729A_INDEX; ++ break; ++#endif ++ ++ default: ++ member->write_format_index = 0 ; ++ } ++ ++ // index for converted_frames array ++ switch ( member->read_format ) ++ { ++ case AST_FORMAT_SLINEAR: ++ member->read_format_index = AC_SLINEAR_INDEX ; ++ break ; ++ ++ case AST_FORMAT_ULAW: ++ member->read_format_index = AC_ULAW_INDEX ; ++ break ; ++ ++ case AST_FORMAT_ALAW: ++ member->read_format_index = AC_ALAW_INDEX ; ++ break ; ++ ++ case AST_FORMAT_GSM: ++ member->read_format_index = AC_GSM_INDEX ; ++ break ; ++ ++ case AST_FORMAT_SPEEX: ++ member->read_format_index = AC_SPEEX_INDEX; ++ break; ++ ++#ifdef AC_USE_G729A ++ case AST_FORMAT_G729A: ++ member->read_format_index = AC_G729A_INDEX; ++ break; ++#endif ++ ++ default: ++ member->read_format_index = 0 ; ++ } ++ ++ // smoother defaults. ++ member->smooth_multiple =1; ++ member->smooth_size_in = -1; ++ member->smooth_size_out = -1; ++ member->inSmoother= NULL; ++ member->outPacker= NULL; ++ ++ switch (member->read_format){ ++ /* these assumptions may be incorrect */ ++ case AST_FORMAT_ULAW: ++ case AST_FORMAT_ALAW: ++ member->smooth_size_in = 160; //bytes ++ member->smooth_size_out = 160; //samples ++ break; ++ case AST_FORMAT_GSM: ++ /* ++ member->smooth_size_in = 33; //bytes ++ member->smooth_size_out = 160;//samples ++ */ ++ break; ++ case AST_FORMAT_SPEEX: ++ case AST_FORMAT_G729A: ++ /* this assumptions are wrong ++ member->smooth_multiple = 2 ; // for testing, force to dual frame ++ member->smooth_size_in = 39; // bytes ++ member->smooth_size_out = 160; // samples ++ */ ++ break; ++ case AST_FORMAT_SLINEAR: ++ member->smooth_size_in = 320; //bytes ++ member->smooth_size_out = 160; //samples ++ break; ++ default: ++ member->inSmoother = NULL; //don't use smoother for this type. ++ //ast_log( AST_CONF_DEBUG, "smoother is NULL for member->read_format => %d\n", member->read_format); ++ } ++ ++ if (member->smooth_size_in > 0){ ++ member->inSmoother = ast_smoother_new(member->smooth_size_in); ++ ast_log( AST_CONF_DEBUG, "created smoother(%d) for %d\n", member->smooth_size_in , member->read_format); ++ } ++ ++ // ++ // finish up ++ // ++ ++ ast_log( AST_CONF_DEBUG, "created member, type => %c, priority => %d, readformat => %d\n", ++ member->type, member->priority, chan->readformat ) ; ++ ++ return member ; ++} ++ ++struct ast_conf_member* delete_member( struct ast_conf_member* member ) ++{ ++ // !!! NO RETURN TEST !!! ++ // do { sleep(1) ; } while (1) ; ++ ++ // !!! CRASH TEST !!! ++ // *((int *)0) = 0; ++ ++ if ( member == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to the delete null member\n" ) ; ++ return NULL ; ++ } ++ ++ ast_mutex_lock(&member->lock); ++ ++ // If member is driving another member, make sure its speaker count is correct ++ if ( member->driven_member != NULL && member->speaking_state == 1 ) ++ decrement_speaker_count(member->driven_member, 1); ++ ++ // ++ // clean up member flags ++ // ++ ++ if ( member->flags != NULL ) ++ { ++ // !!! DEBUGING !!! ++ ast_log( AST_CONF_DEBUG, "freeing member flags, name => %s\n", ++ member->channel_name ) ; ++ free( member->flags ) ; ++ } ++ ++ // ++ // delete the members frames ++ // ++ ++ conf_frame* cf ; ++ ++ // !!! DEBUGING !!! ++ ast_log( AST_CONF_DEBUG, "deleting member input frames, name => %s\n", ++ member->channel_name ) ; ++ ++ // incoming frames ++ cf = member->inFrames ; ++ ++ while ( cf != NULL ) ++ { ++ cf = delete_conf_frame( cf ) ; ++ } ++ ++ if (member->inSmoother != NULL) ++ ast_smoother_free(member->inSmoother); ++ ++ cf = member->inVideoFrames ; ++ ++ while ( cf != NULL ) ++ { ++ cf = delete_conf_frame( cf ) ; ++ } ++ ++ // !!! DEBUGING !!! ++ ast_log( AST_CONF_DEBUG, "deleting member output frames, name => %s\n", ++ member->channel_name ) ; ++ ++ // outgoing frames ++ cf = member->outFrames ; ++ ++ while ( cf != NULL ) ++ { ++ cf = delete_conf_frame( cf ) ; ++ } ++ ++ cf = member->outVideoFrames ; ++ ++ while ( cf != NULL ) ++ { ++ cf = delete_conf_frame( cf ) ; ++ } ++ ++#if ( SILDET == 2 ) ++ if ( member->dsp != NULL ) ++ { ++ // !!! DEBUGING !!! ++ ast_log( AST_CONF_DEBUG, "destroying member preprocessor, name => %s\n", ++ member->channel_name ) ; ++ speex_preprocess_state_destroy( member->dsp ) ; ++ } ++#endif ++ ++ // !!! DEBUGING !!! ++ ast_log( AST_CONF_DEBUG, "freeing member translator paths, name => %s\n", ++ member->channel_name ) ; ++ ++ // free the mixing translators ++ ast_translator_free_path( member->to_slinear ) ; ++ ast_translator_free_path( member->from_slinear ) ; ++ ++ // get a pointer to the next ++ // member so we can return it ++ struct ast_conf_member* nm = member->next ; ++ ++ ast_mutex_unlock(&member->lock); ++ ++ // !!! DEBUGING !!! ++ ast_log( AST_CONF_DEBUG, "freeing member channel name, name => %s\n", ++ member->channel_name ) ; ++ ++ // free the member's copy for the channel name ++ free( member->channel_name ) ; ++ ++ // free the member's copy of the conference name ++ free(member->conf_name); ++ ++ // !!! DEBUGING !!! ++ ast_log( AST_CONF_DEBUG, "freeing member\n" ) ; ++ ++ // free the member's memory ++ free(member->callerid); ++ free(member->callername); ++ ++ // free enter/leave sounds ++ free(member->enter_snd); ++ free(member->leave_snd); ++ ++ free( member ) ; ++ member = NULL ; ++ ++ return nm ; ++} ++ ++// ++// incoming frame functions ++// ++ ++conf_frame* get_incoming_video_frame( struct ast_conf_member *member ) ++{ ++ if ( member == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ; ++ return NULL ; ++ } ++ ++ ast_mutex_lock(&member->lock); ++ ++ if ( member->inVideoFramesCount == 0 ) ++ { ++ ast_mutex_unlock(&member->lock); ++ return NULL ; ++ } ++ ++ // ++ // return the next frame in the queue ++ // ++ ++ conf_frame* cfr = NULL ; ++ ++ // get first frame in line ++ cfr = member->inVideoFramesTail ; ++ ++ // if it's the only frame, reset the queue, ++ // else, move the second frame to the front ++ if ( member->inVideoFramesTail == member->inVideoFrames ) ++ { ++ member->inVideoFramesTail = NULL ; ++ member->inVideoFrames = NULL ; ++ } ++ else ++ { ++ // move the pointer to the next frame ++ member->inVideoFramesTail = member->inVideoFramesTail->prev ; ++ ++ // reset it's 'next' pointer ++ if ( member->inVideoFramesTail != NULL ) ++ member->inVideoFramesTail->next = NULL ; ++ } ++ ++ // separate the conf frame from the list ++ cfr->next = NULL ; ++ cfr->prev = NULL ; ++ ++ // decrement frame count ++ member->inVideoFramesCount-- ; ++ ++ ast_mutex_unlock(&member->lock); ++ return cfr ; ++ ++} ++conf_frame* get_incoming_dtmf_frame( struct ast_conf_member *member ) ++{ ++ if ( member == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ; ++ return NULL ; ++ } ++ ++ ast_mutex_lock(&member->lock); ++ ++ if ( member->inDTMFFramesCount == 0 ) ++ { ++ ast_mutex_unlock(&member->lock); ++ return NULL ; ++ } ++ ++ // ++ // return the next frame in the queue ++ // ++ ++ conf_frame* cfr = NULL ; ++ ++ // get first frame in line ++ cfr = member->inDTMFFramesTail ; ++ ++ // if it's the only frame, reset the queue, ++ // else, move the second frame to the front ++ if ( member->inDTMFFramesTail == member->inDTMFFrames ) ++ { ++ member->inDTMFFramesTail = NULL ; ++ member->inDTMFFrames = NULL ; ++ } ++ else ++ { ++ // move the pointer to the next frame ++ member->inDTMFFramesTail = member->inDTMFFramesTail->prev ; ++ ++ // reset it's 'next' pointer ++ if ( member->inDTMFFramesTail != NULL ) ++ member->inDTMFFramesTail->next = NULL ; ++ } ++ ++ // separate the conf frame from the list ++ cfr->next = NULL ; ++ cfr->prev = NULL ; ++ ++ // decriment frame count ++ member->inDTMFFramesCount-- ; ++ ++ ast_mutex_unlock(&member->lock); ++ return cfr ; ++ ++} ++ ++ ++conf_frame* get_incoming_frame( struct ast_conf_member *member ) ++{ ++ conf_frame *cf_result; ++ // ++ // sanity checks ++ // ++ ++ if ( member == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ; ++ return NULL ; ++ } ++ ++ ast_mutex_lock(&member->lock); ++ ++ // ++ // repeat last frame a couple times to smooth transition ++ // ++ ++#ifdef AST_CONF_CACHE_LAST_FRAME ++ if ( member->inFramesCount == 0 ) ++ { ++ // nothing to do if there's no cached frame ++ if ( member->inFramesLast == NULL ) { ++ ast_mutex_unlock(&member->lock); ++ return NULL ; ++ } ++ ++ // turn off 'okay to cache' flag ++ member->okayToCacheLast = 0 ; ++ ++ if ( member->inFramesRepeatLast >= AST_CONF_CACHE_LAST_FRAME ) ++ { ++ // already used this frame AST_CONF_CACHE_LAST_FRAME times ++ ++ // reset repeat count ++ member->inFramesRepeatLast = 0 ; ++ ++ // clear the cached frame ++ delete_conf_frame( member->inFramesLast ) ; ++ member->inFramesLast = NULL ; ++ ++ // return null ++ ast_mutex_unlock(&member->lock); ++ return NULL ; ++ } ++ else ++ { ++ ast_log( AST_CONF_DEBUG, "repeating cached frame, channel => %s, inFramesRepeatLast => %d\n", ++ member->channel_name, member->inFramesRepeatLast ) ; ++ ++ // increment counter ++ member->inFramesRepeatLast++ ; ++ ++ // return a copy of the cached frame ++ cf_result = copy_conf_frame( member->inFramesLast ) ; ++ ast_mutex_unlock(&member->lock); ++ return cf_result; ++ } ++ } ++ else if ( member->okayToCacheLast == 0 && member->inFramesCount >= 3 ) ++ { ++ ast_log( AST_CONF_DEBUG, "enabling cached frame, channel => %s, incoming => %d, outgoing => %d\n", ++ member->channel_name, member->inFramesCount, member->outFramesCount ) ; ++ ++ // turn on 'okay to cache' flag ++ member->okayToCacheLast = 1 ; ++ } ++#else ++ if ( member->inFramesCount == 0 ) { ++ ast_mutex_unlock(&member->lock); ++ return NULL ; ++ } ++#endif // AST_CONF_CACHE_LAST_FRAME ++ ++ // ++ // return the next frame in the queue ++ // ++ ++ conf_frame* cfr = NULL ; ++ ++ // get first frame in line ++ cfr = member->inFramesTail ; ++ ++ // if it's the only frame, reset the queue, ++ // else, move the second frame to the front ++ if ( member->inFramesTail == member->inFrames ) ++ { ++ member->inFramesTail = NULL ; ++ member->inFrames = NULL ; ++ } ++ else ++ { ++ // move the pointer to the next frame ++ member->inFramesTail = member->inFramesTail->prev ; ++ ++ // reset it's 'next' pointer ++ if ( member->inFramesTail != NULL ) ++ member->inFramesTail->next = NULL ; ++ } ++ ++ // separate the conf frame from the list ++ cfr->next = NULL ; ++ cfr->prev = NULL ; ++ ++ // decriment frame count ++ member->inFramesCount-- ; ++ ++#ifdef AST_CONF_CACHE_LAST_FRAME ++ // copy frame if queue is now empty ++ if ( ++ member->inFramesCount == 0 ++ && member->okayToCacheLast == 1 ++ ) ++ { ++ // reset repeat count ++ member->inFramesRepeatLast = 0 ; ++ ++ // clear cached frame ++ if ( member->inFramesLast != NULL ) ++ { ++ delete_conf_frame( member->inFramesLast ) ; ++ member->inFramesLast = NULL ; ++ } ++ ++ // cache new frame ++ member->inFramesLast = copy_conf_frame( cfr ) ; ++ } ++#endif // AST_CONF_CACHE_LAST_FRAME ++ ++ ast_mutex_unlock(&member->lock); ++ return cfr ; ++} ++ ++int queue_incoming_video_frame( struct ast_conf_member* member, const struct ast_frame* fr ) ++{ ++ // check on frame ++ if ( fr == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to queue null frame\n" ) ; ++ return -1 ; ++ } ++ ++ // check on member ++ if ( member == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ; ++ return -1 ; ++ } ++ ++ // lock the member ++ ast_mutex_lock(&member->lock); ++ ++ if (!member->first_frame_received) ++ { ++ // nat=yes will be correct now ++ member->first_frame_received = 1; ++ member->conference = 1; ++ } ++ ++ // We have to drop if the queue is full! ++ if ( member->inVideoFramesCount >= AST_CONF_MAX_VIDEO_QUEUE ) ++ { ++ ast_log( ++ AST_CONF_DEBUG, ++ "unable to queue incoming VIDEO frame, channel => %s, incoming => %d, outgoing => %d\n", ++ member->channel_name, member->inVideoFramesCount, member->outVideoFramesCount ++ ) ; ++ ast_mutex_unlock(&member->lock); ++ return -1 ; ++ } ++ ++ // ++ // create new conf frame from passed data frame ++ // ++ ++ // ( member->inFrames may be null at this point ) ++ conf_frame* cfr = create_conf_frame( member, member->inVideoFrames, fr ) ; ++ ++ if ( cfr == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to malloc conf_frame\n" ) ; ++ ast_mutex_unlock(&member->lock); ++ return -1 ; ++ } ++ ++ // copy frame data pointer to conf frame ++ // cfr->fr = fr ; ++ ++ // ++ // add new frame to speaking members incoming frame queue ++ // ( i.e. save this frame data, so we can distribute it in conference_exec later ) ++ // ++ ++ if ( member->inVideoFrames == NULL ) ++ { ++ // this is the first frame in the buffer ++ member->inVideoFramesTail = cfr ; ++ member->inVideoFrames = cfr ; ++ } ++ else ++ { ++ // put the new frame at the head of the list ++ member->inVideoFrames = cfr ; ++ } ++ ++ // increment member frame count ++ member->inVideoFramesCount++ ; ++ ++ ast_mutex_unlock(&member->lock); ++ ++ // Everything has gone okay! ++ return 0; ++} ++ ++int queue_incoming_dtmf_frame( struct ast_conf_member* member, const struct ast_frame* fr ) ++{ ++ //ast_log( AST_CONF_DEBUG, "queue incoming video frame\n"); ++ ++ // check on frame ++ if ( fr == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to queue null frame\n" ) ; ++ return -1 ; ++ } ++ ++ // check on member ++ if ( member == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ; ++ return -1 ; ++ } ++ ++ ast_mutex_lock(&member->lock); ++ ++ // We have to drop if the queue is full! ++ if ( member->inDTMFFramesCount >= AST_CONF_MAX_DTMF_QUEUE ) ++ { ++ ast_log( ++ AST_CONF_DEBUG, ++ "unable to queue incoming DTMF frame, channel => %s, incoming => %d, outgoing => %d\n", ++ member->channel_name, member->inDTMFFramesCount, member->outDTMFFramesCount ++ ) ; ++ ast_mutex_unlock(&member->lock); ++ return -1 ; ++ } ++ ++ // ++ // create new conf frame from passed data frame ++ // ++ ++ // ( member->inFrames may be null at this point ) ++ conf_frame* cfr = create_conf_frame( member, member->inDTMFFrames, fr ) ; ++ ++ if ( cfr == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to malloc conf_frame\n" ) ; ++ ast_mutex_unlock(&member->lock); ++ return -1 ; ++ } ++ ++ // copy frame data pointer to conf frame ++ // cfr->fr = fr ; ++ ++ // ++ // add new frame to speaking members incoming frame queue ++ // ( i.e. save this frame data, so we can distribute it in conference_exec later ) ++ // ++ ++ if ( member->inDTMFFrames == NULL ) ++ { ++ // this is the first frame in the buffer ++ member->inDTMFFramesTail = cfr ; ++ member->inDTMFFrames = cfr ; ++ } ++ else ++ { ++ // put the new frame at the head of the list ++ member->inDTMFFrames = cfr ; ++ } ++ ++ // increment member frame count ++ member->inDTMFFramesCount++ ; ++ ++ ast_mutex_unlock(&member->lock); ++ ++ // Everything has gone okay! ++ return 0; ++} ++ ++int queue_incoming_frame( struct ast_conf_member* member, struct ast_frame* fr ) ++{ ++ // ++ // sanity checks ++ // ++ ++ // check on frame ++ if ( fr == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to queue null frame\n" ) ; ++ return -1 ; ++ } ++ ++ // check on member ++ if ( member == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ; ++ return -1 ; ++ } ++ ++ ast_mutex_lock(&member->lock); ++ ++ if ( member->inFramesCount > member->inFramesNeeded ) ++ { ++ if ( member->inFramesCount > AST_CONF_QUEUE_DROP_THRESHOLD ) ++ { ++ struct timeval curr = ast_tvnow(); ++ ++ // time since last dropped frame ++ long diff = ast_tvdiff_ms(curr, member->last_in_dropped); ++ ++ // number of milliseconds which must pass between frame drops ++ // ( 15 frames => -100ms, 10 frames => 400ms, 5 frames => 900ms, 0 frames => 1400ms, etc. ) ++ long time_limit = 1000 - ( ( member->inFramesCount - AST_CONF_QUEUE_DROP_THRESHOLD ) * 100 ) ; ++ ++ if ( diff >= time_limit ) ++ { ++ // count sequential drops ++ member->sequential_drops++ ; ++ ++ ast_log( ++ AST_CONF_DEBUG, ++ "dropping frame from input buffer, channel => %s, incoming => %d, outgoing => %d\n", ++ member->channel_name, member->inFramesCount, member->outFramesCount ++ ) ; ++ ++ // accounting: count dropped incoming frames ++ member->frames_in_dropped++ ; ++ ++ // reset frames since dropped ++ member->since_dropped = 0 ; ++ ++ // delete the frame ++ delete_conf_frame( get_incoming_frame( member ) ) ; ++ ++ member->last_in_dropped = ast_tvnow(); ++ } ++ else ++ { ++/* ++ ast_log( ++ AST_CONF_DEBUG, ++ "input buffer larger than drop threshold, channel => %s, incoming => %d, outgoing => %d\n", ++ member->channel_name, member->inFramesCount, member->outFramesCount ++ ) ; ++*/ ++ } ++ } ++ } ++ ++ // ++ // if we have to drop frames, we'll drop new frames ++ // because it's easier ( and doesn't matter much anyway ). ++ // ++ ++ if ( member->inFramesCount >= AST_CONF_MAX_QUEUE ) ++ { ++ // count sequential drops ++ member->sequential_drops++ ; ++ ++ ast_log( ++ AST_CONF_DEBUG, ++ "unable to queue incoming frame, channel => %s, incoming => %d, outgoing => %d\n", ++ member->channel_name, member->inFramesCount, member->outFramesCount ++ ) ; ++ ++ // accounting: count dropped incoming frames ++ member->frames_in_dropped++ ; ++ ++ // reset frames since dropped ++ member->since_dropped = 0 ; ++ ++ ast_mutex_unlock(&member->lock); ++ return -1 ; ++ } ++ ++ // reset sequential drops ++ member->sequential_drops = 0 ; ++ ++ // increment frames since dropped ++ member->since_dropped++ ; ++ ++ // ++ // create new conf frame from passed data frame ++ // ++ ++ // ( member->inFrames may be null at this point ) ++ if (member->inSmoother == NULL ){ ++ conf_frame* cfr = create_conf_frame( member, member->inFrames, fr ) ; ++ if ( cfr == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to malloc conf_frame\n" ) ; ++ ast_mutex_unlock(&member->lock); ++ return -1 ; ++ } ++ ++ // ++ // add new frame to speaking members incoming frame queue ++ // ( i.e. save this frame data, so we can distribute it in conference_exec later ) ++ // ++ ++ if ( member->inFrames == NULL ) { ++ member->inFramesTail = cfr ; ++ } ++ member->inFrames = cfr ; ++ member->inFramesCount++ ; ++ } else { ++ //feed frame(fr) into the smoother ++ ++ // smoother tmp frame ++ struct ast_frame *sfr; ++ int multiple = 1; ++ int i=0; ++ ++#if 0 ++ if ( (member->smooth_size_in > 0 ) && (member->smooth_size_in * member->smooth_multiple != fr->datalen) ) ++ { ++ ast_log( AST_CONF_DEBUG, "resetting smooth_size_in. old size=> %d, multiple =>%d, datalen=> %d\n", member->smooth_size_in, member->smooth_multiple, fr->datalen ); ++ if ( fr->datalen % member->smooth_multiple != 0) { ++ // if datalen not divisible by smooth_multiple, assume we're just getting normal encoding. ++ // ast_log(AST_CONF_DEBUG,"smooth_multiple does not divide datalen. changing smooth size from %d to %d, multiple => 1\n", member->smooth_size_in, fr->datalen); ++ member->smooth_size_in = fr->datalen; ++ member->smooth_multiple = 1; ++ } else { ++ // assume a fixed multiple, so divide into datalen. ++ int newsmooth = fr->datalen / member->smooth_multiple ; ++ // ast_log(AST_CONF_DEBUG,"datalen is divisible by smooth_multiple, changing smooth size from %d to %d\n", member->smooth_size_in, newsmooth); ++ member->smooth_size_in = newsmooth; ++ } ++ ++ //free input smoother. ++ if (member->inSmoother != NULL) ++ ast_smoother_free(member->inSmoother); ++ ++ //make new input smoother. ++ member->inSmoother = ast_smoother_new(member->smooth_size_in); ++ } ++#endif ++ ++ ast_smoother_feed( member->inSmoother, fr ); ++ast_log (AST_CONF_DEBUG, "SMOOTH:Feeding frame into inSmoother, timestamp => %ld.%ld\n", fr->delivery.tv_sec, fr->delivery.tv_usec); ++ ++ if ( multiple > 1 ) ++ fr->samples /= multiple; ++ ++ // read smoothed version of frames, add to queue ++ while( ( sfr = ast_smoother_read( member->inSmoother ) ) ){ ++ ++ ++i; ++ast_log( AST_CONF_DEBUG , "\treading new frame [%d] from smoother, inFramesCount[%d], \n\tsfr->frametype -> %d , sfr->subclass -> %d , sfr->datalen => %d sfr->samples => %d\n", i , member->inFramesCount , sfr->frametype, sfr->subclass, sfr->datalen, sfr->samples); ++ast_log (AST_CONF_DEBUG, "SMOOTH:Reading frame from inSmoother, i=>%d, timestamp => %ld.%ld\n",i, sfr->delivery.tv_sec, sfr->delivery.tv_usec); ++ conf_frame* cfr = create_conf_frame( member, member->inFrames, sfr ) ; ++ if ( cfr == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to malloc conf_frame\n" ) ; ++ ast_mutex_unlock(&member->lock); ++ return -1 ; ++ } ++ ++ // ++ // add new frame to speaking members incoming frame queue ++ // ( i.e. save this frame data, so we can distribute it in conference_exec later ) ++ // ++ ++ if ( member->inFrames == NULL ) { ++ member->inFramesTail = cfr ; ++ } ++ member->inFrames = cfr ; ++ member->inFramesCount++ ; ++ } ++ } ++ ast_mutex_unlock(&member->lock); ++ return 0 ; ++} ++ ++// ++// outgoing frame functions ++// ++ ++conf_frame* get_outgoing_frame( struct ast_conf_member *member ) ++{ ++ if ( member == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ; ++ return NULL ; ++ } ++ ++ conf_frame* cfr ; ++ ++ // ast_log( AST_CONF_DEBUG, "getting member frames, count => %d\n", member->outFramesCount ) ; ++ ++ ast_mutex_lock(&member->lock); ++ ++ if ( member->outFramesCount > AST_CONF_MIN_QUEUE ) ++ { ++ cfr = member->outFramesTail ; ++ ++ // if it's the only frame, reset the queu, ++ // else, move the second frame to the front ++ if ( member->outFramesTail == member->outFrames ) ++ { ++ member->outFrames = NULL ; ++ member->outFramesTail = NULL ; ++ } ++ else ++ { ++ // move the pointer to the next frame ++ member->outFramesTail = member->outFramesTail->prev ; ++ ++ // reset it's 'next' pointer ++ if ( member->outFramesTail != NULL ) ++ member->outFramesTail->next = NULL ; ++ } ++ ++ // separate the conf frame from the list ++ cfr->next = NULL ; ++ cfr->prev = NULL ; ++ ++ // decriment frame count ++ member->outFramesCount-- ; ++ ast_mutex_unlock(&member->lock); ++ return cfr ; ++ } ++ ast_mutex_unlock(&member->lock); ++ return NULL ; ++} ++ ++int __queue_outgoing_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery ) ++{ ++ // accounting: count the number of outgoing frames for this member ++ member->frames_out++ ; ++ ++ // ++ // we have to drop frames, so we'll drop new frames ++ // because it's easier ( and doesn't matter much anyway ). ++ // ++ if ( member->outFramesCount >= AST_CONF_MAX_QUEUE ) ++ { ++ ast_log( ++ AST_CONF_DEBUG, ++ "unable to queue outgoing frame, channel => %s, incoming => %d, outgoing => %d\n", ++ member->channel_name, member->inFramesCount, member->outFramesCount ++ ) ; ++ ++ // accounting: count dropped outgoing frames ++ member->frames_out_dropped++ ; ++ return -1 ; ++ } ++ ++ // ++ // create new conf frame from passed data frame ++ // ++ ++ conf_frame* cfr = create_conf_frame( member, member->outFrames, fr ) ; ++ ++ if ( cfr == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to create new conf frame\n" ) ; ++ ++ // accounting: count dropped outgoing frames ++ member->frames_out_dropped++ ; ++ return -1 ; ++ } ++ ++ // set delivery timestamp ++ cfr->fr->delivery = delivery ; ++ ++ // ++ // add new frame to speaking members incoming frame queue ++ // ( i.e. save this frame data, so we can distribute it in conference_exec later ) ++ // ++ ++ if ( member->outFrames == NULL ) { ++ member->outFramesTail = cfr ; ++ } ++ member->outFrames = cfr ; ++ member->outFramesCount++ ; ++ ++ // return success ++ return 0 ; ++} ++ ++int queue_outgoing_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery ) ++{ ++ // check on frame ++ if ( fr == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to queue null frame\n" ) ; ++ return -1 ; ++ } ++ ++ // check on member ++ if ( member == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ; ++ return -1 ; ++ } ++ ++ if ( ( member->outPacker == NULL ) && ( member->smooth_multiple > 1 ) && ( member->smooth_size_out > 0 ) ){ ++ //ast_log (AST_CONF_DEBUG, "creating outPacker with size => %d \n\t( multiple => %d ) * ( size => %d )\n", member->smooth_multiple * member-> smooth_size_out, member->smooth_multiple , member->smooth_size_out); ++ member->outPacker = ast_packer_new( member->smooth_multiple * member->smooth_size_out); ++ } ++ ++ if (member->outPacker == NULL ){ ++ return __queue_outgoing_frame( member, fr, delivery ) ; ++ } ++ else ++ { ++ struct ast_frame *sfr; ++ int exitval = 0; ++//ast_log (AST_CONF_DEBUG, "sending fr into outPacker, datalen=>%d, samples=>%d\n",fr->datalen, fr->samples); ++ ast_packer_feed( member->outPacker , fr ); ++ while( (sfr = ast_packer_read( member->outPacker ) ) ) ++ { ++//ast_log (AST_CONF_DEBUG, "read sfr from outPacker, datalen=>%d, samples=>%d\n",sfr->datalen, sfr->samples); ++ if ( __queue_outgoing_frame( member, sfr, delivery ) == -1 ) { ++ exitval = -1; ++ } ++ } ++ ++ return exitval; ++ } ++} ++ ++// ++// outgoing frame functions ++// ++ ++conf_frame* get_outgoing_video_frame( struct ast_conf_member *member ) ++{ ++ if ( member == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ; ++ return NULL ; ++ } ++ ++ conf_frame* cfr ; ++ ++ ast_mutex_lock(&member->lock); ++ ++ // ast_log( AST_CONF_DEBUG, "getting member frames, count => %d\n", member->outFramesCount ) ; ++ ++ if ( member->outVideoFramesCount > AST_CONF_MIN_QUEUE ) ++ { ++ cfr = member->outVideoFramesTail ; ++ ++ // if it's the only frame, reset the queu, ++ // else, move the second frame to the front ++ if ( member->outVideoFramesTail == member->outVideoFrames ) ++ { ++ member->outVideoFrames = NULL ; ++ member->outVideoFramesTail = NULL ; ++ } ++ else ++ { ++ // move the pointer to the next frame ++ member->outVideoFramesTail = member->outVideoFramesTail->prev ; ++ ++ // reset it's 'next' pointer ++ if ( member->outVideoFramesTail != NULL ) ++ member->outVideoFramesTail->next = NULL ; ++ } ++ ++ // separate the conf frame from the list ++ cfr->next = NULL ; ++ cfr->prev = NULL ; ++ ++ // decriment frame count ++ member->outVideoFramesCount-- ; ++ ast_mutex_unlock(&member->lock); ++ return cfr ; ++ } ++ ++ ast_mutex_unlock(&member->lock); ++ return NULL ; ++} ++ ++ ++ ++int queue_outgoing_video_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery ) ++{ ++ // check on frame ++ if ( fr == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to queue null frame\n" ) ; ++ return -1 ; ++ } ++ ++ // check on member ++ if ( member == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ; ++ return -1 ; ++ } ++ ++ ast_mutex_lock(&member->lock); ++ ++ // accounting: count the number of outgoing frames for this member ++ member->video_frames_out++ ; ++ ++ // ++ // we have to drop frames, so we'll drop new frames ++ // because it's easier ( and doesn't matter much anyway ). ++ // ++ if ( member->outVideoFramesCount >= AST_CONF_MAX_VIDEO_QUEUE) ++ { ++ ast_log( ++ AST_CONF_DEBUG, ++ "unable to queue outgoing VIDEO frame, channel => %s, incoming => %d, outgoing => %d\n", ++ member->channel_name, member->inVideoFramesCount, member->outVideoFramesCount ++ ) ; ++ ++ // accounting: count dropped outgoing frames ++ member->video_frames_out_dropped++ ; ++ ast_mutex_unlock(&member->lock); ++ return -1 ; ++ } ++ ++ // ++ // create new conf frame from passed data frame ++ // ++ ++ conf_frame* cfr = create_conf_frame( member, member->outVideoFrames, fr ) ; ++ ++ if ( cfr == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to create new conf frame\n" ) ; ++ ++ // accounting: count dropped outgoing frames ++ member->video_frames_out_dropped++ ; ++ ast_mutex_unlock(&member->lock); ++ return -1 ; ++ } ++ ++ // set delivery timestamp ++#ifdef VIDEO_SETTIMESTAMP ++ cfr->fr->delivery = delivery ; ++#else ++ cfr->fr->delivery.tv_sec = 0; ++ cfr->fr->delivery.tv_usec = 0; ++#endif ++ //ast_log (LOG_WARNING,"%d\n",cfr->fr->seqno); ++ ++#ifdef RTP_SEQNO_ZERO ++ cfr->fr->seqno = 0; ++#endif ++ ++ if ( member->outVideoFrames == NULL ) ++ { ++ // this is the first frame in the buffer ++ member->outVideoFramesTail = cfr ; ++ member->outVideoFrames = cfr ; ++ } ++ else ++ { ++ // put the new frame at the head of the list ++ member->outVideoFrames = cfr ; ++ } ++ ++ // increment member frame count ++ member->outVideoFramesCount++ ; ++ ++ ast_mutex_unlock(&member->lock); ++ ++ // return success ++ return 0 ; ++} ++ ++conf_frame* get_outgoing_dtmf_frame( struct ast_conf_member *member ) ++{ ++ if ( member == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ; ++ return NULL ; ++ } ++ ++ conf_frame* cfr ; ++ ++ // ast_log( AST_CONF_DEBUG, "getting member frames, count => %d\n", member->outFramesCount ) ; ++ ++ ast_mutex_lock(&member->lock); ++ ++ if ( member->outDTMFFramesCount > AST_CONF_MIN_QUEUE ) ++ { ++ cfr = member->outDTMFFramesTail ; ++ ++ // if it's the only frame, reset the queu, ++ // else, move the second frame to the front ++ if ( member->outDTMFFramesTail == member->outDTMFFrames ) ++ { ++ member->outDTMFFrames = NULL ; ++ member->outDTMFFramesTail = NULL ; ++ } ++ else ++ { ++ // move the pointer to the next frame ++ member->outDTMFFramesTail = member->outDTMFFramesTail->prev ; ++ ++ // reset it's 'next' pointer ++ if ( member->outDTMFFramesTail != NULL ) ++ member->outDTMFFramesTail->next = NULL ; ++ } ++ ++ // separate the conf frame from the list ++ cfr->next = NULL ; ++ cfr->prev = NULL ; ++ ++ // decriment frame count ++ member->outDTMFFramesCount-- ; ++ ast_mutex_unlock(&member->lock); ++ return cfr ; ++ } ++ ast_mutex_unlock(&member->lock); ++ return NULL ; ++} ++ ++conf_frame* get_outgoing_text_frame( struct ast_conf_member *member ) ++{ ++ if ( member == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ; ++ return NULL ; ++ } ++ ++ conf_frame* cfr ; ++ ++ // ast_log( AST_CONF_DEBUG, "getting member frames, count => %d\n", member->outFramesCount ) ; ++ ++ ast_mutex_lock(&member->lock); ++ ++ if ( member->outTextFramesCount > AST_CONF_MIN_QUEUE ) ++ { ++ cfr = member->outTextFramesTail ; ++ ++ // if it's the only frame, reset the queu, ++ // else, move the second frame to the front ++ if ( member->outTextFramesTail == member->outTextFrames ) ++ { ++ member->outTextFrames = NULL ; ++ member->outTextFramesTail = NULL ; ++ } ++ else ++ { ++ // move the pointer to the next frame ++ member->outTextFramesTail = member->outTextFramesTail->prev ; ++ ++ // reset it's 'next' pointer ++ if ( member->outTextFramesTail != NULL ) ++ member->outTextFramesTail->next = NULL ; ++ } ++ ++ // separate the conf frame from the list ++ cfr->next = NULL ; ++ cfr->prev = NULL ; ++ ++ // decriment frame count ++ member->outTextFramesCount-- ; ++ ast_mutex_unlock(&member->lock); ++ return cfr ; ++ } ++ ast_mutex_unlock(&member->lock); ++ return NULL ; ++} ++ ++ ++int queue_outgoing_dtmf_frame( struct ast_conf_member* member, const struct ast_frame* fr ) ++{ ++ // check on frame ++ if ( fr == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to queue null frame\n" ) ; ++ return -1 ; ++ } ++ ++ // check on member ++ if ( member == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ; ++ return -1 ; ++ } ++ ++ ast_mutex_lock(&member->lock); ++ ++ // accounting: count the number of outgoing frames for this member ++ member->dtmf_frames_out++ ; ++ ++ // ++ // we have to drop frames, so we'll drop new frames ++ // because it's easier ( and doesn't matter much anyway ). ++ // ++ if ( member->outDTMFFramesCount >= AST_CONF_MAX_DTMF_QUEUE) ++ { ++ ast_log( ++ AST_CONF_DEBUG, ++ "unable to queue outgoing DTMF frame, channel => %s, incoming => %d, outgoing => %d\n", ++ member->channel_name, member->inDTMFFramesCount, member->outDTMFFramesCount ++ ) ; ++ ++ // accounting: count dropped outgoing frames ++ member->dtmf_frames_out_dropped++ ; ++ ast_mutex_unlock(&member->lock); ++ return -1 ; ++ } ++ ++ // ++ // create new conf frame from passed data frame ++ // ++ ++ conf_frame* cfr = create_conf_frame( member, member->outDTMFFrames, fr ) ; ++ ++ if ( cfr == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to create new conf frame\n" ) ; ++ ++ // accounting: count dropped outgoing frames ++ member->dtmf_frames_out_dropped++ ; ++ ast_mutex_unlock(&member->lock); ++ return -1 ; ++ } ++ ++#ifdef RTP_SEQNO_ZERO ++ cfr->fr->seqno = 0; ++#endif ++ ++ if ( member->outDTMFFrames == NULL ) ++ { ++ // this is the first frame in the buffer ++ member->outDTMFFramesTail = cfr ; ++ member->outDTMFFrames = cfr ; ++ } ++ else ++ { ++ // put the new frame at the head of the list ++ member->outDTMFFrames = cfr ; ++ } ++ ++ // increment member frame count ++ member->outDTMFFramesCount++ ; ++ ++ ast_mutex_unlock(&member->lock); ++ // return success ++ return 0 ; ++} ++ ++int queue_outgoing_text_frame( struct ast_conf_member* member, const struct ast_frame* fr) ++{ ++ // check on frame ++ if ( fr == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to queue null frame\n" ) ; ++ return -1 ; ++ } ++ ++ // check on member ++ if ( member == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ; ++ return -1 ; ++ } ++ ++ ast_mutex_lock(&member->lock); ++ ++ // accounting: count the number of outgoing frames for this member ++ member->text_frames_out++ ; ++ ++ // ++ // we have to drop frames, so we'll drop new frames ++ // because it's easier ( and doesn't matter much anyway ). ++ // ++ if ( member->outTextFramesCount >= AST_CONF_MAX_TEXT_QUEUE) ++ { ++ ast_log( ++ AST_CONF_DEBUG, ++ "unable to queue outgoing text frame, channel => %s, incoming => %d, outgoing => %d\n", ++ member->channel_name, member->inTextFramesCount, member->outTextFramesCount ++ ) ; ++ ++ // accounting: count dropped outgoing frames ++ member->text_frames_out_dropped++ ; ++ ast_mutex_unlock(&member->lock); ++ return -1 ; ++ } ++ ++ // ++ // create new conf frame from passed data frame ++ // ++ ++ conf_frame* cfr = create_conf_frame( member, member->outTextFrames, fr ) ; ++ ++ if ( cfr == NULL ) ++ { ++ ast_log( LOG_ERROR, "unable to create new conf frame\n" ) ; ++ ++ // accounting: count dropped outgoing frames ++ member->text_frames_out_dropped++ ; ++ ast_mutex_unlock(&member->lock); ++ return -1 ; ++ } ++ ++#ifdef RTP_SEQNO_ZERO ++ cfr->fr->seqno = 0; ++#endif ++ ++ if ( member->outTextFrames == NULL ) ++ { ++ // this is the first frame in the buffer ++ member->outTextFramesTail = cfr ; ++ member->outTextFrames = cfr ; ++ } ++ else ++ { ++ // put the new frame at the head of the list ++ member->outTextFrames = cfr ; ++ } ++ ++ // increment member frame count ++ member->outTextFramesCount++ ; ++ ++ ast_mutex_unlock(&member->lock); ++ // return success ++ return 0 ; ++} ++ ++ ++// ++// manager functions ++// ++ ++void send_state_change_notifications( struct ast_conf_member* member ) ++{ ++ // ast_log( AST_CONF_DEBUG, "sending state change notification\n" ) ; ++ ++ // loop through list of members, sending state changes ++ while ( member != NULL ) ++ { ++ // has the state changed since last time through this loop? ++ if ( member->speaking_state_notify ) ++ { ++ manager_event( ++ EVENT_FLAG_CALL, ++ "ConferenceState", ++ "Channel: %s\r\n" ++ "State: %s\r\n", ++ member->channel_name, ++ ( ( member->speaking_state == 1 ) ? "speaking" : "silent" ) ++ ) ; ++ ++ ast_log( AST_CONF_DEBUG, "member state changed, channel => %s, state => %d, incoming => %d, outgoing => %d\n", ++ member->channel_name, member->speaking_state, member->inFramesCount, member->outFramesCount ) ; ++ ++ member->speaking_state_notify = 0; ++ } ++ ++ // move the pointer to the next member ++ member = member->next ; ++ } ++ ++ return ; ++} ++ ++// ++// ast_packer, adapted from ast_smoother ++// pack multiple frames together into one packet on the wire. ++// ++ ++#define PACKER_SIZE 8000 ++#define PACKER_QUEUE 10 // store at most 10 complete packets in the queue ++ ++struct ast_packer { ++ int framesize; // number of frames per packet on the wire. ++ int size; ++ int packet_index; ++ int format; ++ int readdata; ++ int optimizablestream; ++ int flags; ++ float samplesperbyte; ++ struct ast_frame f; ++ struct timeval delivery; ++ char data[PACKER_SIZE]; ++ char framedata[PACKER_SIZE + AST_FRIENDLY_OFFSET]; ++ int samples; ++ int sample_queue[PACKER_QUEUE]; ++ int len_queue[PACKER_QUEUE]; ++ struct ast_frame *opt; ++ int len; ++}; ++ ++void ast_packer_reset(struct ast_packer *s, int framesize) ++{ ++ memset(s, 0, sizeof(struct ast_packer)); ++ s->framesize = framesize; ++ s->packet_index=0; ++ s->len=0; ++} ++ ++struct ast_packer *ast_packer_new(int framesize) ++{ ++ struct ast_packer *s; ++ if (framesize < 1) ++ return NULL; ++ s = malloc(sizeof(struct ast_packer)); ++ if (s) ++ ast_packer_reset(s, framesize); ++ return s; ++} ++ ++int ast_packer_get_flags(struct ast_packer *s) ++{ ++ return s->flags; ++} ++ ++void ast_packer_set_flags(struct ast_packer *s, int flags) ++{ ++ s->flags = flags; ++} ++ ++int ast_packer_feed(struct ast_packer *s, const struct ast_frame *f) ++{ ++ if (f->frametype != AST_FRAME_VOICE) { ++ ast_log(LOG_WARNING, "Huh? Can't pack a non-voice frame!\n"); ++ return -1; ++ } ++ if (!s->format) { ++ s->format = f->subclass; ++ s->samples=0; ++ } else if (s->format != f->subclass) { ++ ast_log(LOG_WARNING, "Packer was working on %d format frames, now trying to feed %d?\n", s->format, f->subclass); ++ return -1; ++ } ++ if (s->len + f->datalen > PACKER_SIZE) { ++ ast_log(LOG_WARNING, "Out of packer space\n"); ++ return -1; ++ } ++ if (s->packet_index >= PACKER_QUEUE ){ ++ ast_log(LOG_WARNING, "Out of packer queue space\n"); ++ return -1; ++ } ++ ++ memcpy(s->data + s->len, f->data, f->datalen); ++ /* If either side is empty, reset the delivery time */ ++ if (!s->len || (!f->delivery.tv_sec && !f->delivery.tv_usec) || ++ (!s->delivery.tv_sec && !s->delivery.tv_usec)) ++ s->delivery = f->delivery; ++ s->len += f->datalen; ++//packer stuff ++ s->len_queue[s->packet_index] += f->datalen; ++ s->sample_queue[s->packet_index] += f->samples; ++ s->samples += f->samples; ++ ++ if (s->samples > s->framesize ) ++ ++s->packet_index; ++ ++ return 0; ++} ++ ++struct ast_frame *ast_packer_read(struct ast_packer *s) ++{ ++ struct ast_frame *opt; ++ int len; ++ /* IF we have an optimization frame, send it */ ++ if (s->opt) { ++ opt = s->opt; ++ s->opt = NULL; ++ return opt; ++ } ++ ++ /* Make sure we have enough data */ ++ if (s->samples < s->framesize ){ ++ return NULL; ++ } ++ len = s->len_queue[0]; ++ if (len > s->len) ++ len = s->len; ++ /* Make frame */ ++ s->f.frametype = AST_FRAME_VOICE; ++ s->f.subclass = s->format; ++ s->f.data = s->framedata + AST_FRIENDLY_OFFSET; ++ s->f.offset = AST_FRIENDLY_OFFSET; ++ s->f.datalen = len; ++ s->f.samples = s->sample_queue[0]; ++ s->f.delivery = s->delivery; ++ /* Fill Data */ ++ memcpy(s->f.data, s->data, len); ++ s->len -= len; ++ /* Move remaining data to the front if applicable */ ++ if (s->len) { ++ /* In principle this should all be fine because if we are sending ++ G.729 VAD, the next timestamp will take over anyawy */ ++ memmove(s->data, s->data + len, s->len); ++ if (s->delivery.tv_sec || s->delivery.tv_usec) { ++ /* If we have delivery time, increment it, otherwise, leave it at 0 */ ++ s->delivery.tv_sec += s->sample_queue[0] / 8000.0; ++ s->delivery.tv_usec += (((int)(s->sample_queue[0])) % 8000) * 125; ++ if (s->delivery.tv_usec > 1000000) { ++ s->delivery.tv_usec -= 1000000; ++ s->delivery.tv_sec += 1; ++ } ++ } ++ } ++ int j; ++ s->samples -= s->sample_queue[0]; ++ if( s->packet_index > 0 ){ ++ for (j=0; jpacket_index -1 ; j++){ ++ s->len_queue[j]=s->len_queue[j+1]; ++ s->sample_queue[j]=s->sample_queue[j+1]; ++ } ++ s->len_queue[s->packet_index]=0; ++ s->sample_queue[s->packet_index]=0; ++ s->packet_index--; ++ } else { ++ s->len_queue[0]=0; ++ s->sample_queue[0]=0; ++ } ++ ++ ++ /* Return frame */ ++ return &s->f; ++} ++ ++void ast_packer_free(struct ast_packer *s) ++{ ++ free(s); ++} ++ ++int queue_frame_for_listener( ++ struct ast_conference* conf, ++ struct ast_conf_member* member, ++ conf_frame* frame ++) ++{ ++ // ++ // check inputs ++ // ++ ++ if ( conf == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to queue listener frame with null conference\n" ) ; ++ return -1 ; ++ } ++ ++ if ( member == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to queue listener frame with null member\n" ) ; ++ return -1 ; ++ } ++ ++ // ++ // loop over spoken frames looking for member's appropriate match ++ // ++ ++ short found_flag = 0 ; ++ struct ast_frame* qf ; ++ ++ for ( ; frame != NULL ; frame = frame->next ) ++ { ++ // we're looking for a null or matching member ++ if ( frame->member != NULL && frame->member != member ) ++ continue ; ++ ++ if ( frame->fr == NULL ) ++ { ++ ast_log( LOG_WARNING, "unknown error queueing frame for listener, frame->fr == NULL\n" ) ; ++ continue ; ++ } ++ ++ // first, try for a pre-converted frame ++ qf = frame->converted[ member->write_format_index ] ; ++ ++ // convert ( and store ) the frame ++ if ( qf == NULL ) ++ { ++ // make a copy of the slinear version of the frame ++ qf = ast_frdup( frame->fr ) ; ++ ++ if ( qf == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to duplicate frame\n" ) ; ++ continue ; ++ } ++ ++ // convert using the conference's translation path ++ qf = convert_frame_from_slinear( conf->from_slinear_paths[ member->write_format_index ], qf ) ; ++ ++ // store the converted frame ++ // ( the frame will be free'd next time through the loop ) ++ frame->converted[ member->write_format_index ] = qf ; ++ } ++ ++ if ( qf != NULL ) ++ { ++ // duplicate the frame before queue'ing it ++ // ( since this member doesn't own this _shared_ frame ) ++ // qf = ast_frdup( qf ) ; ++ ++ ++ ++ if ( queue_outgoing_frame( member, qf, conf->delivery_time ) != 0 ) ++ { ++ // free the new frame if it couldn't be queue'd ++ // XXX NEILS - WOULD BE FREED IN CLEANUPast_frfree( qf ) ; ++ //qf = NULL ; ++ } ++ } ++ else ++ { ++ ast_log( LOG_WARNING, "unable to translate outgoing listener frame, channel => %s\n", member->channel_name ) ; ++ } ++ ++ // set found flag ++ found_flag = 1 ; ++ ++ // break from for loop ++ break ; ++ } ++ ++ // queue a silent frame ++ if ( found_flag == 0 ) ++ queue_silent_frame( conf, member ) ; ++ ++ return 0 ; ++} ++ ++ ++int queue_frame_for_speaker( ++ struct ast_conference* conf, ++ struct ast_conf_member* member, ++ conf_frame* frame ++) ++{ ++ // ++ // check inputs ++ // ++ ++ if ( conf == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to queue speaker frame with null conference\n" ) ; ++ return -1 ; ++ } ++ ++ if ( member == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to queue speaker frame with null member\n" ) ; ++ return -1 ; ++ } ++ ++ // ++ // loop over spoken frames looking for member's appropriate match ++ // ++ ++ short found_flag = 0 ; ++ struct ast_frame* qf ; ++ ++ for ( ; frame != NULL ; frame = frame->next ) ++ { ++ if ( frame->member != member ) ++ { ++ continue ; ++ } ++ ++ if ( frame->fr == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to queue speaker frame with null data\n" ) ; ++ continue ; ++ } ++ ++ // ++ // convert and queue frame ++ // ++ ++ // short-cut pointer to the ast_frame ++ qf = frame->fr ; ++ ++ if ( qf->subclass == member->write_format ) ++ { ++ // frame is already in correct format, so just queue it ++ ++ queue_outgoing_frame( member, qf, conf->delivery_time ) ; ++ } ++ else ++ { ++ // ++ // convert frame to member's write format ++ // ( calling ast_frdup() to make sure the translator's copy sticks around ) ++ // ++ qf = convert_frame_from_slinear( member->from_slinear, ast_frdup( qf ) ) ; ++ ++ if ( qf != NULL ) ++ { ++ // queue frame ++ queue_outgoing_frame( member, qf, conf->delivery_time ) ; ++ ++ // free frame ( the translator's copy ) ++ ast_frfree( qf ) ; ++ } ++ else ++ { ++ ast_log( LOG_WARNING, "unable to translate outgoing speaker frame, channel => %s\n", member->channel_name ) ; ++ } ++ } ++ ++ // set found flag ++ found_flag = 1 ; ++ ++ // we found the frame, skip to the next member ++ break ; ++ } ++ ++ // queue a silent frame ++ if ( found_flag == 0 ) ++ queue_silent_frame( conf, member ) ; ++ ++ return 0 ; ++} ++ ++ ++int queue_silent_frame( ++ struct ast_conference* conf, ++ struct ast_conf_member* member ++) ++{ ++ int c; ++#ifdef APP_CONFERENCE_DEBUG ++ // ++ // check inputs ++ // ++ ++ if ( conf == NULL ) ++ { ++ ast_log( AST_CONF_DEBUG, "unable to queue silent frame for null conference\n" ) ; ++ return -1 ; ++ } ++ ++ if ( member == NULL ) ++ { ++ ast_log( AST_CONF_DEBUG, "unable to queue silent frame for null member\n" ) ; ++ return -1 ; ++ } ++#endif // APP_CONFERENCE_DEBUG ++ ++ // ++ // initialize static variables ++ // ++ ++ static conf_frame* silent_frame = NULL ; ++ static struct ast_frame* qf = NULL ; ++ ++ if ( silent_frame == NULL ) ++ { ++ if ( ( silent_frame = get_silent_frame() ) == NULL ) ++ { ++ ast_log( LOG_WARNING, "unable to initialize static silent frame\n" ) ; ++ return -1 ; ++ } ++ } ++ ++ ++ // get the appropriate silent frame ++ qf = silent_frame->converted[ member->write_format_index ] ; ++ ++ if ( qf == NULL ) ++ { ++ // ++ // we need to do this to avoid echo on the speaker's line. ++ // translators seem to be single-purpose, i.e. they ++ // can't be used simultaneously for multiple audio streams ++ // ++ ++ struct ast_trans_pvt* trans = ast_translator_build_path( member->write_format, AST_FORMAT_SLINEAR ) ; ++ ++ if ( trans != NULL ) ++ { ++ // attempt ( five times ) to get a silent frame ++ // to make sure we provice the translator with enough data ++ for ( c = 0 ; c < 5 ; ++c ) ++ { ++ // translate the frame ++ qf = ast_translate( trans, silent_frame->fr, 0 ) ; ++ ++ // break if we get a frame ++ if ( qf != NULL ) break ; ++ } ++ ++ if ( qf != NULL ) ++ { ++ // isolate the frame so we can keep it around after trans is free'd ++ qf = ast_frisolate( qf ) ; ++ ++ // cache the new, isolated frame ++ silent_frame->converted[ member->write_format_index ] = qf ; ++ } ++ ++ ast_translator_free_path( trans ) ; ++ } ++ } ++ ++ // ++ // queue the frame, if it's not null, ++ // otherwise there was an error ++ // ++ if ( qf != NULL ) ++ { ++ queue_outgoing_frame( member, qf, conf->delivery_time ) ; ++ } ++ else ++ { ++ ast_log( LOG_ERROR, "unable to translate outgoing silent frame, channel => %s\n", member->channel_name ) ; ++ } ++ ++ return 0 ; ++} ++ ++ ++ ++void member_process_outgoing_frames(struct ast_conference* conf, ++ struct ast_conf_member *member, ++ struct conf_frame *send_frames) ++{ ++ ast_mutex_lock(&member->lock); ++ ++ // skip members that are not ready ++ if ( member->ready_for_outgoing == 0 ) ++ { ++ ast_mutex_unlock(&member->lock); ++ return ; ++ } ++ ++ // skip no receive audio clients ++ if ( member->norecv_audio ) ++ { ++ ast_mutex_unlock(&member->lock); ++ return; ++ } ++ ++ if ( member->local_speaking_state == 0 ) ++ { ++ // queue listener frame ++ queue_frame_for_listener( conf, member, send_frames ) ; ++ } ++ else ++ { ++ // queue speaker frame ++ queue_frame_for_speaker( conf, member, send_frames ) ; ++ } ++ ast_mutex_unlock(&member->lock); ++} ++ ++// Functions that will increase and decrease speaker_count in a secure way, locking the member mutex if required ++// Will also set speaking_state flag. ++// Returns the previous speaking state ++int increment_speaker_count(struct ast_conf_member *member, int lock) ++{ ++ int old_state; ++ ++ if ( lock ) ++ ast_mutex_lock(&member->lock); ++ ++ old_state = member->speaking_state; ++ member->speaker_count++; ++ member->speaking_state = 1; ++ ++ ast_log(AST_CONF_DEBUG, "Increment speaker count: id=%d, count=%d\n", member->id, member->speaker_count); ++ ++ // If this is a state change, update the timestamp ++ if ( old_state == 0 ) ++ { ++ member->speaking_state_notify = 1; ++ member->last_state_change = ast_tvnow(); ++ } ++ ++ if ( lock ) ++ ast_mutex_unlock(&member->lock); ++ ++ return old_state; ++} ++ ++int decrement_speaker_count(struct ast_conf_member *member, int lock) ++{ ++ int old_state; ++ ++ if ( lock ) ++ ast_mutex_lock(&member->lock); ++ ++ old_state = member->speaking_state; ++ if ( member->speaker_count > 0 ) ++ member->speaker_count--; ++ if ( member->speaker_count == 0 ) ++ member->speaking_state = 0; ++ ++ ast_log(AST_CONF_DEBUG, "Decrement speaker count: id=%d, count=%d\n", member->id, member->speaker_count); ++ ++ // If this is a state change, update the timestamp ++ if ( old_state == 1 && member->speaking_state == 0 ) ++ { ++ member->speaking_state_notify = 1; ++ member->last_state_change = ast_tvnow(); ++ } ++ ++ if ( lock ) ++ ast_mutex_unlock(&member->lock); ++ ++ return old_state; ++} ++ ++void member_process_spoken_frames(struct ast_conference* conf, ++ struct ast_conf_member *member, ++ struct conf_frame **spoken_frames, ++ long time_diff, ++ int *listener_count, ++ int *speaker_count ++ ) ++{ ++ struct conf_frame *cfr; ++ ++ // acquire member mutex ++ TIMELOG(ast_mutex_lock( &member->lock ),1,"conf thread member lock") ; ++ ++ // check for dead members ++ if ( member->remove_flag == 1 ) ++ { ++ // If this member is the default video source for the conference, then change the default to -1 ++ if ( member->id == conf->default_video_source_id ) ++ conf->default_video_source_id = -1; ++ ++ if (conf->debug_flag) ++ { ++ ast_log( LOG_NOTICE, "found member slated for removal, channel => %s\n", member->channel_name ) ; ++ } ++ remove_member( member, conf ) ; ++ return; ++ } ++ ++ // tell member the number of frames we're going to need ( used to help dropping algorithm ) ++ member->inFramesNeeded = ( time_diff / AST_CONF_FRAME_INTERVAL ) - 1 ; ++ ++ // !!! TESTING !!! ++ if ( ++ conf->debug_flag == 1 ++ && member->inFramesNeeded > 0 ++ ) ++ { ++ ast_log( AST_CONF_DEBUG, "channel => %s, inFramesNeeded => %d, inFramesCount => %d\n", ++ member->channel_name, member->inFramesNeeded, member->inFramesCount ) ; ++ } ++ ++ // non-listener member should have frames, ++ // unless silence detection dropped them ++ cfr = get_incoming_frame( member ) ; ++ ++ // handle retrieved frames ++ if ( cfr == NULL || cfr->fr == NULL ) ++ { ++ // Decrement speaker count for us and for driven members ++ // This happens only for the first missed frame, since we want to ++ // decrement only on state transitions ++ if ( member->local_speaking_state == 1 ) ++ { ++ decrement_speaker_count(member, 0); ++ member->local_speaking_state = 0; ++ // If we're driving another member, decrement its speaker count as well ++ if ( member->driven_member != NULL ) ++ decrement_speaker_count(member->driven_member, 1); ++ } ++ ++ // count the listeners ++ (*listener_count)++ ; ++ } ++ else ++ { ++ // append the frame to the list of spoken frames ++ if ( *spoken_frames != NULL ) ++ { ++ // add new frame to end of list ++ cfr->next = *spoken_frames ; ++ (*spoken_frames)->prev = cfr ; ++ } ++ ++ // point the list at the new frame ++ *spoken_frames = cfr ; ++ ++ // Increment speaker count for us and for driven members ++ // This happens only on the first received frame, since we want to ++ // increment only on state transitions ++ if ( member->local_speaking_state == 0 ) ++ { ++ increment_speaker_count(member, 0); ++ member->local_speaking_state = 1; ++ ++ // If we're driving another member, increment its speaker count as well ++ if ( member->driven_member != NULL ) ++ increment_speaker_count(member->driven_member, 1); ++ } ++ ++ // count the speakers ++ (*speaker_count)++ ; ++ } ++ ++ // release member mutex ++ ast_mutex_unlock( &member->lock ) ; ++ ++ return; ++} +diff --git a/apps/conference/member.h b/apps/conference/member.h +new file mode 100644 +index 0000000..f4cae3f +--- /dev/null ++++ b/apps/conference/member.h +@@ -0,0 +1,315 @@ ++ ++// $Id: member.h 872 2007-03-05 23:43:10Z sbalea $ ++ ++/* ++ * app_conference ++ * ++ * A channel independent conference application for Asterisk ++ * ++ * Copyright (C) 2002, 2003 Junghanns.NET GmbH ++ * Copyright (C) 2003, 2004 HorizonLive.com, Inc. ++ * Copyright (C) 2005, 2006 HorizonWimba, Inc. ++ * Copyright (C) 2007 Wimba, Inc. ++ * ++ * Klaus-Peter Junghanns ++ * ++ * Video Conferencing support added by ++ * Neil Stratford ++ * Copyright (C) 2005, 2005 Vipadia Limited ++ * ++ * VAD driven video conferencing, text message support ++ * and miscellaneous enhancements added by ++ * Mihai Balea ++ * ++ * This program may be modified and distributed under the ++ * terms of the GNU General Public License. You should have received ++ * a copy of the GNU General Public License along with this ++ * program; if not, write to the Free Software Foundation, Inc. ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _APP_CONF_MEMBER_H ++#define _APP_CONF_MEMBER_H ++ ++// ++// includes ++// ++ ++#include "app_conference.h" ++#include "common.h" ++ ++// ++// struct declarations ++// ++ ++struct ast_conf_soundq ++{ ++ char name[256]; ++ struct ast_filestream *stream; // the stream ++ int muted; // should incoming audio be muted while we play? ++ struct ast_conf_soundq *next; ++}; ++ ++struct ast_conf_member ++{ ++ ast_mutex_t lock ; // member data mutex ++ ++ struct ast_channel* chan ; // member's channel ++ char* channel_name ; // member's channel name ++ ++ // values passed to create_member () via *data ++ int priority ; // highest priority gets the channel ++ char* flags ; // raw member-type flags ++ char type ; // L = ListenOnly, M = Moderator, S = Standard (Listen/Talk) ++ char* conf_name ; // name of the conference that own this member ++ ++ char *callerid; ++ char *callername; ++ ++ // voice flags ++ int vad_flag; ++ int denoise_flag; ++ int agc_flag; ++ int via_telephone; ++ ++ // video conference params ++ int id; ++ int initial_id; ++ int req_id; ++ ++ // muting options - this member will not be heard/seen ++ int mute_audio; ++ int mute_video; ++ ++ // this member will not hear/see ++ int norecv_audio; ++ int norecv_video; ++ ++ // this member does not have a camera ++ int no_camera; ++ ++ // is this person a moderator? ++ int ismoderator; ++ ++ // determine by flags and channel name ++ char connection_type ; // T = telephone, X = iaxclient, S = sip ++ ++ // vad voice probability thresholds ++ float vad_prob_start ; ++ float vad_prob_continue ; ++ ++ // ready flag ++ short ready_for_outgoing ; ++ ++ // input frame queue ++ conf_frame* inFrames ; ++ conf_frame* inFramesTail ; ++ unsigned int inFramesCount ; ++ conf_frame* inVideoFrames ; ++ conf_frame* inVideoFramesTail ; ++ unsigned int inVideoFramesCount ; ++ conf_frame* inDTMFFrames ; ++ conf_frame* inDTMFFramesTail ; ++ unsigned int inDTMFFramesCount ; ++ conf_frame* inTextFrames ; ++ conf_frame* inTextFramesTail ; ++ unsigned int inTextFramesCount ; ++ ++ ++ // input/output smoother ++ struct ast_smoother *inSmoother; ++ struct ast_packer *outPacker; ++ int smooth_size_in; ++ int smooth_size_out; ++ int smooth_multiple; ++ ++ // frames needed by conference_exec ++ unsigned int inFramesNeeded ; ++ unsigned int inVideoFramesNeeded ; ++ ++ // used when caching last frame ++ conf_frame* inFramesLast ; ++ unsigned int inFramesRepeatLast ; ++ unsigned short okayToCacheLast ; ++ ++ // LL output frame queue ++ conf_frame* outFrames ; ++ conf_frame* outFramesTail ; ++ unsigned int outFramesCount ; ++ conf_frame* outVideoFrames ; ++ conf_frame* outVideoFramesTail ; ++ unsigned int outVideoFramesCount ; ++ conf_frame* outDTMFFrames ; ++ conf_frame* outDTMFFramesTail ; ++ unsigned int outDTMFFramesCount ; ++ conf_frame* outTextFrames ; ++ conf_frame* outTextFramesTail ; ++ unsigned int outTextFramesCount ; ++ ++ // LL video switched flag ++ short conference; ++ ++ // switch video by VAD? ++ short vad_switch; ++ // switch by dtmf? ++ short dtmf_switch; ++ // relay dtmf to manager? ++ short dtmf_relay; ++ // initial nat delay flag ++ short first_frame_received; ++ // does text messages? ++ short does_text; ++ ++ ++ // time we last dropped a frame ++ struct timeval last_in_dropped ; ++ struct timeval last_out_dropped ; ++ ++ // ( not currently used ) ++ // int samplesperframe ; ++ ++ // used for determining need to mix frames ++ // and for management interface notification ++ // and for VAD based video switching ++ short speaking_state_notify ; ++ short speaking_state ; // This flag will be true if this member or any of its drivers is speaking ++ short local_speaking_state; // This flag will be true only if this member is speaking ++ struct timeval last_state_change; ++ int speaker_count; // Number of drivers (including this member) that are speaking ++ ++ // pointer to next member in single-linked list ++ struct ast_conf_member* next ; ++ ++ // accounting values ++ unsigned long frames_in ; ++ unsigned long frames_in_dropped ; ++ unsigned long frames_out ; ++ unsigned long frames_out_dropped ; ++ ++ unsigned long video_frames_in ; ++ unsigned long video_frames_in_dropped ; ++ unsigned long video_frames_out ; ++ unsigned long video_frames_out_dropped ; ++ ++ unsigned long dtmf_frames_in ; ++ unsigned long dtmf_frames_in_dropped ; ++ unsigned long dtmf_frames_out ; ++ unsigned long dtmf_frames_out_dropped ; ++ ++ unsigned long text_frames_in ; ++ unsigned long text_frames_in_dropped ; ++ unsigned long text_frames_out ; ++ unsigned long text_frames_out_dropped ; ++ ++ // for counting sequentially dropped frames ++ unsigned int sequential_drops ; ++ unsigned long since_dropped ; ++ ++ // start time ++ struct timeval time_entered ; ++ struct timeval lastsent_timeval ; ++ ++ // flag indicating we should remove this member ++ short remove_flag ; ++ short kick_flag ; ++ ++#if ( SILDET == 2 ) ++ // pointer to speex preprocessor dsp ++ SpeexPreprocessState *dsp ; ++ // number of frames to ignore speex_preprocess() ++ int ignore_speex_count; ++#else ++ // placeholder when preprocessing is not enabled ++ void* dsp ; ++#endif ++ ++ // audio format this member is using ++ int write_format ; ++ int read_format ; ++ ++ int write_format_index ; ++ int read_format_index ; ++ ++ // member frame translators ++ struct ast_trans_pvt* to_slinear ; ++ struct ast_trans_pvt* from_slinear ; ++ ++ // For playing sounds ++ struct ast_conf_soundq *soundq; ++ struct ast_conf_soundq *videoq; ++ ++ // Enter/leave sounds ++ char * enter_snd; ++ char * leave_snd; ++ ++ // Pointer to another member that will be driven from this member's audio ++ struct ast_conf_member *driven_member; ++} ; ++ ++struct conf_member ++{ ++ struct ast_conf_member* realmember ; ++ struct conf_member* next ; ++} ; ++ ++// ++// function declarations ++// ++ ++int member_exec( struct ast_channel* chan, void* data ) ; ++ ++struct ast_conf_member* check_active_video( int id, struct ast_conference *conf ); ++ ++struct ast_conf_member* create_member( struct ast_channel* chan, const char* data ) ; ++struct ast_conf_member* delete_member( struct ast_conf_member* member ) ; ++ ++// incoming queue ++int queue_incoming_frame( struct ast_conf_member* member, struct ast_frame* fr ) ; ++int queue_incoming_video_frame( struct ast_conf_member* member, const struct ast_frame* fr ) ; ++int queue_incoming_dtmf_frame( struct ast_conf_member* member, const struct ast_frame* fr ) ; ++conf_frame* get_incoming_frame( struct ast_conf_member* member ) ; ++conf_frame* get_incoming_video_frame( struct ast_conf_member* member ) ; ++conf_frame* get_incoming_dtmf_frame( struct ast_conf_member* member ) ; ++ ++// outgoing queue ++int queue_outgoing_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery ) ; ++int __queue_outgoing_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery ) ; ++conf_frame* get_outgoing_frame( struct ast_conf_member* member ) ; ++ ++int queue_outgoing_video_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery ) ; ++conf_frame* get_outgoing_video_frame( struct ast_conf_member* member ) ; ++int queue_outgoing_dtmf_frame( struct ast_conf_member* member, const struct ast_frame* fr ) ; ++int queue_outgoing_text_frame( struct ast_conf_member* member, const struct ast_frame* fr ) ; ++conf_frame* get_outgoing_dtmf_frame( struct ast_conf_member* member ) ; ++conf_frame* get_outgoing_text_frame( struct ast_conf_member* member ) ; ++ ++void send_state_change_notifications( struct ast_conf_member* member ) ; ++ ++int increment_speaker_count(struct ast_conf_member *member, int lock); ++int decrement_speaker_count(struct ast_conf_member *member, int lock); ++ ++void member_process_spoken_frames(struct ast_conference* conf, ++ struct ast_conf_member *member, ++ struct conf_frame **spoken_frames, ++ long time_diff, ++ int *listener_count, ++ int *speaker_count); ++ ++void member_process_outgoing_frames(struct ast_conference* conf, ++ struct ast_conf_member *member, ++ struct conf_frame *send_frames); ++ ++// ++// packer functions ++// ++ ++struct ast_packer; ++ ++extern struct ast_packer *ast_packer_new(int bytes); ++extern void ast_packer_set_flags(struct ast_packer *packer, int flags); ++extern int ast_packer_get_flags(struct ast_packer *packer); ++extern void ast_packer_free(struct ast_packer *s); ++extern void ast_packer_reset(struct ast_packer *s, int bytes); ++extern int ast_packer_feed(struct ast_packer *s, const struct ast_frame *f); ++extern struct ast_frame *ast_packer_read(struct ast_packer *s); ++#endif +-- +1.5.3.7 + diff --git a/asterisk-1.4.16.2-autoconf.patch b/asterisk-1.4.16.2-autoconf.patch new file mode 100644 index 0000000..a109cc2 --- /dev/null +++ b/asterisk-1.4.16.2-autoconf.patch @@ -0,0 +1,1635 @@ +From 1cd952f0ade8f86dac7374a45f4fa9a02d08babb Mon Sep 17 00:00:00 2001 +From: Jeffrey C. Ollie +Date: Sun, 18 Nov 2007 22:30:31 -0600 +Subject: [PATCH] Update configure script. + +--- + configure | 1421 +++++++++++++++++++++++++++++++++++++- + include/asterisk/autoconfig.h.in | 9 + + 2 files changed, 1409 insertions(+), 21 deletions(-) + +diff --git a/configure b/configure +index 17d2677..86593f5 100755 +--- a/configure ++++ b/configure +@@ -1,5 +1,5 @@ + #! /bin/sh +-# From configure.ac Revision: 93180 . ++# From configure.ac Revision. + # Guess values for system-dependent variables and create Makefiles. + # Generated by GNU Autoconf 2.61 for asterisk 1.4. + # +@@ -722,10 +722,18 @@ ALSA_LIB + ALSA_INCLUDE + ALSA_DIR + PBX_ALSA ++BLUETOOTH_LIB ++BLUETOOTH_INCLUDE ++BLUETOOTH_DIR ++PBX_BLUETOOTH + CURL_LIB + CURL_INCLUDE + CURL_DIR + PBX_CURL ++CAP_LIB ++CAP_INCLUDE ++CAP_DIR ++PBX_CAP + CURSES_LIB + CURSES_INCLUDE + CURSES_DIR +@@ -822,6 +830,10 @@ RADIUS_LIB + RADIUS_INCLUDE + RADIUS_DIR + PBX_RADIUS ++SPANDSP_LIB ++SPANDSP_INCLUDE ++SPANDSP_DIR ++PBX_SPANDSP + SPEEX_LIB + SPEEX_INCLUDE + SPEEX_DIR +@@ -1507,7 +1519,9 @@ Optional Packages: + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-gnu-ld assume the C compiler uses GNU ld [default=no] + --with-asound=PATH use Advanced Linux Sound Architecture files in PATH ++ --with-bluetooth=PATH use Bluetooth Support files in PATH + --with-curl=PATH use cURL files in PATH ++ --with-cap=PATH use POSIX 1.e capabilities files in PATH + --with-curses=PATH use curses files in PATH + --with-gnutls=PATH use GNU TLS support (used for iksemel only) files in + PATH +@@ -1533,6 +1547,7 @@ Optional Packages: + --with-h323=PATH use OpenH323 files in PATH + --with-qt=PATH use Qt files in PATH + --with-radius=PATH use Radius Client files in PATH ++ --with-spandsp=PATH use spandsp Library files in PATH + --with-speex=PATH use Speex files in PATH + --with-sqlite=PATH use SQLite files in PATH + --with-suppserv=PATH use mISDN Supplemental Services files in PATH +@@ -7770,6 +7785,34 @@ PBX_ALSA=0 + + + ++BLUETOOTH_DESCRIP="Bluetooth Support" ++BLUETOOTH_OPTION="bluetooth" ++ ++# Check whether --with-bluetooth was given. ++if test "${with_bluetooth+set}" = set; then ++ withval=$with_bluetooth; ++case ${withval} in ++ n|no) ++ USE_BLUETOOTH=no ++ ;; ++ y|ye|yes) ++ BLUETOOTH_MANDATORY="yes" ++ ;; ++ *) ++ BLUETOOTH_DIR="${withval}" ++ BLUETOOTH_MANDATORY="yes" ++ ;; ++esac ++ ++fi ++ ++PBX_BLUETOOTH=0 ++ ++ ++ ++ ++ ++ + CURL_DESCRIP="cURL" + CURL_OPTION="curl" + +@@ -7798,6 +7841,34 @@ PBX_CURL=0 + + + ++CAP_DESCRIP="POSIX 1.e capabilities" ++CAP_OPTION="cap" ++ ++# Check whether --with-cap was given. ++if test "${with_cap+set}" = set; then ++ withval=$with_cap; ++case ${withval} in ++ n|no) ++ USE_CAP=no ++ ;; ++ y|ye|yes) ++ CAP_MANDATORY="yes" ++ ;; ++ *) ++ CAP_DIR="${withval}" ++ CAP_MANDATORY="yes" ++ ;; ++esac ++ ++fi ++ ++PBX_CAP=0 ++ ++ ++ ++ ++ ++ + CURSES_DESCRIP="curses" + CURSES_OPTION="curses" + +@@ -8470,6 +8541,34 @@ PBX_RADIUS=0 + + + ++SPANDSP_DESCRIP="spandsp Library" ++SPANDSP_OPTION="spandsp" ++ ++# Check whether --with-spandsp was given. ++if test "${with_spandsp+set}" = set; then ++ withval=$with_spandsp; ++case ${withval} in ++ n|no) ++ USE_SPANDSP=no ++ ;; ++ y|ye|yes) ++ SPANDSP_MANDATORY="yes" ++ ;; ++ *) ++ SPANDSP_DIR="${withval}" ++ SPANDSP_MANDATORY="yes" ++ ;; ++esac ++ ++fi ++ ++PBX_SPANDSP=0 ++ ++ ++ ++ ++ ++ + SPEEX_DESCRIP="Speex" + SPEEX_OPTION="speex" + +@@ -12060,13 +12159,11 @@ _ACEOF + cat confdefs.h >>conftest.$ac_ext + cat >>conftest.$ac_ext <<_ACEOF + /* end confdefs.h. */ +-#include /* for off_t */ +- #include ++#include + int + main () + { +-int (*fp) (FILE *, off_t, int) = fseeko; +- return fseeko (stdin, 0, 0) && fp (stdin, 0, 0); ++return fseeko (stdin, 0, 0) && (fseeko) (stdin, 0, 0); + ; + return 0; + } +@@ -12106,13 +12203,11 @@ cat confdefs.h >>conftest.$ac_ext + cat >>conftest.$ac_ext <<_ACEOF + /* end confdefs.h. */ + #define _LARGEFILE_SOURCE 1 +-#include /* for off_t */ +- #include ++#include + int + main () + { +-int (*fp) (FILE *, off_t, int) = fseeko; +- return fseeko (stdin, 0, 0) && fp (stdin, 0, 0); ++return fseeko (stdin, 0, 0) && (fseeko) (stdin, 0, 0); + ; + return 0; + } +@@ -16829,6 +16924,415 @@ fi + + + ++if test "${USE_BLUETOOTH}" != "no"; then ++ pbxlibdir="" ++ if test "x${BLUETOOTH_DIR}" != "x"; then ++ if test -d ${BLUETOOTH_DIR}/lib; then ++ pbxlibdir="-L${BLUETOOTH_DIR}/lib" ++ else ++ pbxlibdir="-L${BLUETOOTH_DIR}" ++ fi ++ fi ++ { echo "$as_me:$LINENO: checking for ba2str in -lbluetooth" >&5 ++echo $ECHO_N "checking for ba2str in -lbluetooth... $ECHO_C" >&6; } ++if test "${ac_cv_lib_bluetooth_ba2str+set}" = set; then ++ echo $ECHO_N "(cached) $ECHO_C" >&6 ++else ++ ac_check_lib_save_LIBS=$LIBS ++LIBS="-lbluetooth ${pbxlibdir} $LIBS" ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++ ++/* Override any GCC internal prototype to avoid an error. ++ Use char because int might match the return type of a GCC ++ builtin and then its argument prototype would still apply. */ ++#ifdef __cplusplus ++extern "C" ++#endif ++char ba2str (); ++int ++main () ++{ ++return ba2str (); ++ ; ++ return 0; ++} ++_ACEOF ++rm -f conftest.$ac_objext conftest$ac_exeext ++if { (ac_try="$ac_link" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 ++ (eval "$ac_link") 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } && { ++ test -z "$ac_c_werror_flag" || ++ test ! -s conftest.err ++ } && test -s conftest$ac_exeext && ++ $as_test_x conftest$ac_exeext; then ++ ac_cv_lib_bluetooth_ba2str=yes ++else ++ echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_cv_lib_bluetooth_ba2str=no ++fi ++ ++rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ ++ conftest$ac_exeext conftest.$ac_ext ++LIBS=$ac_check_lib_save_LIBS ++fi ++{ echo "$as_me:$LINENO: result: $ac_cv_lib_bluetooth_ba2str" >&5 ++echo "${ECHO_T}$ac_cv_lib_bluetooth_ba2str" >&6; } ++if test $ac_cv_lib_bluetooth_ba2str = yes; then ++ AST_BLUETOOTH_FOUND=yes ++else ++ AST_BLUETOOTH_FOUND=no ++fi ++ ++ ++ if test "${AST_BLUETOOTH_FOUND}" = "yes"; then ++ BLUETOOTH_LIB="-lbluetooth " ++ BLUETOOTH_HEADER_FOUND="1" ++ if test "x${BLUETOOTH_DIR}" != "x"; then ++ BLUETOOTH_LIB="${pbxlibdir} ${BLUETOOTH_LIB}" ++ BLUETOOTH_INCLUDE="-I${BLUETOOTH_DIR}/include" ++ saved_cppflags="${CPPFLAGS}" ++ CPPFLAGS="${CPPFLAGS} -I${BLUETOOTH_DIR}/include" ++ if test "xbluetooth/bluetooth.h" != "x" ; then ++ as_ac_Header=`echo "ac_cv_header_${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h" | $as_tr_sh` ++if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then ++ { echo "$as_me:$LINENO: checking for ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h" >&5 ++echo $ECHO_N "checking for ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h... $ECHO_C" >&6; } ++if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then ++ echo $ECHO_N "(cached) $ECHO_C" >&6 ++fi ++ac_res=`eval echo '${'$as_ac_Header'}'` ++ { echo "$as_me:$LINENO: result: $ac_res" >&5 ++echo "${ECHO_T}$ac_res" >&6; } ++else ++ # Is the header compilable? ++{ echo "$as_me:$LINENO: checking ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h usability" >&5 ++echo $ECHO_N "checking ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h usability... $ECHO_C" >&6; } ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++$ac_includes_default ++#include <${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h> ++_ACEOF ++rm -f conftest.$ac_objext ++if { (ac_try="$ac_compile" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 ++ (eval "$ac_compile") 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } && { ++ test -z "$ac_c_werror_flag" || ++ test ! -s conftest.err ++ } && test -s conftest.$ac_objext; then ++ ac_header_compiler=yes ++else ++ echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_header_compiler=no ++fi ++ ++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ++{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 ++echo "${ECHO_T}$ac_header_compiler" >&6; } ++ ++# Is the header present? ++{ echo "$as_me:$LINENO: checking ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h presence" >&5 ++echo $ECHO_N "checking ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h presence... $ECHO_C" >&6; } ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++#include <${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h> ++_ACEOF ++if { (ac_try="$ac_cpp conftest.$ac_ext" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 ++ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } >/dev/null && { ++ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || ++ test ! -s conftest.err ++ }; then ++ ac_header_preproc=yes ++else ++ echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_header_preproc=no ++fi ++ ++rm -f conftest.err conftest.$ac_ext ++{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 ++echo "${ECHO_T}$ac_header_preproc" >&6; } ++ ++# So? What about this header? ++case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in ++ yes:no: ) ++ { echo "$as_me:$LINENO: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: accepted by the compiler, rejected by the preprocessor!" >&5 ++echo "$as_me: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: accepted by the compiler, rejected by the preprocessor!" >&2;} ++ { echo "$as_me:$LINENO: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: proceeding with the compiler's result" >&5 ++echo "$as_me: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: proceeding with the compiler's result" >&2;} ++ ac_header_preproc=yes ++ ;; ++ no:yes:* ) ++ { echo "$as_me:$LINENO: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: present but cannot be compiled" >&5 ++echo "$as_me: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: present but cannot be compiled" >&2;} ++ { echo "$as_me:$LINENO: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: check for missing prerequisite headers?" >&5 ++echo "$as_me: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: check for missing prerequisite headers?" >&2;} ++ { echo "$as_me:$LINENO: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: see the Autoconf documentation" >&5 ++echo "$as_me: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: see the Autoconf documentation" >&2;} ++ { echo "$as_me:$LINENO: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: section \"Present But Cannot Be Compiled\"" >&5 ++echo "$as_me: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: section \"Present But Cannot Be Compiled\"" >&2;} ++ { echo "$as_me:$LINENO: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: proceeding with the preprocessor's result" >&5 ++echo "$as_me: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: proceeding with the preprocessor's result" >&2;} ++ { echo "$as_me:$LINENO: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: in the future, the compiler will take precedence" >&5 ++echo "$as_me: WARNING: ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h: in the future, the compiler will take precedence" >&2;} ++ ( cat <<\_ASBOX ++## ------------------------------- ## ++## Report this to www.asterisk.org ## ++## ------------------------------- ## ++_ASBOX ++ ) | sed "s/^/$as_me: WARNING: /" >&2 ++ ;; ++esac ++{ echo "$as_me:$LINENO: checking for ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h" >&5 ++echo $ECHO_N "checking for ${BLUETOOTH_DIR}/include/bluetooth/bluetooth.h... $ECHO_C" >&6; } ++if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then ++ echo $ECHO_N "(cached) $ECHO_C" >&6 ++else ++ eval "$as_ac_Header=\$ac_header_preproc" ++fi ++ac_res=`eval echo '${'$as_ac_Header'}'` ++ { echo "$as_me:$LINENO: result: $ac_res" >&5 ++echo "${ECHO_T}$ac_res" >&6; } ++ ++fi ++if test `eval echo '${'$as_ac_Header'}'` = yes; then ++ BLUETOOTH_HEADER_FOUND=1 ++else ++ BLUETOOTH_HEADER_FOUND=0 ++fi ++ ++ ++ fi ++ CPPFLAGS="${saved_cppflags}" ++ else ++ if test "xbluetooth/bluetooth.h" != "x" ; then ++ if test "${ac_cv_header_bluetooth_bluetooth_h+set}" = set; then ++ { echo "$as_me:$LINENO: checking for bluetooth/bluetooth.h" >&5 ++echo $ECHO_N "checking for bluetooth/bluetooth.h... $ECHO_C" >&6; } ++if test "${ac_cv_header_bluetooth_bluetooth_h+set}" = set; then ++ echo $ECHO_N "(cached) $ECHO_C" >&6 ++fi ++{ echo "$as_me:$LINENO: result: $ac_cv_header_bluetooth_bluetooth_h" >&5 ++echo "${ECHO_T}$ac_cv_header_bluetooth_bluetooth_h" >&6; } ++else ++ # Is the header compilable? ++{ echo "$as_me:$LINENO: checking bluetooth/bluetooth.h usability" >&5 ++echo $ECHO_N "checking bluetooth/bluetooth.h usability... $ECHO_C" >&6; } ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++$ac_includes_default ++#include ++_ACEOF ++rm -f conftest.$ac_objext ++if { (ac_try="$ac_compile" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 ++ (eval "$ac_compile") 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } && { ++ test -z "$ac_c_werror_flag" || ++ test ! -s conftest.err ++ } && test -s conftest.$ac_objext; then ++ ac_header_compiler=yes ++else ++ echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_header_compiler=no ++fi ++ ++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ++{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 ++echo "${ECHO_T}$ac_header_compiler" >&6; } ++ ++# Is the header present? ++{ echo "$as_me:$LINENO: checking bluetooth/bluetooth.h presence" >&5 ++echo $ECHO_N "checking bluetooth/bluetooth.h presence... $ECHO_C" >&6; } ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++#include ++_ACEOF ++if { (ac_try="$ac_cpp conftest.$ac_ext" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 ++ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } >/dev/null && { ++ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || ++ test ! -s conftest.err ++ }; then ++ ac_header_preproc=yes ++else ++ echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_header_preproc=no ++fi ++ ++rm -f conftest.err conftest.$ac_ext ++{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 ++echo "${ECHO_T}$ac_header_preproc" >&6; } ++ ++# So? What about this header? ++case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in ++ yes:no: ) ++ { echo "$as_me:$LINENO: WARNING: bluetooth/bluetooth.h: accepted by the compiler, rejected by the preprocessor!" >&5 ++echo "$as_me: WARNING: bluetooth/bluetooth.h: accepted by the compiler, rejected by the preprocessor!" >&2;} ++ { echo "$as_me:$LINENO: WARNING: bluetooth/bluetooth.h: proceeding with the compiler's result" >&5 ++echo "$as_me: WARNING: bluetooth/bluetooth.h: proceeding with the compiler's result" >&2;} ++ ac_header_preproc=yes ++ ;; ++ no:yes:* ) ++ { echo "$as_me:$LINENO: WARNING: bluetooth/bluetooth.h: present but cannot be compiled" >&5 ++echo "$as_me: WARNING: bluetooth/bluetooth.h: present but cannot be compiled" >&2;} ++ { echo "$as_me:$LINENO: WARNING: bluetooth/bluetooth.h: check for missing prerequisite headers?" >&5 ++echo "$as_me: WARNING: bluetooth/bluetooth.h: check for missing prerequisite headers?" >&2;} ++ { echo "$as_me:$LINENO: WARNING: bluetooth/bluetooth.h: see the Autoconf documentation" >&5 ++echo "$as_me: WARNING: bluetooth/bluetooth.h: see the Autoconf documentation" >&2;} ++ { echo "$as_me:$LINENO: WARNING: bluetooth/bluetooth.h: section \"Present But Cannot Be Compiled\"" >&5 ++echo "$as_me: WARNING: bluetooth/bluetooth.h: section \"Present But Cannot Be Compiled\"" >&2;} ++ { echo "$as_me:$LINENO: WARNING: bluetooth/bluetooth.h: proceeding with the preprocessor's result" >&5 ++echo "$as_me: WARNING: bluetooth/bluetooth.h: proceeding with the preprocessor's result" >&2;} ++ { echo "$as_me:$LINENO: WARNING: bluetooth/bluetooth.h: in the future, the compiler will take precedence" >&5 ++echo "$as_me: WARNING: bluetooth/bluetooth.h: in the future, the compiler will take precedence" >&2;} ++ ( cat <<\_ASBOX ++## ------------------------------- ## ++## Report this to www.asterisk.org ## ++## ------------------------------- ## ++_ASBOX ++ ) | sed "s/^/$as_me: WARNING: /" >&2 ++ ;; ++esac ++{ echo "$as_me:$LINENO: checking for bluetooth/bluetooth.h" >&5 ++echo $ECHO_N "checking for bluetooth/bluetooth.h... $ECHO_C" >&6; } ++if test "${ac_cv_header_bluetooth_bluetooth_h+set}" = set; then ++ echo $ECHO_N "(cached) $ECHO_C" >&6 ++else ++ ac_cv_header_bluetooth_bluetooth_h=$ac_header_preproc ++fi ++{ echo "$as_me:$LINENO: result: $ac_cv_header_bluetooth_bluetooth_h" >&5 ++echo "${ECHO_T}$ac_cv_header_bluetooth_bluetooth_h" >&6; } ++ ++fi ++if test $ac_cv_header_bluetooth_bluetooth_h = yes; then ++ BLUETOOTH_HEADER_FOUND=1 ++else ++ BLUETOOTH_HEADER_FOUND=0 ++fi ++ ++ ++ fi ++ fi ++ if test "x${BLUETOOTH_HEADER_FOUND}" = "x0" ; then ++ if test -n "${BLUETOOTH_MANDATORY}" ; ++ then ++ { echo "$as_me:$LINENO: ***" >&5 ++echo "$as_me: ***" >&6;} ++ { echo "$as_me:$LINENO: *** It appears that you do not have the bluetooth development package installed." >&5 ++echo "$as_me: *** It appears that you do not have the bluetooth development package installed." >&6;} ++ { echo "$as_me:$LINENO: *** Please install it to include ${BLUETOOTH_DESCRIP} support, or re-run configure" >&5 ++echo "$as_me: *** Please install it to include ${BLUETOOTH_DESCRIP} support, or re-run configure" >&6;} ++ { echo "$as_me:$LINENO: *** without explicitly specifying --with-${BLUETOOTH_OPTION}" >&5 ++echo "$as_me: *** without explicitly specifying --with-${BLUETOOTH_OPTION}" >&6;} ++ exit 1 ++ fi ++ BLUETOOTH_LIB="" ++ BLUETOOTH_INCLUDE="" ++ PBX_BLUETOOTH=0 ++ else ++ PBX_BLUETOOTH=1 ++ ++cat >>confdefs.h <<_ACEOF ++#define HAVE_BLUETOOTH 1 ++_ACEOF ++ ++ fi ++ elif test -n "${BLUETOOTH_MANDATORY}"; ++ then ++ { echo "$as_me:$LINENO: ***" >&5 ++echo "$as_me: ***" >&6;} ++ { echo "$as_me:$LINENO: *** The ${BLUETOOTH_DESCRIP} installation on this system appears to be broken." >&5 ++echo "$as_me: *** The ${BLUETOOTH_DESCRIP} installation on this system appears to be broken." >&6;} ++ { echo "$as_me:$LINENO: *** Either correct the installation, or run configure" >&5 ++echo "$as_me: *** Either correct the installation, or run configure" >&6;} ++ { echo "$as_me:$LINENO: *** without explicitly specifying --with-${BLUETOOTH_OPTION}" >&5 ++echo "$as_me: *** without explicitly specifying --with-${BLUETOOTH_OPTION}" >&6;} ++ exit 1 ++ fi ++fi ++ ++ ++ + if test "${USE_CURSES}" != "no"; then + pbxlibdir="" + if test "x${CURSES_DIR}" != "x"; then +@@ -17237,6 +17741,417 @@ echo "$as_me: *** without explicitly specifying --with-${CURSES_OPTION}" >&6;} + fi + + ++if test "x${host_os}" = "xlinux-gnu" ; then ++ ++if test "${USE_CAP}" != "no"; then ++ pbxlibdir="" ++ if test "x${CAP_DIR}" != "x"; then ++ if test -d ${CAP_DIR}/lib; then ++ pbxlibdir="-L${CAP_DIR}/lib" ++ else ++ pbxlibdir="-L${CAP_DIR}" ++ fi ++ fi ++ { echo "$as_me:$LINENO: checking for cap_from_text in -lcap" >&5 ++echo $ECHO_N "checking for cap_from_text in -lcap... $ECHO_C" >&6; } ++if test "${ac_cv_lib_cap_cap_from_text+set}" = set; then ++ echo $ECHO_N "(cached) $ECHO_C" >&6 ++else ++ ac_check_lib_save_LIBS=$LIBS ++LIBS="-lcap ${pbxlibdir} $LIBS" ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++ ++/* Override any GCC internal prototype to avoid an error. ++ Use char because int might match the return type of a GCC ++ builtin and then its argument prototype would still apply. */ ++#ifdef __cplusplus ++extern "C" ++#endif ++char cap_from_text (); ++int ++main () ++{ ++return cap_from_text (); ++ ; ++ return 0; ++} ++_ACEOF ++rm -f conftest.$ac_objext conftest$ac_exeext ++if { (ac_try="$ac_link" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 ++ (eval "$ac_link") 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } && { ++ test -z "$ac_c_werror_flag" || ++ test ! -s conftest.err ++ } && test -s conftest$ac_exeext && ++ $as_test_x conftest$ac_exeext; then ++ ac_cv_lib_cap_cap_from_text=yes ++else ++ echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_cv_lib_cap_cap_from_text=no ++fi ++ ++rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ ++ conftest$ac_exeext conftest.$ac_ext ++LIBS=$ac_check_lib_save_LIBS ++fi ++{ echo "$as_me:$LINENO: result: $ac_cv_lib_cap_cap_from_text" >&5 ++echo "${ECHO_T}$ac_cv_lib_cap_cap_from_text" >&6; } ++if test $ac_cv_lib_cap_cap_from_text = yes; then ++ AST_CAP_FOUND=yes ++else ++ AST_CAP_FOUND=no ++fi ++ ++ ++ if test "${AST_CAP_FOUND}" = "yes"; then ++ CAP_LIB="-lcap " ++ CAP_HEADER_FOUND="1" ++ if test "x${CAP_DIR}" != "x"; then ++ CAP_LIB="${pbxlibdir} ${CAP_LIB}" ++ CAP_INCLUDE="-I${CAP_DIR}/include" ++ saved_cppflags="${CPPFLAGS}" ++ CPPFLAGS="${CPPFLAGS} -I${CAP_DIR}/include" ++ if test "xsys/capability.h" != "x" ; then ++ as_ac_Header=`echo "ac_cv_header_${CAP_DIR}/include/sys/capability.h" | $as_tr_sh` ++if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then ++ { echo "$as_me:$LINENO: checking for ${CAP_DIR}/include/sys/capability.h" >&5 ++echo $ECHO_N "checking for ${CAP_DIR}/include/sys/capability.h... $ECHO_C" >&6; } ++if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then ++ echo $ECHO_N "(cached) $ECHO_C" >&6 ++fi ++ac_res=`eval echo '${'$as_ac_Header'}'` ++ { echo "$as_me:$LINENO: result: $ac_res" >&5 ++echo "${ECHO_T}$ac_res" >&6; } ++else ++ # Is the header compilable? ++{ echo "$as_me:$LINENO: checking ${CAP_DIR}/include/sys/capability.h usability" >&5 ++echo $ECHO_N "checking ${CAP_DIR}/include/sys/capability.h usability... $ECHO_C" >&6; } ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++$ac_includes_default ++#include <${CAP_DIR}/include/sys/capability.h> ++_ACEOF ++rm -f conftest.$ac_objext ++if { (ac_try="$ac_compile" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 ++ (eval "$ac_compile") 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } && { ++ test -z "$ac_c_werror_flag" || ++ test ! -s conftest.err ++ } && test -s conftest.$ac_objext; then ++ ac_header_compiler=yes ++else ++ echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_header_compiler=no ++fi ++ ++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ++{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 ++echo "${ECHO_T}$ac_header_compiler" >&6; } ++ ++# Is the header present? ++{ echo "$as_me:$LINENO: checking ${CAP_DIR}/include/sys/capability.h presence" >&5 ++echo $ECHO_N "checking ${CAP_DIR}/include/sys/capability.h presence... $ECHO_C" >&6; } ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++#include <${CAP_DIR}/include/sys/capability.h> ++_ACEOF ++if { (ac_try="$ac_cpp conftest.$ac_ext" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 ++ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } >/dev/null && { ++ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || ++ test ! -s conftest.err ++ }; then ++ ac_header_preproc=yes ++else ++ echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_header_preproc=no ++fi ++ ++rm -f conftest.err conftest.$ac_ext ++{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 ++echo "${ECHO_T}$ac_header_preproc" >&6; } ++ ++# So? What about this header? ++case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in ++ yes:no: ) ++ { echo "$as_me:$LINENO: WARNING: ${CAP_DIR}/include/sys/capability.h: accepted by the compiler, rejected by the preprocessor!" >&5 ++echo "$as_me: WARNING: ${CAP_DIR}/include/sys/capability.h: accepted by the compiler, rejected by the preprocessor!" >&2;} ++ { echo "$as_me:$LINENO: WARNING: ${CAP_DIR}/include/sys/capability.h: proceeding with the compiler's result" >&5 ++echo "$as_me: WARNING: ${CAP_DIR}/include/sys/capability.h: proceeding with the compiler's result" >&2;} ++ ac_header_preproc=yes ++ ;; ++ no:yes:* ) ++ { echo "$as_me:$LINENO: WARNING: ${CAP_DIR}/include/sys/capability.h: present but cannot be compiled" >&5 ++echo "$as_me: WARNING: ${CAP_DIR}/include/sys/capability.h: present but cannot be compiled" >&2;} ++ { echo "$as_me:$LINENO: WARNING: ${CAP_DIR}/include/sys/capability.h: check for missing prerequisite headers?" >&5 ++echo "$as_me: WARNING: ${CAP_DIR}/include/sys/capability.h: check for missing prerequisite headers?" >&2;} ++ { echo "$as_me:$LINENO: WARNING: ${CAP_DIR}/include/sys/capability.h: see the Autoconf documentation" >&5 ++echo "$as_me: WARNING: ${CAP_DIR}/include/sys/capability.h: see the Autoconf documentation" >&2;} ++ { echo "$as_me:$LINENO: WARNING: ${CAP_DIR}/include/sys/capability.h: section \"Present But Cannot Be Compiled\"" >&5 ++echo "$as_me: WARNING: ${CAP_DIR}/include/sys/capability.h: section \"Present But Cannot Be Compiled\"" >&2;} ++ { echo "$as_me:$LINENO: WARNING: ${CAP_DIR}/include/sys/capability.h: proceeding with the preprocessor's result" >&5 ++echo "$as_me: WARNING: ${CAP_DIR}/include/sys/capability.h: proceeding with the preprocessor's result" >&2;} ++ { echo "$as_me:$LINENO: WARNING: ${CAP_DIR}/include/sys/capability.h: in the future, the compiler will take precedence" >&5 ++echo "$as_me: WARNING: ${CAP_DIR}/include/sys/capability.h: in the future, the compiler will take precedence" >&2;} ++ ( cat <<\_ASBOX ++## ------------------------------- ## ++## Report this to www.asterisk.org ## ++## ------------------------------- ## ++_ASBOX ++ ) | sed "s/^/$as_me: WARNING: /" >&2 ++ ;; ++esac ++{ echo "$as_me:$LINENO: checking for ${CAP_DIR}/include/sys/capability.h" >&5 ++echo $ECHO_N "checking for ${CAP_DIR}/include/sys/capability.h... $ECHO_C" >&6; } ++if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then ++ echo $ECHO_N "(cached) $ECHO_C" >&6 ++else ++ eval "$as_ac_Header=\$ac_header_preproc" ++fi ++ac_res=`eval echo '${'$as_ac_Header'}'` ++ { echo "$as_me:$LINENO: result: $ac_res" >&5 ++echo "${ECHO_T}$ac_res" >&6; } ++ ++fi ++if test `eval echo '${'$as_ac_Header'}'` = yes; then ++ CAP_HEADER_FOUND=1 ++else ++ CAP_HEADER_FOUND=0 ++fi ++ ++ ++ fi ++ CPPFLAGS="${saved_cppflags}" ++ else ++ if test "xsys/capability.h" != "x" ; then ++ if test "${ac_cv_header_sys_capability_h+set}" = set; then ++ { echo "$as_me:$LINENO: checking for sys/capability.h" >&5 ++echo $ECHO_N "checking for sys/capability.h... $ECHO_C" >&6; } ++if test "${ac_cv_header_sys_capability_h+set}" = set; then ++ echo $ECHO_N "(cached) $ECHO_C" >&6 ++fi ++{ echo "$as_me:$LINENO: result: $ac_cv_header_sys_capability_h" >&5 ++echo "${ECHO_T}$ac_cv_header_sys_capability_h" >&6; } ++else ++ # Is the header compilable? ++{ echo "$as_me:$LINENO: checking sys/capability.h usability" >&5 ++echo $ECHO_N "checking sys/capability.h usability... $ECHO_C" >&6; } ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++$ac_includes_default ++#include ++_ACEOF ++rm -f conftest.$ac_objext ++if { (ac_try="$ac_compile" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 ++ (eval "$ac_compile") 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } && { ++ test -z "$ac_c_werror_flag" || ++ test ! -s conftest.err ++ } && test -s conftest.$ac_objext; then ++ ac_header_compiler=yes ++else ++ echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_header_compiler=no ++fi ++ ++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ++{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 ++echo "${ECHO_T}$ac_header_compiler" >&6; } ++ ++# Is the header present? ++{ echo "$as_me:$LINENO: checking sys/capability.h presence" >&5 ++echo $ECHO_N "checking sys/capability.h presence... $ECHO_C" >&6; } ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++#include ++_ACEOF ++if { (ac_try="$ac_cpp conftest.$ac_ext" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 ++ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } >/dev/null && { ++ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || ++ test ! -s conftest.err ++ }; then ++ ac_header_preproc=yes ++else ++ echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_header_preproc=no ++fi ++ ++rm -f conftest.err conftest.$ac_ext ++{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 ++echo "${ECHO_T}$ac_header_preproc" >&6; } ++ ++# So? What about this header? ++case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in ++ yes:no: ) ++ { echo "$as_me:$LINENO: WARNING: sys/capability.h: accepted by the compiler, rejected by the preprocessor!" >&5 ++echo "$as_me: WARNING: sys/capability.h: accepted by the compiler, rejected by the preprocessor!" >&2;} ++ { echo "$as_me:$LINENO: WARNING: sys/capability.h: proceeding with the compiler's result" >&5 ++echo "$as_me: WARNING: sys/capability.h: proceeding with the compiler's result" >&2;} ++ ac_header_preproc=yes ++ ;; ++ no:yes:* ) ++ { echo "$as_me:$LINENO: WARNING: sys/capability.h: present but cannot be compiled" >&5 ++echo "$as_me: WARNING: sys/capability.h: present but cannot be compiled" >&2;} ++ { echo "$as_me:$LINENO: WARNING: sys/capability.h: check for missing prerequisite headers?" >&5 ++echo "$as_me: WARNING: sys/capability.h: check for missing prerequisite headers?" >&2;} ++ { echo "$as_me:$LINENO: WARNING: sys/capability.h: see the Autoconf documentation" >&5 ++echo "$as_me: WARNING: sys/capability.h: see the Autoconf documentation" >&2;} ++ { echo "$as_me:$LINENO: WARNING: sys/capability.h: section \"Present But Cannot Be Compiled\"" >&5 ++echo "$as_me: WARNING: sys/capability.h: section \"Present But Cannot Be Compiled\"" >&2;} ++ { echo "$as_me:$LINENO: WARNING: sys/capability.h: proceeding with the preprocessor's result" >&5 ++echo "$as_me: WARNING: sys/capability.h: proceeding with the preprocessor's result" >&2;} ++ { echo "$as_me:$LINENO: WARNING: sys/capability.h: in the future, the compiler will take precedence" >&5 ++echo "$as_me: WARNING: sys/capability.h: in the future, the compiler will take precedence" >&2;} ++ ( cat <<\_ASBOX ++## ------------------------------- ## ++## Report this to www.asterisk.org ## ++## ------------------------------- ## ++_ASBOX ++ ) | sed "s/^/$as_me: WARNING: /" >&2 ++ ;; ++esac ++{ echo "$as_me:$LINENO: checking for sys/capability.h" >&5 ++echo $ECHO_N "checking for sys/capability.h... $ECHO_C" >&6; } ++if test "${ac_cv_header_sys_capability_h+set}" = set; then ++ echo $ECHO_N "(cached) $ECHO_C" >&6 ++else ++ ac_cv_header_sys_capability_h=$ac_header_preproc ++fi ++{ echo "$as_me:$LINENO: result: $ac_cv_header_sys_capability_h" >&5 ++echo "${ECHO_T}$ac_cv_header_sys_capability_h" >&6; } ++ ++fi ++if test $ac_cv_header_sys_capability_h = yes; then ++ CAP_HEADER_FOUND=1 ++else ++ CAP_HEADER_FOUND=0 ++fi ++ ++ ++ fi ++ fi ++ if test "x${CAP_HEADER_FOUND}" = "x0" ; then ++ if test -n "${CAP_MANDATORY}" ; ++ then ++ { echo "$as_me:$LINENO: ***" >&5 ++echo "$as_me: ***" >&6;} ++ { echo "$as_me:$LINENO: *** It appears that you do not have the cap development package installed." >&5 ++echo "$as_me: *** It appears that you do not have the cap development package installed." >&6;} ++ { echo "$as_me:$LINENO: *** Please install it to include ${CAP_DESCRIP} support, or re-run configure" >&5 ++echo "$as_me: *** Please install it to include ${CAP_DESCRIP} support, or re-run configure" >&6;} ++ { echo "$as_me:$LINENO: *** without explicitly specifying --with-${CAP_OPTION}" >&5 ++echo "$as_me: *** without explicitly specifying --with-${CAP_OPTION}" >&6;} ++ exit 1 ++ fi ++ CAP_LIB="" ++ CAP_INCLUDE="" ++ PBX_CAP=0 ++ else ++ PBX_CAP=1 ++ ++cat >>confdefs.h <<_ACEOF ++#define HAVE_CAP 1 ++_ACEOF ++ ++ fi ++ elif test -n "${CAP_MANDATORY}"; ++ then ++ { echo "$as_me:$LINENO: ***" >&5 ++echo "$as_me: ***" >&6;} ++ { echo "$as_me:$LINENO: *** The ${CAP_DESCRIP} installation on this system appears to be broken." >&5 ++echo "$as_me: *** The ${CAP_DESCRIP} installation on this system appears to be broken." >&6;} ++ { echo "$as_me:$LINENO: *** Either correct the installation, or run configure" >&5 ++echo "$as_me: *** Either correct the installation, or run configure" >&6;} ++ { echo "$as_me:$LINENO: *** without explicitly specifying --with-${CAP_OPTION}" >&5 ++echo "$as_me: *** without explicitly specifying --with-${CAP_OPTION}" >&6;} ++ exit 1 ++ fi ++fi ++ ++fi ++ + GSM_INTERNAL="yes" + + GSM_SYSTEM="yes" +@@ -27990,6 +28905,415 @@ fi + + + ++if test "${USE_SPANDSP}" != "no"; then ++ pbxlibdir="" ++ if test "x${SPANDSP_DIR}" != "x"; then ++ if test -d ${SPANDSP_DIR}/lib; then ++ pbxlibdir="-L${SPANDSP_DIR}/lib" ++ else ++ pbxlibdir="-L${SPANDSP_DIR}" ++ fi ++ fi ++ { echo "$as_me:$LINENO: checking for fax_init in -lspandsp" >&5 ++echo $ECHO_N "checking for fax_init in -lspandsp... $ECHO_C" >&6; } ++if test "${ac_cv_lib_spandsp_fax_init+set}" = set; then ++ echo $ECHO_N "(cached) $ECHO_C" >&6 ++else ++ ac_check_lib_save_LIBS=$LIBS ++LIBS="-lspandsp ${pbxlibdir} -ltiff $LIBS" ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++ ++/* Override any GCC internal prototype to avoid an error. ++ Use char because int might match the return type of a GCC ++ builtin and then its argument prototype would still apply. */ ++#ifdef __cplusplus ++extern "C" ++#endif ++char fax_init (); ++int ++main () ++{ ++return fax_init (); ++ ; ++ return 0; ++} ++_ACEOF ++rm -f conftest.$ac_objext conftest$ac_exeext ++if { (ac_try="$ac_link" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 ++ (eval "$ac_link") 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } && { ++ test -z "$ac_c_werror_flag" || ++ test ! -s conftest.err ++ } && test -s conftest$ac_exeext && ++ $as_test_x conftest$ac_exeext; then ++ ac_cv_lib_spandsp_fax_init=yes ++else ++ echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_cv_lib_spandsp_fax_init=no ++fi ++ ++rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ ++ conftest$ac_exeext conftest.$ac_ext ++LIBS=$ac_check_lib_save_LIBS ++fi ++{ echo "$as_me:$LINENO: result: $ac_cv_lib_spandsp_fax_init" >&5 ++echo "${ECHO_T}$ac_cv_lib_spandsp_fax_init" >&6; } ++if test $ac_cv_lib_spandsp_fax_init = yes; then ++ AST_SPANDSP_FOUND=yes ++else ++ AST_SPANDSP_FOUND=no ++fi ++ ++ ++ if test "${AST_SPANDSP_FOUND}" = "yes"; then ++ SPANDSP_LIB="-lspandsp -ltiff" ++ SPANDSP_HEADER_FOUND="1" ++ if test "x${SPANDSP_DIR}" != "x"; then ++ SPANDSP_LIB="${pbxlibdir} ${SPANDSP_LIB}" ++ SPANDSP_INCLUDE="-I${SPANDSP_DIR}/include" ++ saved_cppflags="${CPPFLAGS}" ++ CPPFLAGS="${CPPFLAGS} -I${SPANDSP_DIR}/include" ++ if test "xspandsp.h" != "x" ; then ++ as_ac_Header=`echo "ac_cv_header_${SPANDSP_DIR}/include/spandsp.h" | $as_tr_sh` ++if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then ++ { echo "$as_me:$LINENO: checking for ${SPANDSP_DIR}/include/spandsp.h" >&5 ++echo $ECHO_N "checking for ${SPANDSP_DIR}/include/spandsp.h... $ECHO_C" >&6; } ++if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then ++ echo $ECHO_N "(cached) $ECHO_C" >&6 ++fi ++ac_res=`eval echo '${'$as_ac_Header'}'` ++ { echo "$as_me:$LINENO: result: $ac_res" >&5 ++echo "${ECHO_T}$ac_res" >&6; } ++else ++ # Is the header compilable? ++{ echo "$as_me:$LINENO: checking ${SPANDSP_DIR}/include/spandsp.h usability" >&5 ++echo $ECHO_N "checking ${SPANDSP_DIR}/include/spandsp.h usability... $ECHO_C" >&6; } ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++$ac_includes_default ++#include <${SPANDSP_DIR}/include/spandsp.h> ++_ACEOF ++rm -f conftest.$ac_objext ++if { (ac_try="$ac_compile" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 ++ (eval "$ac_compile") 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } && { ++ test -z "$ac_c_werror_flag" || ++ test ! -s conftest.err ++ } && test -s conftest.$ac_objext; then ++ ac_header_compiler=yes ++else ++ echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_header_compiler=no ++fi ++ ++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ++{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 ++echo "${ECHO_T}$ac_header_compiler" >&6; } ++ ++# Is the header present? ++{ echo "$as_me:$LINENO: checking ${SPANDSP_DIR}/include/spandsp.h presence" >&5 ++echo $ECHO_N "checking ${SPANDSP_DIR}/include/spandsp.h presence... $ECHO_C" >&6; } ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++#include <${SPANDSP_DIR}/include/spandsp.h> ++_ACEOF ++if { (ac_try="$ac_cpp conftest.$ac_ext" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 ++ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } >/dev/null && { ++ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || ++ test ! -s conftest.err ++ }; then ++ ac_header_preproc=yes ++else ++ echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_header_preproc=no ++fi ++ ++rm -f conftest.err conftest.$ac_ext ++{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 ++echo "${ECHO_T}$ac_header_preproc" >&6; } ++ ++# So? What about this header? ++case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in ++ yes:no: ) ++ { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: accepted by the compiler, rejected by the preprocessor!" >&5 ++echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: accepted by the compiler, rejected by the preprocessor!" >&2;} ++ { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: proceeding with the compiler's result" >&5 ++echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: proceeding with the compiler's result" >&2;} ++ ac_header_preproc=yes ++ ;; ++ no:yes:* ) ++ { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: present but cannot be compiled" >&5 ++echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: present but cannot be compiled" >&2;} ++ { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: check for missing prerequisite headers?" >&5 ++echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: check for missing prerequisite headers?" >&2;} ++ { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: see the Autoconf documentation" >&5 ++echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: see the Autoconf documentation" >&2;} ++ { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: section \"Present But Cannot Be Compiled\"" >&5 ++echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: section \"Present But Cannot Be Compiled\"" >&2;} ++ { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: proceeding with the preprocessor's result" >&5 ++echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: proceeding with the preprocessor's result" >&2;} ++ { echo "$as_me:$LINENO: WARNING: ${SPANDSP_DIR}/include/spandsp.h: in the future, the compiler will take precedence" >&5 ++echo "$as_me: WARNING: ${SPANDSP_DIR}/include/spandsp.h: in the future, the compiler will take precedence" >&2;} ++ ( cat <<\_ASBOX ++## ------------------------------- ## ++## Report this to www.asterisk.org ## ++## ------------------------------- ## ++_ASBOX ++ ) | sed "s/^/$as_me: WARNING: /" >&2 ++ ;; ++esac ++{ echo "$as_me:$LINENO: checking for ${SPANDSP_DIR}/include/spandsp.h" >&5 ++echo $ECHO_N "checking for ${SPANDSP_DIR}/include/spandsp.h... $ECHO_C" >&6; } ++if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then ++ echo $ECHO_N "(cached) $ECHO_C" >&6 ++else ++ eval "$as_ac_Header=\$ac_header_preproc" ++fi ++ac_res=`eval echo '${'$as_ac_Header'}'` ++ { echo "$as_me:$LINENO: result: $ac_res" >&5 ++echo "${ECHO_T}$ac_res" >&6; } ++ ++fi ++if test `eval echo '${'$as_ac_Header'}'` = yes; then ++ SPANDSP_HEADER_FOUND=1 ++else ++ SPANDSP_HEADER_FOUND=0 ++fi ++ ++ ++ fi ++ CPPFLAGS="${saved_cppflags}" ++ else ++ if test "xspandsp.h" != "x" ; then ++ if test "${ac_cv_header_spandsp_h+set}" = set; then ++ { echo "$as_me:$LINENO: checking for spandsp.h" >&5 ++echo $ECHO_N "checking for spandsp.h... $ECHO_C" >&6; } ++if test "${ac_cv_header_spandsp_h+set}" = set; then ++ echo $ECHO_N "(cached) $ECHO_C" >&6 ++fi ++{ echo "$as_me:$LINENO: result: $ac_cv_header_spandsp_h" >&5 ++echo "${ECHO_T}$ac_cv_header_spandsp_h" >&6; } ++else ++ # Is the header compilable? ++{ echo "$as_me:$LINENO: checking spandsp.h usability" >&5 ++echo $ECHO_N "checking spandsp.h usability... $ECHO_C" >&6; } ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++$ac_includes_default ++#include ++_ACEOF ++rm -f conftest.$ac_objext ++if { (ac_try="$ac_compile" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 ++ (eval "$ac_compile") 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } && { ++ test -z "$ac_c_werror_flag" || ++ test ! -s conftest.err ++ } && test -s conftest.$ac_objext; then ++ ac_header_compiler=yes ++else ++ echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_header_compiler=no ++fi ++ ++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ++{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 ++echo "${ECHO_T}$ac_header_compiler" >&6; } ++ ++# Is the header present? ++{ echo "$as_me:$LINENO: checking spandsp.h presence" >&5 ++echo $ECHO_N "checking spandsp.h presence... $ECHO_C" >&6; } ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++#include ++_ACEOF ++if { (ac_try="$ac_cpp conftest.$ac_ext" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 ++ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } >/dev/null && { ++ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || ++ test ! -s conftest.err ++ }; then ++ ac_header_preproc=yes ++else ++ echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_header_preproc=no ++fi ++ ++rm -f conftest.err conftest.$ac_ext ++{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 ++echo "${ECHO_T}$ac_header_preproc" >&6; } ++ ++# So? What about this header? ++case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in ++ yes:no: ) ++ { echo "$as_me:$LINENO: WARNING: spandsp.h: accepted by the compiler, rejected by the preprocessor!" >&5 ++echo "$as_me: WARNING: spandsp.h: accepted by the compiler, rejected by the preprocessor!" >&2;} ++ { echo "$as_me:$LINENO: WARNING: spandsp.h: proceeding with the compiler's result" >&5 ++echo "$as_me: WARNING: spandsp.h: proceeding with the compiler's result" >&2;} ++ ac_header_preproc=yes ++ ;; ++ no:yes:* ) ++ { echo "$as_me:$LINENO: WARNING: spandsp.h: present but cannot be compiled" >&5 ++echo "$as_me: WARNING: spandsp.h: present but cannot be compiled" >&2;} ++ { echo "$as_me:$LINENO: WARNING: spandsp.h: check for missing prerequisite headers?" >&5 ++echo "$as_me: WARNING: spandsp.h: check for missing prerequisite headers?" >&2;} ++ { echo "$as_me:$LINENO: WARNING: spandsp.h: see the Autoconf documentation" >&5 ++echo "$as_me: WARNING: spandsp.h: see the Autoconf documentation" >&2;} ++ { echo "$as_me:$LINENO: WARNING: spandsp.h: section \"Present But Cannot Be Compiled\"" >&5 ++echo "$as_me: WARNING: spandsp.h: section \"Present But Cannot Be Compiled\"" >&2;} ++ { echo "$as_me:$LINENO: WARNING: spandsp.h: proceeding with the preprocessor's result" >&5 ++echo "$as_me: WARNING: spandsp.h: proceeding with the preprocessor's result" >&2;} ++ { echo "$as_me:$LINENO: WARNING: spandsp.h: in the future, the compiler will take precedence" >&5 ++echo "$as_me: WARNING: spandsp.h: in the future, the compiler will take precedence" >&2;} ++ ( cat <<\_ASBOX ++## ------------------------------- ## ++## Report this to www.asterisk.org ## ++## ------------------------------- ## ++_ASBOX ++ ) | sed "s/^/$as_me: WARNING: /" >&2 ++ ;; ++esac ++{ echo "$as_me:$LINENO: checking for spandsp.h" >&5 ++echo $ECHO_N "checking for spandsp.h... $ECHO_C" >&6; } ++if test "${ac_cv_header_spandsp_h+set}" = set; then ++ echo $ECHO_N "(cached) $ECHO_C" >&6 ++else ++ ac_cv_header_spandsp_h=$ac_header_preproc ++fi ++{ echo "$as_me:$LINENO: result: $ac_cv_header_spandsp_h" >&5 ++echo "${ECHO_T}$ac_cv_header_spandsp_h" >&6; } ++ ++fi ++if test $ac_cv_header_spandsp_h = yes; then ++ SPANDSP_HEADER_FOUND=1 ++else ++ SPANDSP_HEADER_FOUND=0 ++fi ++ ++ ++ fi ++ fi ++ if test "x${SPANDSP_HEADER_FOUND}" = "x0" ; then ++ if test -n "${SPANDSP_MANDATORY}" ; ++ then ++ { echo "$as_me:$LINENO: ***" >&5 ++echo "$as_me: ***" >&6;} ++ { echo "$as_me:$LINENO: *** It appears that you do not have the spandsp development package installed." >&5 ++echo "$as_me: *** It appears that you do not have the spandsp development package installed." >&6;} ++ { echo "$as_me:$LINENO: *** Please install it to include ${SPANDSP_DESCRIP} support, or re-run configure" >&5 ++echo "$as_me: *** Please install it to include ${SPANDSP_DESCRIP} support, or re-run configure" >&6;} ++ { echo "$as_me:$LINENO: *** without explicitly specifying --with-${SPANDSP_OPTION}" >&5 ++echo "$as_me: *** without explicitly specifying --with-${SPANDSP_OPTION}" >&6;} ++ exit 1 ++ fi ++ SPANDSP_LIB="" ++ SPANDSP_INCLUDE="" ++ PBX_SPANDSP=0 ++ else ++ PBX_SPANDSP=1 ++ ++cat >>confdefs.h <<_ACEOF ++#define HAVE_SPANDSP 1 ++_ACEOF ++ ++ fi ++ elif test -n "${SPANDSP_MANDATORY}"; ++ then ++ { echo "$as_me:$LINENO: ***" >&5 ++echo "$as_me: ***" >&6;} ++ { echo "$as_me:$LINENO: *** The ${SPANDSP_DESCRIP} installation on this system appears to be broken." >&5 ++echo "$as_me: *** The ${SPANDSP_DESCRIP} installation on this system appears to be broken." >&6;} ++ { echo "$as_me:$LINENO: *** Either correct the installation, or run configure" >&5 ++echo "$as_me: *** Either correct the installation, or run configure" >&6;} ++ { echo "$as_me:$LINENO: *** without explicitly specifying --with-${SPANDSP_OPTION}" >&5 ++echo "$as_me: *** without explicitly specifying --with-${SPANDSP_OPTION}" >&6;} ++ exit 1 ++ fi ++fi ++ ++ ++ + if test "${USE_SPEEX}" != "no"; then + pbxlibdir="" + if test "x${SPEEX_DIR}" != "x"; then +@@ -34341,10 +35665,18 @@ ALSA_LIB!$ALSA_LIB$ac_delim + ALSA_INCLUDE!$ALSA_INCLUDE$ac_delim + ALSA_DIR!$ALSA_DIR$ac_delim + PBX_ALSA!$PBX_ALSA$ac_delim ++BLUETOOTH_LIB!$BLUETOOTH_LIB$ac_delim ++BLUETOOTH_INCLUDE!$BLUETOOTH_INCLUDE$ac_delim ++BLUETOOTH_DIR!$BLUETOOTH_DIR$ac_delim ++PBX_BLUETOOTH!$PBX_BLUETOOTH$ac_delim + CURL_LIB!$CURL_LIB$ac_delim + CURL_INCLUDE!$CURL_INCLUDE$ac_delim + CURL_DIR!$CURL_DIR$ac_delim + PBX_CURL!$PBX_CURL$ac_delim ++CAP_LIB!$CAP_LIB$ac_delim ++CAP_INCLUDE!$CAP_INCLUDE$ac_delim ++CAP_DIR!$CAP_DIR$ac_delim ++PBX_CAP!$PBX_CAP$ac_delim + CURSES_LIB!$CURSES_LIB$ac_delim + CURSES_INCLUDE!$CURSES_INCLUDE$ac_delim + CURSES_DIR!$CURSES_DIR$ac_delim +@@ -34425,14 +35757,6 @@ PRI_LIB!$PRI_LIB$ac_delim + PRI_INCLUDE!$PRI_INCLUDE$ac_delim + PRI_DIR!$PRI_DIR$ac_delim + PBX_PRI!$PBX_PRI$ac_delim +-PWLIB_LIB!$PWLIB_LIB$ac_delim +-PWLIB_INCLUDE!$PWLIB_INCLUDE$ac_delim +-PWLIB_DIR!$PWLIB_DIR$ac_delim +-PBX_PWLIB!$PBX_PWLIB$ac_delim +-OPENH323_LIB!$OPENH323_LIB$ac_delim +-OPENH323_INCLUDE!$OPENH323_INCLUDE$ac_delim +-OPENH323_DIR!$OPENH323_DIR$ac_delim +-PBX_OPENH323!$PBX_OPENH323$ac_delim + _ACEOF + + if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then +@@ -34474,6 +35798,14 @@ _ACEOF + ac_delim='%!_!# ' + for ac_last_try in false false false false false :; do + cat >conf$$subs.sed <<_ACEOF ++PWLIB_LIB!$PWLIB_LIB$ac_delim ++PWLIB_INCLUDE!$PWLIB_INCLUDE$ac_delim ++PWLIB_DIR!$PWLIB_DIR$ac_delim ++PBX_PWLIB!$PBX_PWLIB$ac_delim ++OPENH323_LIB!$OPENH323_LIB$ac_delim ++OPENH323_INCLUDE!$OPENH323_INCLUDE$ac_delim ++OPENH323_DIR!$OPENH323_DIR$ac_delim ++PBX_OPENH323!$PBX_OPENH323$ac_delim + QT_LIB!$QT_LIB$ac_delim + QT_INCLUDE!$QT_INCLUDE$ac_delim + QT_DIR!$QT_DIR$ac_delim +@@ -34482,6 +35814,10 @@ RADIUS_LIB!$RADIUS_LIB$ac_delim + RADIUS_INCLUDE!$RADIUS_INCLUDE$ac_delim + RADIUS_DIR!$RADIUS_DIR$ac_delim + PBX_RADIUS!$PBX_RADIUS$ac_delim ++SPANDSP_LIB!$SPANDSP_LIB$ac_delim ++SPANDSP_INCLUDE!$SPANDSP_INCLUDE$ac_delim ++SPANDSP_DIR!$SPANDSP_DIR$ac_delim ++PBX_SPANDSP!$PBX_SPANDSP$ac_delim + SPEEX_LIB!$SPEEX_LIB$ac_delim + SPEEX_INCLUDE!$SPEEX_INCLUDE$ac_delim + SPEEX_DIR!$SPEEX_DIR$ac_delim +@@ -34559,6 +35895,47 @@ QTMOC!$QTMOC$ac_delim + PBX_ZAPTEL_VLDTMF!$PBX_ZAPTEL_VLDTMF$ac_delim + PBX_ZAPTEL_TRANSCODE!$PBX_ZAPTEL_TRANSCODE$ac_delim + EDITLINE_LIB!$EDITLINE_LIB$ac_delim ++_ACEOF ++ ++ if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then ++ break ++ elif $ac_last_try; then ++ { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 ++echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} ++ { (exit 1); exit 1; }; } ++ else ++ ac_delim="$ac_delim!$ac_delim _$ac_delim!! " ++ fi ++done ++ ++ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed` ++if test -n "$ac_eof"; then ++ ac_eof=`echo "$ac_eof" | sort -nru | sed 1q` ++ ac_eof=`expr $ac_eof + 1` ++fi ++ ++cat >>$CONFIG_STATUS <<_ACEOF ++cat >"\$tmp/subs-3.sed" <<\CEOF$ac_eof ++/@[a-zA-Z_][a-zA-Z_0-9]*@/!b ++_ACEOF ++sed ' ++s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g ++s/^/s,@/; s/!/@,|#_!!_#|/ ++:n ++t n ++s/'"$ac_delim"'$/,g/; t ++s/$/\\/; p ++N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n ++' >>$CONFIG_STATUS >$CONFIG_STATUS <<_ACEOF ++CEOF$ac_eof ++_ACEOF ++ ++ ++ac_delim='%!_!# ' ++for ac_last_try in false false false false false :; do ++ cat >conf$$subs.sed <<_ACEOF + PBX_H323!$PBX_H323$ac_delim + PBX_IXJUSER!$PBX_IXJUSER$ac_delim + GTKCONFIG!$GTKCONFIG$ac_delim +@@ -34573,7 +35950,7 @@ CURL_CONFIG!$CURL_CONFIG$ac_delim + LTLIBOBJS!$LTLIBOBJS$ac_delim + _ACEOF + +- if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then ++ if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 12; then + break + elif $ac_last_try; then + { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +@@ -34591,8 +35968,8 @@ if test -n "$ac_eof"; then + fi + + cat >>$CONFIG_STATUS <<_ACEOF +-cat >"\$tmp/subs-3.sed" <<\CEOF$ac_eof +-/@[a-zA-Z_][a-zA-Z_0-9]*@/!b ++cat >"\$tmp/subs-4.sed" <<\CEOF$ac_eof ++/@[a-zA-Z_][a-zA-Z_0-9]*@/!b end + _ACEOF + sed ' + s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g +@@ -34605,6 +35982,8 @@ N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n + ' >>$CONFIG_STATUS >$CONFIG_STATUS <<_ACEOF ++:end ++s/|#_!!_#|//g + CEOF$ac_eof + _ACEOF + +@@ -34852,7 +36231,7 @@ s&@abs_builddir@&$ac_abs_builddir&;t t + s&@abs_top_builddir@&$ac_abs_top_builddir&;t t + s&@INSTALL@&$ac_INSTALL&;t t + $ac_datarootdir_hack +-" $ac_file_inputs | sed -f "$tmp/subs-1.sed" | sed -f "$tmp/subs-2.sed" | sed -f "$tmp/subs-3.sed" | sed 's/|#_!!_#|//g' >$tmp/out ++" $ac_file_inputs | sed -f "$tmp/subs-1.sed" | sed -f "$tmp/subs-2.sed" | sed -f "$tmp/subs-3.sed" | sed -f "$tmp/subs-4.sed" >$tmp/out + + test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && +diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in +index 3e015f1..d93a303 100644 +--- a/include/asterisk/autoconfig.h.in ++++ b/include/asterisk/autoconfig.h.in +@@ -59,9 +59,15 @@ + /* Define to 1 if your GCC C compiler supports the 'unused' attribute. */ + #undef HAVE_ATTRIBUTE_unused + ++/* Define to indicate the ${BLUETOOTH_DESCRIP} library */ ++#undef HAVE_BLUETOOTH ++ + /* Define to 1 if you have the `bzero' function. */ + #undef HAVE_BZERO + ++/* Define to indicate the ${CAP_DESCRIP} library */ ++#undef HAVE_CAP ++ + /* Define to 1 if your system has a working `chown' function. */ + #undef HAVE_CHOWN + +@@ -326,6 +332,9 @@ + /* Define to 1 if your system has soxmix application. */ + #undef HAVE_SOXMIX + ++/* Define to indicate the ${SPANDSP_DESCRIP} library */ ++#undef HAVE_SPANDSP ++ + /* Define to indicate the ${SPEEX_DESCRIP} library */ + #undef HAVE_SPEEX + +-- +1.5.3.7 + diff --git a/asterisk-1.4.16.2-chanmobile.patch b/asterisk-1.4.16.2-chanmobile.patch new file mode 100644 index 0000000..c848808 --- /dev/null +++ b/asterisk-1.4.16.2-chanmobile.patch @@ -0,0 +1,2521 @@ +From b7b79d988252729c187bce32cd21b2c5595ca263 Mon Sep 17 00:00:00 2001 +From: Jeffrey C. Ollie +Date: Sun, 18 Nov 2007 22:25:43 -0600 +Subject: [PATCH] Add chan_mobile (backported from asterisk-addons trunk). + +--- + build_tools/menuselect-deps.in | 1 + + channels/Makefile | 2 + + channels/chan_mobile.c | 2126 ++++++++++++++++++++++++++++++++++++++++ + configs/mobile.conf.sample | 60 ++ + configure.ac | 3 + + doc/chan_mobile.txt | 240 +++++ + makeopts.in | 3 + + 7 files changed, 2435 insertions(+), 0 deletions(-) + create mode 100644 channels/chan_mobile.c + create mode 100644 configs/mobile.conf.sample + create mode 100644 doc/chan_mobile.txt + +diff --git a/build_tools/menuselect-deps.in b/build_tools/menuselect-deps.in +index fb10545..4f12adb 100644 +--- a/build_tools/menuselect-deps.in ++++ b/build_tools/menuselect-deps.in +@@ -1,4 +1,5 @@ + ASOUND=@PBX_ALSA@ ++BLUETOOTH=@PBX_BLUETOOTH@ + CURL=@PBX_CURL@ + FREETDS=@PBX_FREETDS@ + GSM=@PBX_GSM@ +diff --git a/channels/Makefile b/channels/Makefile +index f5e96c7..9844128 100644 +--- a/channels/Makefile ++++ b/channels/Makefile +@@ -124,4 +124,6 @@ misdn/isdn_lib.o: ASTCFLAGS+=-Wno-strict-aliasing + + $(if $(filter chan_misdn,$(EMBEDDED_MODS)),modules.link,chan_misdn.so): chan_misdn.o misdn_config.o misdn/isdn_lib.o misdn/isdn_msg_parser.o + ++chan_mobile.so: LIBS+=$(BLUETOOTH_LIB) ++ + chan_vpb.oo: ASTCFLAGS:=$(filter-out -Wdeclaration-after-statement,$(ASTCFLAGS)) +diff --git a/channels/chan_mobile.c b/channels/chan_mobile.c +new file mode 100644 +index 0000000..69c871f +--- /dev/null ++++ b/channels/chan_mobile.c +@@ -0,0 +1,2126 @@ ++/* ++ * Asterisk -- An open source telephony toolkit. ++ * ++ * Copyright (C) 1999 - 2006, Digium, Inc. ++ * ++ * Mark Spencer ++ * ++ * See http://www.asterisk.org for more information about ++ * the Asterisk project. Please do not directly contact ++ * any of the maintainers of this project for assistance; ++ * the project provides a web site, mailing lists and IRC ++ * channels for your use. ++ * ++ * This program is free software, distributed under the terms of ++ * the GNU General Public License Version 2. See the LICENSE file ++ * at the top of the source tree. ++ */ ++ ++/*! \file ++ * ++ * \brief Bluetooth Mobile Device channel driver ++ * ++ * \author Dave Bowerman ++ * ++ * \ingroup channel_drivers ++ */ ++ ++/*** MODULEINFO ++ bluetooth ++ ***/ ++ ++#include ++ ++ASTERISK_FILE_VERSION(__FILE__, "$Revision$") ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define AST_MODULE "chan_mobile" ++ ++#define MBL_CONFIG "mobile.conf" ++ ++#define DEVICE_FRAME_SIZE 48 ++#define DEVICE_FRAME_FORMAT AST_FORMAT_SLINEAR ++#define CHANNEL_FRAME_SIZE 320 ++ ++static int prefformat = DEVICE_FRAME_FORMAT; ++ ++static int discovery_interval = 60; /* The device discovery interval, default 60 seconds. */ ++static pthread_t discovery_thread = AST_PTHREADT_NULL; /* The discovery thread */ ++static sdp_session_t *sdp_session; ++ ++enum mbl_type { ++ MBL_TYPE_PHONE, ++ MBL_TYPE_HEADSET ++}; ++ ++enum mbl_state { ++ MBL_STATE_INIT = 0, ++ MBL_STATE_INIT1, ++ MBL_STATE_INIT2, ++ MBL_STATE_INIT3, ++ MBL_STATE_INIT4, ++ MBL_STATE_INIT5, ++ MBL_STATE_INIT6, ++ MBL_STATE_PREIDLE, ++ MBL_STATE_IDLE, ++ MBL_STATE_DIAL, ++ MBL_STATE_DIAL1, ++ MBL_STATE_OUTGOING, ++ MBL_STATE_RING, ++ MBL_STATE_RING2, ++ MBL_STATE_RING3, ++ MBL_STATE_INCOMING, ++ MBL_STATE_HANGUP, ++ MBL_STATE_INSMS, ++ MBL_STATE_OUTSMS, ++ MBL_STATE_OUTSMS1, ++ MBL_STATE_OUTSMS2 ++}; ++ ++struct adapter_pvt { ++ int dev_id; /* device id */ ++ int hci_socket; /* device descriptor */ ++ char id[31]; /* the 'name' from mobile.conf */ ++ bdaddr_t addr; /* adddress of adapter */ ++ unsigned int inuse:1; /* are we in use ? */ ++ unsigned int alignment_detection:1; /* do alignment detection on this adpater? */ ++ int sco_socket; ++ AST_LIST_ENTRY(adapter_pvt) entry; ++}; ++ ++static AST_LIST_HEAD_STATIC(adapters, adapter_pvt); ++ ++struct mbl_pvt { ++ struct ast_channel *owner; /* Channel we belong to, possibly NULL */ ++ struct ast_frame fr; /* "null" frame */ ++ enum mbl_type type; /* Phone or Headset */ ++ char id[31]; /* The id from mobile.conf */ ++ int group; /* group number for group dialling */ ++ bdaddr_t addr; /* address of device */ ++ struct adapter_pvt *adapter; /* the adapter we use */ ++ char context[AST_MAX_CONTEXT]; /* the context for incoming calls */ ++ char connected; /* is it connected? */ ++ int rfcomm_port; /* rfcomm port number */ ++ int rfcomm_socket; /* rfcomm socket descriptor */ ++ char rfcomm_buf[256]; ++ char io_buf[CHANNEL_FRAME_SIZE + AST_FRIENDLY_OFFSET]; ++ char io_save_buf[DEVICE_FRAME_SIZE]; ++ int io_save_len; ++ int io_pipe[2]; ++ int sco_socket; /* sco socket descriptor */ ++ pthread_t sco_listener_thread; /* inbound sco listener for this device */ ++ enum mbl_state state; /* monitor thread current state */ ++ pthread_t monitor_thread; /* monitor thread handle */ ++ char dial_number[AST_MAX_EXTENSION]; /* number for the monitor thread to dial */ ++ int dial_timeout; ++ char ciev_call_0[4]; /* dynamically built reponse strings */ ++ char ciev_call_1[4]; ++ char ciev_callsetup_0[4]; ++ char ciev_callsetup_1[4]; ++ char ciev_callsetup_2[4]; ++ char ciev_callsetup_3[4]; ++ unsigned int no_callsetup:1; ++ unsigned int has_sms:1; ++ unsigned int sent_answer:1; ++ unsigned int do_alignment_detection:1; ++ unsigned int alignment_detection_triggered:1; ++ unsigned int do_hangup:1; ++ short alignment_samples[4]; ++ int alignment_count; ++ char sms_txt[160]; ++ struct ast_dsp *dsp; ++ struct ast_frame *dsp_fr; ++ int dtmf_skip; ++ int skip_frames; ++ char hangup_count; ++ AST_LIST_ENTRY(mbl_pvt) entry; ++}; ++ ++static AST_LIST_HEAD_STATIC(devices, mbl_pvt); ++ ++/* CLI stuff */ ++static const char show_usage[] = ++"Usage: mobile show devices\n" ++" Shows the state of Bluetooth Cell / Mobile devices.\n"; ++ ++static const char search_usage[] = ++"Usage: mobile search\n" ++" Searches for Bluetooth Cell / Mobile devices in range.\n"; ++ ++static const char rfcomm_usage[] = ++"Usage: mobile rfcomm command\n" ++" Send command to the rfcomm port.\n"; ++ ++static int do_show_devices(int, int, char **); ++static int do_search_devices(int, int, char **); ++static int do_send_rfcomm(int, int, char **); ++ ++static struct ast_cli_entry mbl_cli[] = { ++ {{"mobile", "show", "devices", NULL}, do_show_devices, "Show Bluetooth Cell / Mobile devices", show_usage}, ++ {{"mobile", "search", NULL}, do_search_devices, "Search for Bluetooth Cell / Mobile devices", search_usage}, ++ {{"mobile", "rfcomm", NULL}, do_send_rfcomm, "Send commands to the rfcomm port for debugging", rfcomm_usage}, ++}; ++ ++/* App stuff */ ++static char *app_mblstatus = "MobileStatus"; ++static char *mblstatus_synopsis = "MobileStatus(Device,Variable)"; ++static char *mblstatus_desc = ++"MobileStatus(Device,Variable)\n" ++" Device - Id of mobile device from mobile.conf\n" ++" Variable - Variable to store status in will be 1-3.\n" ++" In order, Disconnected, Connected & Free, Connected & Busy.\n"; ++ ++static char *app_mblsendsms = "MobileSendSMS"; ++static char *mblsendsms_synopsis = "MobileSendSMS(Device,Dest,Message)"; ++static char *mblsendsms_desc = ++"MobileSendSms(Device,Dest,Message)\n" ++" Device - Id of device from mobile.conf\n" ++" Dest - destination\n" ++" Message - text of the message\n"; ++ ++static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num); ++static struct ast_channel *mbl_request(const char *type, int format, void *data, int *cause); ++static int mbl_call(struct ast_channel *ast, char *dest, int timeout); ++static int mbl_hangup(struct ast_channel *ast); ++static int mbl_answer(struct ast_channel *ast); ++static int mbl_digit_begin(struct ast_channel *ast, char digit); ++static int mbl_digit_end(struct ast_channel *ast, char digit, unsigned int duration); ++static struct ast_frame *mbl_read(struct ast_channel *ast); ++static int mbl_write(struct ast_channel *ast, struct ast_frame *frame); ++static int mbl_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); ++static int mbl_devicestate(void *data); ++ ++static void do_alignment_detection(struct mbl_pvt *pvt, char *buf, int buflen); ++ ++static int rfcomm_connect(bdaddr_t src, bdaddr_t dst, int remote_channel); ++static int rfcomm_write(struct mbl_pvt *pvt, char *buf); ++static int rfcomm_read(struct mbl_pvt *pvt, char *buf, char flush, int timeout); ++ ++static int sco_connect(bdaddr_t src, bdaddr_t dst); ++static int sco_write(int s, char *buf, int len); ++static int sco_read(int s, char *buf, int len); ++ ++static void *do_sco_listen(void *data); ++static int sdp_search(char *addr, int profile); ++ ++static const struct ast_channel_tech mbl_tech = { ++ .type = "Mobile", ++ .description = "Bluetooth Mobile Device Channel Driver", ++ .capabilities = AST_FORMAT_SLINEAR, ++ .requester = mbl_request, ++ .call = mbl_call, ++ .hangup = mbl_hangup, ++ .answer = mbl_answer, ++ .send_digit_begin = mbl_digit_begin, ++ .send_digit_end = mbl_digit_end, ++ .read = mbl_read, ++ .write = mbl_write, ++ .fixup = mbl_fixup, ++ .devicestate = mbl_devicestate ++}; ++ ++/* ++ ++ CLI Commands implementation ++ ++*/ ++ ++static int do_show_devices(int fd, int argc, char **argv) ++{ ++ ++ struct mbl_pvt *pvt; ++ char bdaddr[18]; ++ char group[6]; ++ ++ #define FORMAT "%-15.15s %-17.17s %-5.5s %-15.15s %-9.9s %-5.5s %-3.3s\n" ++ ++ ast_cli(fd, FORMAT, "ID", "Address", "Group", "Adapter", "Connected", "State", "SMS"); ++ AST_LIST_TRAVERSE(&devices, pvt, entry) { ++ ba2str(&pvt->addr, bdaddr); ++ snprintf(group, 5, "%d", pvt->group); ++ ast_cli(fd, FORMAT, pvt->id, bdaddr, group, pvt->adapter->id, pvt->connected?"Yes":"No", (pvt->state == MBL_STATE_IDLE)?"Free":(pvt->state < MBL_STATE_IDLE)?"Init":"Busy", (pvt->has_sms)?"Yes":"No"); ++ } ++ ++ return RESULT_SUCCESS; ++ ++} ++ ++static int do_search_devices(int fd, int argc, char **argv) ++{ ++ ++ struct adapter_pvt *adapter; ++ inquiry_info *ii = NULL; ++ int max_rsp, num_rsp; ++ int len, flags; ++ int i, phport, hsport; ++ char addr[19] = {0}; ++ char name[31] = {0}; ++ ++ #define FORMAT2 "%-17.17s %-30.30s %-6.6s %-7.7s %-4.4s\n" ++ #define FORMAT3 "%-17.17s %-30.30s %-6.6s %-7.7s %d\n" ++ ++ /* find a free adapter */ ++ AST_LIST_TRAVERSE(&adapters, adapter, entry) { ++ if (!adapter->inuse) ++ break; ++ } ++ if (!adapter) { ++ ast_cli(fd, "All Bluetooth adapters are in use at this time.\n"); ++ return RESULT_SUCCESS; ++ } ++ ++ len = 8; ++ max_rsp = 255; ++ flags = IREQ_CACHE_FLUSH; ++ ++ ii = alloca(max_rsp * sizeof(inquiry_info)); ++ num_rsp = hci_inquiry(adapter->dev_id, len, max_rsp, NULL, &ii, flags); ++ if (num_rsp > 0) { ++ ast_cli(fd, FORMAT2, "Address", "Name", "Usable", "Type", "Port"); ++ for (i = 0; i < num_rsp; i++) { ++ ba2str(&(ii+i)->bdaddr, addr); ++ name[0] = 0x00; ++ if (hci_read_remote_name(adapter->hci_socket, &(ii+i)->bdaddr, sizeof(name)-1, name, 0) < 0) ++ strcpy(name, "[unknown]"); ++ phport = sdp_search(addr, HANDSFREE_AGW_PROFILE_ID); ++ if (!phport) ++ hsport = sdp_search(addr, HEADSET_PROFILE_ID); ++ else ++ hsport = 0; ++ ast_cli(fd, FORMAT3, addr, name, (phport > 0 || hsport > 0)?"Yes":"No", (phport > 0)?"Phone":"Headset", (phport > 0)?phport:hsport); ++ } ++ } else ++ ast_cli(fd, "No Bluetooth Cell / Mobile devices found.\n"); ++ ++ return RESULT_SUCCESS; ++ ++} ++ ++static int do_send_rfcomm(int fd, int argc, char **argv) ++{ ++ ++ struct mbl_pvt *pvt; ++ char buf[128]; ++ ++ if (argc != 4) { ++ ast_cli(fd, "You must specify the name of the device and command.\n"); ++ return RESULT_SUCCESS; ++ } ++ ++ AST_LIST_TRAVERSE(&devices, pvt, entry) { ++ if (!strcmp(pvt->id, argv[2])) ++ break; ++ } ++ ++ if (!pvt || !pvt->connected) { ++ ast_cli(fd, "Device %s not found.\n", argv[2]); ++ return RESULT_SUCCESS; ++ } ++ ++ sprintf(buf, "%s\r", argv[3]); ++ rfcomm_write(pvt, buf); ++ ++ return RESULT_SUCCESS; ++ ++} ++ ++/* ++ ++ Dialplan applications implementation ++ ++*/ ++ ++static int mbl_status_exec(struct ast_channel *ast, void *data) ++{ ++ ++ struct mbl_pvt *pvt; ++ char *parse; ++ int stat; ++ char status[2]; ++ ++ AST_DECLARE_APP_ARGS(args, ++ AST_APP_ARG(device); ++ AST_APP_ARG(variable); ++ ); ++ ++ if (ast_strlen_zero(data)) ++ return -1; ++ ++ parse = ast_strdupa(data); ++ ++ AST_STANDARD_APP_ARGS(args, parse); ++ ++ if (ast_strlen_zero(args.device) || ast_strlen_zero(args.variable)) ++ return -1; ++ ++ stat = 1; ++ ++ AST_LIST_TRAVERSE(&devices, pvt, entry) { ++ if (!strcmp(pvt->id, args.device)) ++ break; ++ } ++ ++ if (pvt) { ++ if (pvt->connected) ++ stat = 2; ++ if (pvt->owner) ++ stat = 3; ++ } ++ ++ sprintf(status, "%d", stat); ++ pbx_builtin_setvar_helper(ast, args.variable, status); ++ ++ return 0; ++ ++} ++ ++static int mbl_sendsms_exec(struct ast_channel *ast, void *data) ++{ ++ ++ struct mbl_pvt *pvt; ++ char *parse; ++ ++ AST_DECLARE_APP_ARGS(args, ++ AST_APP_ARG(device); ++ AST_APP_ARG(dest); ++ AST_APP_ARG(message); ++ ); ++ ++ if (ast_strlen_zero(data)) ++ return -1; ++ ++ parse = ast_strdupa(data); ++ ++ AST_STANDARD_APP_ARGS(args, parse); ++ ++ if (ast_strlen_zero(args.device)) { ++ ast_log(LOG_ERROR,"NULL device for message -- SMS will not be sent.\n"); ++ return -1; ++ } ++ ++ if (ast_strlen_zero(args.dest)) { ++ ast_log(LOG_ERROR,"NULL destination for message -- SMS will not be sent.\n"); ++ return -1; ++ } ++ ++ if (ast_strlen_zero(args.message)) { ++ ast_log(LOG_ERROR,"NULL Message to be sent -- SMS will not be sent.\n"); ++ return -1; ++ } ++ ++ AST_LIST_TRAVERSE(&devices, pvt, entry) { ++ if (!strcmp(pvt->id, args.device)) ++ break; ++ } ++ ++ if (!pvt) { ++ ast_log(LOG_ERROR,"Bluetooth device %s wasn't found in the list -- SMS will not be sent.\n", args.device); ++ return -1; ++ } ++ ++ if (!pvt->connected) { ++ ast_log(LOG_ERROR,"Bluetooth device %s wasn't connected -- SMS will not be sent.\n", args.device); ++ return -1; ++ } ++ ++ if (!pvt->has_sms) { ++ ast_log(LOG_ERROR,"Bluetooth device %s doesn't handle SMS -- SMS will not be sent.\n", args.device); ++ return -1; ++ } ++ ++ if (pvt->state != MBL_STATE_IDLE) { ++ ast_log(LOG_ERROR,"Bluetooth device %s isn't IDLE -- SMS will not be sent.\n", args.device); ++ return -1; ++ } ++ ++ ast_copy_string(pvt->dial_number, args.dest, sizeof(pvt->dial_number)); ++ ast_copy_string(pvt->sms_txt, args.message, sizeof(pvt->sms_txt)); ++ pvt->state = MBL_STATE_OUTSMS; ++ ++ return 0; ++ ++} ++ ++/* ++ ++ Channel Driver callbacks ++ ++*/ ++ ++static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num) ++{ ++ ++ struct ast_channel *chn; ++ ++ if (pipe(pvt->io_pipe) == -1) { ++ ast_log(LOG_ERROR, "Failed to create io_pipe.\n"); ++ return NULL; ++ } ++ ++ if (pvt->sco_socket != -1) ++ close(pvt->sco_socket); ++ pvt->sco_socket = -1; ++ pvt->io_save_len = 0; ++ pvt->sent_answer = 0; ++ pvt->skip_frames = 0; ++ pvt->alignment_count = 0; ++ pvt->alignment_detection_triggered = 0; ++ if (pvt->adapter->alignment_detection) ++ pvt->do_alignment_detection = 1; ++ else ++ pvt->do_alignment_detection = 0; ++ pvt->do_hangup = 1; ++ chn = ast_channel_alloc(1, state, cid_num, pvt->id, 0, 0, pvt->context, 0, "Mobile/%s-%04lx", pvt->id, ast_random() & 0xffff); ++ if (chn) { ++ chn->tech = &mbl_tech; ++ chn->nativeformats = prefformat; ++ chn->rawreadformat = prefformat; ++ chn->rawwriteformat = prefformat; ++ chn->writeformat = prefformat; ++ chn->readformat = prefformat; ++ chn->tech_pvt = pvt; ++ ast_channel_lock(chn); ++ chn->fds[0] = pvt->io_pipe[0]; ++ ast_channel_unlock(chn); ++ if (state == AST_STATE_RING) ++ chn->rings = 1; ++ ast_string_field_set(chn, language, "en"); ++ pvt->owner = chn; ++ return chn; ++ } ++ ++ return NULL; ++ ++} ++ ++static struct ast_channel *mbl_request(const char *type, int format, void *data, int *cause) ++{ ++ ++ struct ast_channel *chn = NULL; ++ struct mbl_pvt *pvt; ++ char *dest_dev = NULL; ++ char *dest_num = NULL; ++ int oldformat, group; ++ ++ if (!data) { ++ ast_log(LOG_WARNING, "Channel requested with no data\n"); ++ *cause = AST_CAUSE_INCOMPATIBLE_DESTINATION; ++ return NULL; ++ } ++ ++ oldformat = format; ++ format &= (AST_FORMAT_SLINEAR); ++ if (!format) { ++ ast_log(LOG_WARNING, "Asked to get a channel of unsupported format '%d'\n", oldformat); ++ *cause = AST_CAUSE_FACILITY_NOT_IMPLEMENTED; ++ return NULL; ++ } ++ ++ dest_dev = ast_strdupa((char *)data); ++ ++ dest_num = strchr(dest_dev, '/'); ++ if (dest_num) ++ *dest_num++ = 0x00; ++ ++ /* Find requested device and make sure its connected. */ ++ AST_LIST_TRAVERSE(&devices, pvt, entry) { ++ if (((dest_dev[0] == 'g') || (dest_dev[0] == 'G')) && ((dest_dev[1] >= '0') && (dest_dev[1] <= '9'))) { ++ group = atoi(dest_dev+1); ++ if (pvt->group == group) ++ break; ++ } else if (!strcmp(pvt->id, dest_dev)) { ++ break; ++ } ++ } ++ if (!pvt || !pvt->connected || pvt->owner) { ++ ast_log(LOG_WARNING, "Request to call on device %s which is not connected / already in use.\n", dest_dev); ++ *cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL; ++ return NULL; ++ } ++ ++ if ((pvt->type == MBL_TYPE_PHONE) && !dest_num) { ++ ast_log(LOG_WARNING, "Cant determine destination number.\n"); ++ *cause = AST_CAUSE_INCOMPATIBLE_DESTINATION; ++ return NULL; ++ } ++ ++ chn = mbl_new(AST_STATE_DOWN, pvt, NULL); ++ if (!chn) { ++ ast_log(LOG_WARNING, "Unable to allocate channel structure\n"); ++ *cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL; ++ return NULL; ++ } ++ ++ return chn; ++ ++} ++ ++static int mbl_call(struct ast_channel *ast, char *dest, int timeout) ++{ ++ ++ struct mbl_pvt *pvt; ++ char *dest_dev = NULL; ++ char *dest_num = NULL; ++ ++ dest_dev = ast_strdupa((char *)dest); ++ ++ pvt = ast->tech_pvt; ++ ++ if (pvt->type == MBL_TYPE_PHONE) { ++ dest_num = strchr(dest_dev, '/'); ++ if (!dest_num) { ++ ast_log(LOG_WARNING, "Cant determine destination number.\n"); ++ return -1; ++ } ++ *dest_num++ = 0x00; ++ } ++ ++ if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) { ++ ast_log(LOG_WARNING, "mbl_call called on %s, neither down nor reserved\n", ast->name); ++ return -1; ++ } ++ ++ if (option_debug) ++ ast_log(LOG_DEBUG, "Calling %s on %s\n", dest, ast->name); ++ ++ if (pvt->type == MBL_TYPE_PHONE) { ++ ast_copy_string(pvt->dial_number, dest_num, sizeof(pvt->dial_number)); ++ pvt->state = MBL_STATE_DIAL; ++ pvt->dial_timeout = (timeout == 0) ? 30 : timeout; ++ } else { ++ pvt->state = MBL_STATE_RING; ++ } ++ ++ return 0; ++ ++} ++ ++static int mbl_hangup(struct ast_channel *ast) ++{ ++ ++ struct mbl_pvt *pvt; ++ ++ if (!ast->tech_pvt) { ++ ast_log(LOG_WARNING, "Asked to hangup channel not connected\n"); ++ return 0; ++ } ++ pvt = ast->tech_pvt; ++ ++ if (option_debug) ++ ast_log(LOG_DEBUG, "Hanging up device %s.\n", pvt->id); ++ ++ ast_channel_lock(ast); ++ ast->fds[0] = -1; ++ ast_channel_unlock(ast); ++ close(pvt->io_pipe[0]); ++ close(pvt->io_pipe[1]); ++ ++ if (pvt->type == MBL_TYPE_HEADSET && pvt->sco_socket != -1) { ++ close(pvt->sco_socket); ++ pvt->sco_socket = -1; ++ } ++ ++ if ((pvt->state == MBL_STATE_INCOMING || pvt->state == MBL_STATE_OUTGOING || pvt->state == MBL_STATE_DIAL1 || pvt->state == MBL_STATE_RING3) && pvt->type == MBL_TYPE_PHONE) { ++ if (pvt->do_hangup) { ++ rfcomm_write(pvt, "AT+CHUP\r"); ++ } ++ pvt->state = MBL_STATE_HANGUP; ++ pvt->hangup_count = 0; ++ } else ++ pvt->state = MBL_STATE_IDLE; ++ ++ pvt->owner = NULL; ++ ast->tech_pvt = NULL; ++ ast_setstate(ast, AST_STATE_DOWN); ++ ++ return 0; ++ ++} ++ ++static int mbl_answer(struct ast_channel *ast) ++{ ++ ++ struct mbl_pvt *pvt; ++ ++ pvt = ast->tech_pvt; ++ ++ rfcomm_write(pvt, "ATA\r"); ++ ++ ast_setstate(ast, AST_STATE_UP); ++ ++ pvt->sent_answer = 1; ++ ++ return 0; ++ ++} ++ ++static int mbl_digit_begin(struct ast_channel *chan, char digit) ++{ ++ ++ return 0; ++ ++} ++ ++static int mbl_digit_end(struct ast_channel *ast, char digit, unsigned int duration) ++{ ++ ++ struct mbl_pvt *pvt; ++ char buf[11]; ++ ++ pvt = ast->tech_pvt; ++ ++ if (pvt->type == MBL_TYPE_HEADSET) ++ return 0; ++ ++ if (option_debug) ++ ast_log(LOG_DEBUG, "Dialed %c\n", digit); ++ ++ switch(digit) { ++ case '0': ++ case '1': ++ case '2': ++ case '3': ++ case '4': ++ case '5': ++ case '6': ++ case '7': ++ case '8': ++ case '9': ++ case '*': ++ case '#': ++ sprintf(buf, "AT+VTS=%c\r", digit); ++ rfcomm_write(pvt, buf); ++ break; ++ default: ++ ast_log(LOG_WARNING, "Unknown digit '%c'\n", digit); ++ return -1; ++ } ++ ++ return 0; ++ ++} ++ ++static struct ast_frame *mbl_read(struct ast_channel *ast) ++{ ++ ++ struct mbl_pvt *pvt = ast->tech_pvt; ++ struct ast_frame *f; ++ int r; ++ ++ if (option_debug > 1) ++ ast_log(LOG_DEBUG, "*** mbl_read()\n"); ++ ++ if (!pvt->owner) { ++ return &ast_null_frame; ++ } ++ memset(&pvt->fr, 0x00, sizeof(struct ast_frame)); ++ pvt->fr.frametype = AST_FRAME_VOICE; ++ pvt->fr.subclass = DEVICE_FRAME_FORMAT; ++ pvt->fr.datalen = CHANNEL_FRAME_SIZE; ++ pvt->fr.samples = CHANNEL_FRAME_SIZE / 2; ++ pvt->fr.src = "Mobile"; ++ pvt->fr.offset = AST_FRIENDLY_OFFSET; ++ pvt->fr.mallocd = 0; ++ pvt->fr.delivery.tv_sec = 0; ++ pvt->fr.delivery.tv_usec = 0; ++ pvt->fr.data = pvt->io_buf + AST_FRIENDLY_OFFSET; ++ ++ if ((r = read(pvt->io_pipe[0], pvt->fr.data, CHANNEL_FRAME_SIZE)) != CHANNEL_FRAME_SIZE) { ++ if (r == -1) { ++ ast_log(LOG_ERROR, "read error %d\n", errno); ++ return &ast_null_frame; ++ } else { ++ pvt->fr.datalen = r; ++ pvt->fr.samples = r / 2; ++ } ++ } ++ ++ f = ast_dsp_process(0, pvt->dsp, &pvt->fr); ++ if (f && (f->frametype == AST_FRAME_DTMF_END)) { ++ pvt->fr.frametype = AST_FRAME_DTMF_END; ++ pvt->fr.subclass = f->subclass; ++ } ++ ++ return &pvt->fr; ++ ++} ++ ++static int mbl_write(struct ast_channel *ast, struct ast_frame *frame) ++{ ++ ++ struct mbl_pvt *pvt = ast->tech_pvt; ++ int i, r, io_need, num_frames; ++ char *pfr, buf[DEVICE_FRAME_SIZE]; ++ ++ if (option_debug > 1) ++ ast_log(LOG_DEBUG, "*** mbl_write\n"); ++ ++ if (frame->frametype != AST_FRAME_VOICE) { ++ return 0; ++ } ++ ++ io_need = 0; ++ if (pvt->io_save_len > 0) { ++ io_need = DEVICE_FRAME_SIZE - pvt->io_save_len; ++ memcpy(pvt->io_save_buf + pvt->io_save_len, frame->data, io_need); ++ sco_write(pvt->sco_socket, pvt->io_save_buf, DEVICE_FRAME_SIZE); ++ if ((r = sco_read(pvt->sco_socket, buf, DEVICE_FRAME_SIZE))) { ++ if (pvt->do_alignment_detection) ++ do_alignment_detection(pvt, buf, r); ++ sco_write(pvt->io_pipe[1], buf, r); ++ } ++ } ++ ++ num_frames = (frame->datalen - io_need) / DEVICE_FRAME_SIZE; ++ pfr = frame->data + io_need; ++ ++ for (i=0; isco_socket, pfr, DEVICE_FRAME_SIZE); ++ if ((r = sco_read(pvt->sco_socket, buf, DEVICE_FRAME_SIZE))) { ++ if (pvt->do_alignment_detection) ++ do_alignment_detection(pvt, buf, r); ++ sco_write(pvt->io_pipe[1], buf, r); ++ } ++ pfr += DEVICE_FRAME_SIZE; ++ } ++ ++ pvt->io_save_len = (frame->datalen - io_need) - (num_frames * DEVICE_FRAME_SIZE); ++ if (pvt->io_save_len > 0) { ++ memcpy(pvt->io_save_buf, pfr, pvt->io_save_len); ++ } ++ ++ return 0; ++ ++} ++ ++static int mbl_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) ++{ ++ ++ struct mbl_pvt *pvt = oldchan->tech_pvt; ++ ++ if (pvt && pvt->owner == oldchan) ++ pvt->owner = newchan; ++ ++ return 0; ++ ++} ++ ++static int mbl_devicestate(void *data) ++{ ++ ++ char *device; ++ int res = AST_DEVICE_INVALID; ++ struct mbl_pvt *pvt; ++ ++ device = ast_strdupa(S_OR(data, "")); ++ ++ if (option_debug) ++ ast_log(LOG_DEBUG, "Checking device state for device %s\n", device); ++ ++ AST_LIST_TRAVERSE(&devices, pvt, entry) { ++ if (!strcmp(pvt->id, device)) ++ break; ++ } ++ ++ if (pvt) { ++ if (pvt->connected) { ++ if (pvt->owner) ++ res = AST_DEVICE_INUSE; ++ else ++ res = AST_DEVICE_NOT_INUSE; ++ } ++ } ++ ++ return res; ++ ++} ++ ++/* ++ ++ Callback helpers ++ ++*/ ++ ++/* ++ ++ do_alignment_detection() ++ ++ This routine attempts to detect where we get misaligned sco audio data from the bluetooth adaptor. ++ ++ Its enabled by alignmentdetect=yes under the adapter entry in mobile.conf ++ ++ Some adapters suffer a problem where occasionally they will byte shift the audio stream one byte to the right. ++ The result is static or white noise on the inbound (from the adapter) leg of the call. ++ This is characterised by a sudden jump in magnitude of the value of the 16 bit samples. ++ ++ Here we look at the first 4 48 byte frames. We average the absolute values of each sample in the frame, ++ then average the sum of the averages of frames 1, 2, and 3. ++ Frame zero is usually zero. ++ If the end result > 100, and it usually is if we have the problem, set a flag and compensate by shifting the bytes ++ for each subsequent frame during the call. ++ ++ If the result is <= 100 then clear the flag so we dont come back in here... ++ ++ This seems to work OK.... ++ ++*/ ++ ++static void do_alignment_detection(struct mbl_pvt *pvt, char *buf, int buflen) ++{ ++ ++ int i; ++ short a, *s; ++ char *p; ++ ++ if (pvt->alignment_detection_triggered) { ++ for (i=buflen, p=buf+buflen-1; i>0; i--, p--) ++ *p = *(p-1); ++ *(p+1) = 0; ++ return; ++ } ++ ++ if (pvt->alignment_count < 4) { ++ s = (short *)buf; ++ for (i=0, a=0; ialignment_samples[pvt->alignment_count++] = a; ++ return; ++ } ++ ++ if (option_debug) ++ ast_log(LOG_DEBUG, "Alignment Detection result is [%-d %-d %-d %-d]\n", pvt->alignment_samples[0], pvt->alignment_samples[1], pvt->alignment_samples[2], pvt->alignment_samples[3]); ++ ++ a = abs(pvt->alignment_samples[1]) + abs(pvt->alignment_samples[2]) + abs(pvt->alignment_samples[3]); ++ a /= 3; ++ if (a > 100) { ++ pvt->alignment_detection_triggered = 1; ++ if (option_debug) ++ ast_log(LOG_DEBUG, "Alignment Detection Triggered.\n"); ++ } else ++ pvt->do_alignment_detection = 0; ++ ++} ++ ++/* ++ ++ rfcomm helpers ++ ++*/ ++ ++static int rfcomm_connect(bdaddr_t src, bdaddr_t dst, int remote_channel) { ++ ++ struct sockaddr_rc addr; ++ int s; ++ ++ if ((s = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) { ++ if (option_debug) ++ ast_log(LOG_DEBUG, "socket() failed (%d).\n", errno); ++ return -1; ++ } ++ ++ memset(&addr, 0, sizeof(addr)); ++ addr.rc_family = AF_BLUETOOTH; ++ bacpy(&addr.rc_bdaddr, &src); ++ addr.rc_channel = (uint8_t) 1; ++ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { ++ if (option_debug) ++ ast_log(LOG_DEBUG, "bind() failed (%d).\n", errno); ++ close(s); ++ return -1; ++ } ++ ++ memset(&addr, 0, sizeof(addr)); ++ addr.rc_family = AF_BLUETOOTH; ++ bacpy(&addr.rc_bdaddr, &dst); ++ addr.rc_channel = remote_channel; ++ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { ++ if (option_debug) ++ ast_log(LOG_DEBUG, "connect() failed (%d).\n", errno); ++ close(s); ++ return -1; ++ } ++ ++ return s; ++ ++} ++ ++static int rfcomm_write(struct mbl_pvt *pvt, char *buf) ++{ ++ ++ char *p; ++ ssize_t num_write; ++ int len; ++ ++ if (option_debug) ++ ast_log(LOG_DEBUG, "rfcomm_write() (%s) [%s]\n", pvt->id, buf); ++ len = strlen(buf); ++ p = buf; ++ while (len > 0) { ++ if ((num_write = write(pvt->rfcomm_socket, p, len)) == -1) { ++ if (option_debug) ++ ast_log(LOG_DEBUG, "rfcomm_write() error [%d]\n", errno); ++ return 0; ++ } ++ len -= num_write; ++ p += num_write; ++ } ++ ++ return 1; ++ ++} ++ ++/* ++ ++ Here we need to return complete '\r' terminated single responses to the devices monitor thread, or ++ a timeout if nothing is available. ++ The rfcomm connection to the device is asynchronous, so there is no guarantee that responses will ++ be returned in a single read() call. We handle this by buffering the input and returning one response ++ per call, or a timeout if nothing is available. ++ ++*/ ++ ++static int rfcomm_read(struct mbl_pvt *pvt, char *buf, char flush, int timeout) ++{ ++ ++ int sel, rlen, slen; ++ fd_set rfds; ++ struct timeval tv; ++ char *p; ++ ++ if (!flush) { ++ if ((p = strchr(pvt->rfcomm_buf, '\r'))) { ++ *p++ = 0x00; ++ if (*p == '\n') ++ p++; ++ memmove(buf, pvt->rfcomm_buf, strlen(pvt->rfcomm_buf)); ++ *(buf + strlen(pvt->rfcomm_buf)) = 0x00; ++ memmove(pvt->rfcomm_buf, p, strlen(p)); ++ *(pvt->rfcomm_buf+strlen(p)) = 0x00; ++ return 1; ++ } ++ } else { ++ pvt->rfcomm_buf[0] = 0x00; ++ } ++ ++ FD_ZERO(&rfds); ++ FD_SET(pvt->rfcomm_socket, &rfds); ++ ++ tv.tv_sec = timeout; ++ tv.tv_usec = 0; ++ ++ if ((sel = select(pvt->rfcomm_socket + 1, &rfds, NULL, NULL, &tv)) > 0) { ++ if (FD_ISSET(pvt->rfcomm_socket, &rfds)) { ++ slen = strlen(pvt->rfcomm_buf); ++ rlen = read(pvt->rfcomm_socket, pvt->rfcomm_buf + slen, sizeof(pvt->rfcomm_buf) - slen - 1); ++ if (rlen > 0) { ++ pvt->rfcomm_buf[slen+rlen] = 0x00; ++ if ((p = strchr(pvt->rfcomm_buf, '\r'))) { ++ *p++ = 0x00; ++ if (*p == '\n') ++ p++; ++ memmove(buf, pvt->rfcomm_buf, strlen(pvt->rfcomm_buf)); ++ *(buf + strlen(pvt->rfcomm_buf)) = 0x00; ++ memmove(pvt->rfcomm_buf, p, strlen(p)); ++ *(pvt->rfcomm_buf+strlen(p)) = 0x00; ++ return 1; ++ } ++ } else ++ return rlen; ++ } ++ } else if (sel == 0) { /* timeout */ ++ return 0; ++ } ++ ++ return 1; ++ ++} ++ ++/* ++ ++ sco helpers ++ ++*/ ++ ++static int sco_connect(bdaddr_t src, bdaddr_t dst) ++{ ++ ++ struct sockaddr_sco addr; ++ int s; ++ ++ if ((s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) { ++ if (option_debug) ++ ast_log(LOG_DEBUG, "socket() failed (%d).\n", errno); ++ return -1; ++ } ++ ++ memset(&addr, 0, sizeof(addr)); ++ addr.sco_family = AF_BLUETOOTH; ++ bacpy(&addr.sco_bdaddr, &src); ++ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { ++ if (option_debug) ++ ast_log(LOG_DEBUG, "bind() failed (%d).\n", errno); ++ close(s); ++ return -1; ++ } ++ ++ memset(&addr, 0, sizeof(addr)); ++ addr.sco_family = AF_BLUETOOTH; ++ bacpy(&addr.sco_bdaddr, &dst); ++ ++ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { ++ if (option_debug) ++ ast_log(LOG_DEBUG, "sco connect() failed (%d).\n", errno); ++ close(s); ++ return -1; ++ } ++ ++ return s; ++ ++} ++ ++static int sco_write(int s, char *buf, int len) ++{ ++ ++ int r; ++ ++ if (s == -1) { ++ if (option_debug > 1) ++ ast_log(LOG_DEBUG, "sco_write() not ready\n"); ++ return 0; ++ } ++ ++ if (option_debug > 1) ++ ast_log(LOG_DEBUG, "sco_write()\n"); ++ ++ r = write(s, buf, len); ++ if (r == -1) { ++ if (option_debug > 1) ++ ast_log(LOG_DEBUG, "sco write error %d\n", errno); ++ return 0; ++ } ++ ++ return 1; ++ ++} ++ ++static int sco_read(int s, char *buf, int len) ++{ ++ ++ int r; ++ ++ if (s == -1) { ++ if (option_debug > 1) ++ ast_log(LOG_DEBUG, "sco_read() not ready\n"); ++ return 0; ++ } ++ ++ if (option_debug > 1) ++ ast_log(LOG_DEBUG, "sco_read()\n"); ++ ++ r = read(s, buf, len); ++ if (r == -1) { ++ if (option_debug > 1) ++ ast_log(LOG_DEBUG, "sco_read() error %d\n", errno); ++ return 0; ++ } ++ ++ return r; ++ ++} ++ ++/* ++ ++ sdp helpers ++ ++*/ ++ ++static int sdp_search(char *addr, int profile) ++{ ++ ++ sdp_session_t *session = 0; ++ bdaddr_t bdaddr; ++ uuid_t svc_uuid; ++ uint32_t range = 0x0000ffff; ++ sdp_list_t *response_list, *search_list, *attrid_list; ++ int status, port; ++ sdp_list_t *proto_list; ++ sdp_record_t *sdprec; ++ ++ str2ba(addr, &bdaddr); ++ port = 0; ++ session = sdp_connect(BDADDR_ANY, &bdaddr, SDP_RETRY_IF_BUSY); ++ if (!session) { ++ if (option_debug) ++ ast_log(LOG_DEBUG, "sdp_connect() failed on device %s.\n", addr); ++ return 0; ++ } ++ ++ sdp_uuid32_create(&svc_uuid, profile); ++ search_list = sdp_list_append(0, &svc_uuid); ++ attrid_list = sdp_list_append(0, &range); ++ response_list = 0x00; ++ status = sdp_service_search_attr_req(session, search_list, SDP_ATTR_REQ_RANGE, attrid_list, &response_list); ++ if (status == 0) { ++ if (response_list) { ++ sdprec = (sdp_record_t *) response_list->data; ++ proto_list = 0x00; ++ if (sdp_get_access_protos(sdprec, &proto_list) == 0) { ++ port = sdp_get_proto_port(proto_list, RFCOMM_UUID); ++ sdp_list_free(proto_list, 0); ++ } ++ sdp_record_free(sdprec); ++ sdp_list_free(response_list, 0); ++ } else ++ if (option_debug) ++ ast_log(LOG_DEBUG, "No responses returned for device %s.\n", addr); ++ } else ++ if (option_debug) ++ ast_log(LOG_DEBUG, "sdp_service_search_attr_req() failed on device %s.\n", addr); ++ ++ sdp_list_free(search_list, 0); ++ sdp_list_free(attrid_list, 0); ++ sdp_close(session); ++ ++ return port; ++ ++} ++ ++static sdp_session_t *sdp_register(void) ++{ ++ ++ uint32_t service_uuid_int[] = {0, 0, 0, GENERIC_AUDIO_SVCLASS_ID}; ++ uint8_t rfcomm_channel = 1; ++ const char *service_name = "Asterisk PABX"; ++ const char *service_dsc = "Asterisk PABX"; ++ const char *service_prov = "Asterisk"; ++ ++ uuid_t root_uuid, l2cap_uuid, rfcomm_uuid, svc_uuid, svc_class1_uuid, svc_class2_uuid; ++ sdp_list_t *l2cap_list = 0, *rfcomm_list = 0, *root_list = 0, *proto_list = 0, *access_proto_list = 0, *svc_uuid_list = 0; ++ sdp_data_t *channel = 0; ++ ++ int err = 0; ++ sdp_session_t *session = 0; ++ ++ sdp_record_t *record = sdp_record_alloc(); ++ ++ sdp_uuid128_create(&svc_uuid, &service_uuid_int); ++ sdp_set_service_id(record, svc_uuid); ++ ++ sdp_uuid32_create(&svc_class1_uuid, GENERIC_AUDIO_SVCLASS_ID); ++ sdp_uuid32_create(&svc_class2_uuid, HEADSET_PROFILE_ID); ++ ++ svc_uuid_list = sdp_list_append(0, &svc_class1_uuid); ++ svc_uuid_list = sdp_list_append(svc_uuid_list, &svc_class2_uuid); ++ sdp_set_service_classes(record, svc_uuid_list); ++ ++ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); ++ root_list = sdp_list_append(0, &root_uuid); ++ sdp_set_browse_groups( record, root_list ); ++ ++ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); ++ l2cap_list = sdp_list_append(0, &l2cap_uuid); ++ proto_list = sdp_list_append(0, l2cap_list); ++ ++ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); ++ channel = sdp_data_alloc(SDP_UINT8, &rfcomm_channel); ++ rfcomm_list = sdp_list_append(0, &rfcomm_uuid); ++ sdp_list_append(rfcomm_list, channel); ++ sdp_list_append(proto_list, rfcomm_list); ++ ++ access_proto_list = sdp_list_append(0, proto_list); ++ sdp_set_access_protos(record, access_proto_list); ++ ++ sdp_set_info_attr(record, service_name, service_prov, service_dsc); ++ ++ if (!(session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY))) ++ ast_log(LOG_WARNING, "Failed to connect sdp and create session.\n"); ++ else ++ err = sdp_record_register(session, record, 0); ++ ++ sdp_data_free(channel); ++ sdp_list_free(rfcomm_list, 0); ++ sdp_list_free(root_list, 0); ++ sdp_list_free(access_proto_list, 0); ++ sdp_list_free(svc_uuid_list, 0); ++ ++ return session; ++ ++} ++ ++/* ++ ++ Thread routines ++ ++*/ ++ ++static void *do_monitor_phone(void *data) ++{ ++ ++ struct mbl_pvt *pvt = (struct mbl_pvt *)data; ++ struct ast_channel *chn; ++ char monitor = 1; ++ char buf[256]; ++ char cid_num[AST_MAX_EXTENSION], *pcids, *pcide; ++ int s, t, i, smsi; ++ int group, group2; ++ int callp = 0, callsetupp; ++ char brsf, nsmode, *p, *p1; ++ char sms_src[13]; ++ char sms_txt[160]; ++ ++ brsf = nsmode = 0; ++ ++ if (!rfcomm_write(pvt, "AT+BRSF=4\r")) ++ monitor = 0; ++ ++ while (monitor) { ++ ++ if (pvt->state == MBL_STATE_DIAL1) ++ t = pvt->dial_timeout; ++ else if (pvt->state == MBL_STATE_HANGUP) ++ t = 2; ++ else if (pvt->state == MBL_STATE_OUTSMS1) ++ t = 2; ++ else if (pvt->state == MBL_STATE_OUTSMS2) ++ t = 10; ++ else ++ t = 1; ++ ++ s = rfcomm_read(pvt, buf, 0, t); ++ ++ if ((s > 0) && (buf[0] != 0x0) && (buf[0] != '\r')) { ++ if (option_debug) ++ ast_log(LOG_DEBUG, "rfcomm_read() (%s) [%s]\n", pvt->id, buf); ++ switch (pvt->state) { ++ case MBL_STATE_INIT: ++ if (strstr(buf, "+BRSF:")) { ++ brsf = 1; ++ } else if (strstr(buf, "ERROR") && !nsmode) { /* Hmmm, Non-Standard Phone, just continue */ ++ rfcomm_write(pvt, "AT+CIND=?\r"); ++ pvt->state++; ++ nsmode = 1; ++ } else if (strstr(buf, "OK") && brsf) { ++ rfcomm_write(pvt, "AT+CIND=?\r"); ++ pvt->state++; ++ } ++ break; ++ case MBL_STATE_INIT1: ++ if (strstr(buf, "+CIND:")) { ++ group = callp = callsetupp = 0; ++ group2 = 1; ++ for (i=0; iciev_call_0, "%d,0", callp); ++ sprintf(pvt->ciev_call_1, "%d,1", callp); ++ sprintf(pvt->ciev_callsetup_0, "%d,0", callsetupp); ++ sprintf(pvt->ciev_callsetup_1, "%d,1", callsetupp); ++ sprintf(pvt->ciev_callsetup_2, "%d,2", callsetupp); ++ sprintf(pvt->ciev_callsetup_3, "%d,3", callsetupp); ++ if (callsetupp == 0) /* This phone has no call setup indication!! ... */ ++ pvt->no_callsetup = 1; ++ if (option_debug) ++ ast_log(LOG_DEBUG, "CIEV_CALL=%d CIEV_CALLSETUP=%d\n", callp, callsetupp); ++ } ++ if (strstr(buf, "OK")) { ++ rfcomm_write(pvt, "AT+CIND?\r"); ++ pvt->state++; ++ } ++ break; ++ case MBL_STATE_INIT2: ++ if ((p = strstr(buf, "+CIND:"))) { ++ p += 5; ++ if (*(p+(callp*2)) == '1') { ++ ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s has a call in progress - delaying connection.\n", pvt->id); ++ monitor = 0; ++ } ++ } else if (strstr(buf, "OK")) { ++ rfcomm_write(pvt, "AT+CMER=3,0,0,1\r"); ++ pvt->state++; ++ } ++ break; ++ case MBL_STATE_INIT3: ++ if (strstr(buf, "OK")) { ++ rfcomm_write(pvt, "AT+CLIP=1\r"); ++ pvt->state++; ++ } ++ break; ++ case MBL_STATE_INIT4: ++ if (strstr(buf, "OK")) { ++ rfcomm_write(pvt, "AT+CMGF=1\r"); ++ pvt->state++; ++ } ++ break; ++ case MBL_STATE_INIT5: ++ if (strstr(buf, "ERROR")) { /* No SMS Support ! */ ++ pvt->state = MBL_STATE_PREIDLE; ++ } else if (strstr(buf, "OK")) { ++ rfcomm_write(pvt, "AT+CNMI=2,1,0,1,0\r"); ++ pvt->state++; ++ } ++ break; ++ case MBL_STATE_INIT6: ++ if (strstr(buf, "OK")) { /* We have SMS Support */ ++ pvt->has_sms = 1; ++ pvt->state = MBL_STATE_PREIDLE; ++ } else if (strstr(buf, "ERROR")) { ++ pvt->has_sms = 0; ++ pvt->state = MBL_STATE_PREIDLE; ++ } ++ break; ++ case MBL_STATE_PREIDLE: /* Nothing handled here, wait for timeout, then off we go... */ ++ break; ++ case MBL_STATE_IDLE: ++ if (option_debug) ++ ast_log(LOG_DEBUG, "Device %s [%s]\n", pvt->id, buf); ++ if (strstr(buf, "RING")) { ++ pvt->state = MBL_STATE_RING; ++ } else if (strstr(buf, "+CIEV:")) { ++ if (strstr(buf, pvt->ciev_callsetup_3)) { /* User has dialed out on handset */ ++ monitor = 0; /* We disconnect now, as he is */ ++ } /* probably leaving BT range... */ ++ } ++ break; ++ case MBL_STATE_DIAL: /* Nothing handled here, we need to wait for a timeout */ ++ break; ++ case MBL_STATE_DIAL1: ++ if (strstr(buf, "OK")) { ++ if (pvt->no_callsetup) { ++ ast_queue_control(pvt->owner, AST_CONTROL_ANSWER); ++ } else { ++ ast_setstate(pvt->owner, AST_STATE_RINGING); ++ } ++ pvt->state = MBL_STATE_OUTGOING; ++ } ++ break; ++ case MBL_STATE_OUTGOING: ++ if (strstr(buf, "+CIEV")) { ++ if (strstr(buf, pvt->ciev_call_0)) { /* call was hung up */ ++ pvt->do_hangup = 0; ++ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP); ++ } else if (strstr(buf, pvt->ciev_callsetup_3)) { /* b-party ringing */ ++ ast_queue_control(pvt->owner, AST_CONTROL_RINGING); ++ } else if (strstr(buf, pvt->ciev_call_1) && !pvt->no_callsetup) { /* b-party answer */ ++ ast_queue_control(pvt->owner, AST_CONTROL_ANSWER); ++ } ++ } ++ break; ++ case MBL_STATE_RING: ++ cid_num[0] = 0x00; ++ if ((pcids = strstr(buf, "+CLIP:"))) { ++ if ((pcids = strchr(pcids, '"'))) { ++ if ((pcide = strchr(pcids+1, '"'))) { ++ strncpy(cid_num, pcids+1, pcide - pcids - 1); ++ cid_num[pcide - pcids - 1] = 0x00; ++ } ++ } ++ chn = mbl_new(AST_STATE_RING, pvt, cid_num); ++ if (chn) { ++ if (ast_pbx_start(chn)) { ++ ast_log(LOG_ERROR, "Unable to start pbx on incoming call.\n"); ++ ast_hangup(chn); ++ } else ++ pvt->state = MBL_STATE_RING3; ++ } else { ++ ast_log(LOG_ERROR, "Unable to allocate channel for incoming call.\n"); ++ rfcomm_write(pvt, "AT+CHUP\r"); ++ pvt->state = MBL_STATE_IDLE; ++ } ++ } ++ break; ++ case MBL_STATE_RING2: ++ chn = mbl_new(AST_STATE_RING, pvt, cid_num); ++ if (chn) { ++ if (ast_pbx_start(chn)) { ++ ast_log(LOG_ERROR, "Unable to start pbx on incoming call.\n"); ++ ast_hangup(chn); ++ } else ++ pvt->state = MBL_STATE_RING3; ++ } else { ++ ast_log(LOG_ERROR, "Unable to allocate channel for incoming call.\n"); ++ rfcomm_write(pvt, "AT+CHUP\r"); ++ pvt->state = MBL_STATE_IDLE; ++ } ++ break; ++ case MBL_STATE_RING3: ++ if (strstr(buf, "+CIEV")) { ++ if (strstr(buf, pvt->ciev_call_1)) { ++ if (pvt->sent_answer) { /* We answered */ ++ pvt->state = MBL_STATE_INCOMING; ++ } else { /* User answered on handset!, disconnect */ ++ pvt->state = MBL_STATE_IDLE; ++ if (pvt->sco_socket > -1) ++ close(pvt->sco_socket); ++ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP); ++ } ++ } ++ if ((strstr(buf, pvt->ciev_callsetup_0) || strstr(buf, pvt->ciev_call_0))) { /* Caller disconnected */ ++ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP); ++ } ++ } ++ break; ++ case MBL_STATE_INCOMING: ++ if (strstr(buf, "+CIEV")) { ++ if (strstr(buf, pvt->ciev_call_0)) { ++ pvt->do_hangup = 0; ++ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP); ++ } ++ } ++ break; ++ case MBL_STATE_HANGUP: ++ if (strstr(buf, "OK") || strstr(buf, pvt->ciev_call_0)) { ++ close(pvt->sco_socket); ++ pvt->sco_socket = -1; ++ pvt->state = MBL_STATE_IDLE; ++ } ++ break; ++ case MBL_STATE_INSMS: ++ if (strstr(buf, "+CMGR:")) { ++ memset(sms_src, 0x00, sizeof(sms_src)); ++ if ((p = strchr(buf, ','))) { ++ if (*(++p) == '"') ++ p++; ++ if ((p1 = strchr(p, ','))) { ++ if (*(--p1) == '"') ++ p1--; ++ memset(sms_src, 0x00, sizeof(sms_src)); ++ strncpy(sms_src, p, p1 - p + 1); ++ } ++ } ++ } else if (strstr(buf, "OK")) { ++ chn = mbl_new(AST_STATE_DOWN, pvt, NULL); ++ strcpy(chn->exten, "sms"); ++ pbx_builtin_setvar_helper(chn, "SMSSRC", sms_src); ++ pbx_builtin_setvar_helper(chn, "SMSTXT", sms_txt); ++ if (ast_pbx_start(chn)) ++ ast_log(LOG_ERROR, "Unable to start pbx on incoming sms.\n"); ++ pvt->state = MBL_STATE_IDLE; ++ } else { ++ ast_copy_string(sms_txt, buf, sizeof(sms_txt)); ++ } ++ break; ++ case MBL_STATE_OUTSMS: ++ break; ++ case MBL_STATE_OUTSMS1: ++ break; ++ case MBL_STATE_OUTSMS2: ++ if (strstr(buf, "OK")) { ++ pvt->state = MBL_STATE_IDLE; ++ } ++ break; ++ } ++ /* Unsolicited responses */ ++ ++ if (strstr(buf, "+CMTI:")) { /* SMS Incoming... */ ++ if ((p = strchr(buf, ','))) { ++ p++; ++ smsi = atoi(p); ++ if (smsi > 0) { ++ sprintf(buf, "AT+CMGR=%d\r", smsi); ++ rfcomm_write(pvt, buf); ++ pvt->state = MBL_STATE_INSMS; ++ } ++ } ++ } ++ ++ } else if (s == 0) { /* Timeouts */ ++ if (pvt->state == MBL_STATE_INIT2) { /* Some devices dont respond to AT+CIND? properly. RIM Blackberry 4 example */ ++ pvt->state++; ++ rfcomm_write(pvt, "AT+CMER=3,0,0,1\r"); ++ } else if (pvt->state == MBL_STATE_INIT3) { /* Some devices dont respond to AT+CMER=3,0,0,1 properly. VK 2020 for example */ ++ pvt->state++; ++ rfcomm_write(pvt, "AT+CLIP=1\r"); ++ } else if (pvt->state == MBL_STATE_PREIDLE) { ++ pvt->connected = 1; ++ ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s initialised and ready.\n", pvt->id); ++ pvt->state = MBL_STATE_IDLE; ++ } else if (pvt->state == MBL_STATE_DIAL) { ++ sprintf(buf, "ATD%s;\r", pvt->dial_number); ++ if (!rfcomm_write(pvt, buf)) { ++ ast_log(LOG_ERROR, "Dial failed on %s state %d\n", pvt->owner->name, pvt->state); ++ ast_queue_control(pvt->owner, AST_CONTROL_CONGESTION); ++ pvt->state = MBL_STATE_IDLE; ++ } else { ++ pvt->state = MBL_STATE_DIAL1; ++ } ++ } else if (pvt->state == MBL_STATE_DIAL1) { ++ ast_log(LOG_ERROR, "Dial failed on %s state %d\n", pvt->owner->name, pvt->state); ++ ast_queue_control(pvt->owner, AST_CONTROL_CONGESTION); ++ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP); ++ pvt->state = MBL_STATE_IDLE; ++ } else if (pvt->state == MBL_STATE_RING) { /* No CLIP?, bump it */ ++ pvt->state = MBL_STATE_RING2; ++ } else if (pvt->state == MBL_STATE_HANGUP) { ++ if (pvt->do_hangup) { ++ if (pvt->hangup_count == 6) { ++ if (option_debug) ++ ast_log(LOG_DEBUG, "Device %s failed to hangup after 6 tries, disconnecting.\n", pvt->id); ++ monitor = 0; ++ } ++ rfcomm_write(pvt, "AT+CHUP\r"); ++ pvt->hangup_count++; ++ } else ++ pvt->state = MBL_STATE_IDLE; ++ } else if (pvt->state == MBL_STATE_OUTSMS) { ++ sprintf(buf, "AT+CMGS=\"%s\"\r", pvt->dial_number); ++ rfcomm_write(pvt, buf); ++ pvt->state = MBL_STATE_OUTSMS1; ++ } else if (pvt->state == MBL_STATE_OUTSMS1) { ++ if (pvt->rfcomm_buf[0] == '>') { ++ snprintf(buf, sizeof(buf), "%s%c", pvt->sms_txt, 0x1a); ++ rfcomm_write(pvt, buf); ++ pvt->state = MBL_STATE_OUTSMS2; ++ } else { ++ ast_log(LOG_ERROR, "Failed to send SMS to %s on device %s\n", pvt->dial_number, pvt->id); ++ pvt->state = MBL_STATE_IDLE; ++ } ++ } else if (pvt->state == MBL_STATE_OUTSMS2) { ++ ast_log(LOG_ERROR, "Failed to send SMS to %s on device %s\n", pvt->dial_number, pvt->id); ++ pvt->state = MBL_STATE_IDLE; ++ } ++ } else if (s == -1) { ++ if (option_verbose > 2) ++ ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s has disconnected, reason (%d).\n", pvt->id, errno); ++ monitor = 0; ++ } ++ ++ } ++ ++ if (pvt->rfcomm_socket > -1) ++ close(pvt->rfcomm_socket); ++ if (pvt->sco_socket > -1) ++ close(pvt->sco_socket); ++ pvt->sco_socket = -1; ++ pvt->connected = 0; ++ pvt->monitor_thread = AST_PTHREADT_NULL; ++ ++ pthread_cancel(pvt->sco_listener_thread); ++ pthread_join(pvt->sco_listener_thread, NULL); ++ pvt->sco_listener_thread = AST_PTHREADT_NULL; ++ ++ close(pvt->adapter->sco_socket); ++ ++ manager_event(EVENT_FLAG_SYSTEM, "MobileStatus", "Status: Disconnect\r\nDevice: %s\r\n", pvt->id); ++ ++ pvt->adapter->inuse = 0; ++ ++ return NULL; ++ ++} ++ ++static void *do_monitor_headset(void *data) ++{ ++ ++ struct mbl_pvt *pvt = (struct mbl_pvt *)data; ++ char monitor = 1; ++ char buf[256]; ++ int s, t; ++ ++ pvt->state = MBL_STATE_PREIDLE; ++ ++ while (monitor) { ++ ++ if (pvt->state == MBL_STATE_RING2) ++ t = 2; ++ else ++ t = 1; ++ s = rfcomm_read(pvt, buf, 0, t); ++ ++ if ((s > 0) && (buf[0] != 0x0) && (buf[0] != '\r')) { ++ if (option_debug) ++ ast_log(LOG_DEBUG, "rfcomm_read() (%s) [%s]\n", pvt->id, buf); ++ switch (pvt->state) { ++ case MBL_STATE_RING2: ++ if (strstr(buf, "AT+CKPD=")) { ++ ast_queue_control(pvt->owner, AST_CONTROL_ANSWER); ++ pvt->state = MBL_STATE_INCOMING; ++ rfcomm_write(pvt, "\r\n+VGS=13\r\n"); ++ rfcomm_write(pvt, "\r\n+VGM=13\r\n"); ++ } ++ break; ++ case MBL_STATE_INCOMING: ++ if (strstr(buf, "AT+CKPD=")) { ++ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP); ++ } ++ break; ++ default: ++ break; ++ } ++ if (strstr(buf, "AT+VGS=")) { ++ rfcomm_write(pvt, "\r\nOK\r\n"); ++ } else if (strstr(buf, "AT+VGM=")) { ++ rfcomm_write(pvt, "\r\nOK\r\n"); ++ } ++ } else if (s == 0) { /* Timeouts */ ++ if (pvt->state == MBL_STATE_PREIDLE) { ++ pvt->connected = 1; ++ ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s initialised and ready.\n", pvt->id); ++ pvt->state = MBL_STATE_IDLE; ++ } else if (pvt->state == MBL_STATE_RING) { ++ pvt->sco_socket = sco_connect(pvt->adapter->addr, pvt->addr); ++ if (pvt->sco_socket > -1) { ++ ast_setstate(pvt->owner, AST_STATE_RINGING); ++ ast_queue_control(pvt->owner, AST_CONTROL_RINGING); ++ pvt->state = MBL_STATE_RING2; ++ } else { ++ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP); ++ } ++ } else if (pvt->state == MBL_STATE_RING2) { ++ rfcomm_write(pvt, "\r\nRING\r\n"); ++ } ++ } else if (s == -1) { ++ if (option_verbose > 2) ++ ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s has disconnected, reason (%d).\n", pvt->id, errno); ++ monitor = 0; ++ } ++ ++ } ++ ++ if (pvt->rfcomm_socket > -1) ++ close(pvt->rfcomm_socket); ++ if (pvt->sco_socket > -1) ++ close(pvt->sco_socket); ++ pvt->sco_socket = -1; ++ pvt->connected = 0; ++ pvt->monitor_thread = AST_PTHREADT_NULL; ++ ++ manager_event(EVENT_FLAG_SYSTEM, "MobileStatus", "Status: Disconnect\r\nDevice: %s\r\n", pvt->id); ++ ++ pvt->adapter->inuse = 0; ++ ++ return NULL; ++ ++} ++ ++static int start_monitor(struct mbl_pvt *pvt) ++{ ++ ++ if (pvt->type == MBL_TYPE_PHONE) { ++ if (ast_pthread_create_background(&pvt->monitor_thread, NULL, do_monitor_phone, pvt) < 0) { ++ pvt->monitor_thread = AST_PTHREADT_NULL; ++ return 0; ++ } ++ /* we are a phone, so spin the sco listener on the adapter as well */ ++ if (ast_pthread_create_background(&pvt->sco_listener_thread, NULL, do_sco_listen, pvt->adapter) < 0) { ++ ast_log(LOG_ERROR, "Unable to create sco listener thread for device %s.\n", pvt->id); ++ } ++ ++ } else { ++ if (ast_pthread_create_background(&pvt->monitor_thread, NULL, do_monitor_headset, pvt) < 0) { ++ pvt->monitor_thread = AST_PTHREADT_NULL; ++ return 0; ++ } ++ } ++ ++ return 1; ++ ++} ++ ++static void *do_discovery(void *data) ++{ ++ ++ struct adapter_pvt *adapter; ++ struct mbl_pvt *pvt; ++ ++ for (;;) { ++ AST_LIST_TRAVERSE(&adapters, adapter, entry) { ++ if (!adapter->inuse) { ++ AST_LIST_TRAVERSE(&devices, pvt, entry) { ++ if (!adapter->inuse && !pvt->connected && !strcmp(adapter->id, pvt->adapter->id)) { ++ if ((pvt->rfcomm_socket = rfcomm_connect(adapter->addr, pvt->addr, pvt->rfcomm_port)) > -1) { ++ pvt->state = 0; ++ if (start_monitor(pvt)) { ++ pvt->connected = 1; ++ adapter->inuse = 1; ++ manager_event(EVENT_FLAG_SYSTEM, "MobileStatus", "Status: Connect\r\nDevice: %s\r\n", pvt->id); ++ if (option_verbose > 2) ++ ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s has connected.\n", pvt->id); ++ } ++ } ++ } ++ } ++ } ++ } ++ /* Go to sleep */ ++ sleep(discovery_interval); ++ } ++ ++ return NULL; ++} ++ ++static void *do_sco_listen(void *data) ++{ ++ ++ int ns; ++ struct sockaddr_sco addr; ++ char saddr[18]; ++ struct sco_options so; ++ socklen_t len; ++ int opt = 1; ++ socklen_t addrlen; ++ struct mbl_pvt *pvt; ++ struct adapter_pvt *adapter = (struct adapter_pvt *) data; ++ ++ if ((adapter->sco_socket = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) { ++ ast_log(LOG_ERROR, "Unable to create sco listener socket.\n"); ++ return NULL; ++ } ++ memset(&addr, 0, sizeof(addr)); ++ addr.sco_family = AF_BLUETOOTH; ++ bacpy(&addr.sco_bdaddr, &adapter->addr); ++ if (bind(adapter->sco_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) { ++ ast_log(LOG_ERROR, "Unable to bind sco listener socket. (%d)\n", errno); ++ close(adapter->sco_socket); ++ return NULL; ++ } ++ if (setsockopt(adapter->sco_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) { ++ ast_log(LOG_ERROR, "Unable to setsockopt sco listener socket.\n"); ++ close(adapter->sco_socket); ++ return NULL; ++ } ++ if (listen(adapter->sco_socket, 5) < 0) { ++ ast_log(LOG_ERROR, "Unable to listen sco listener socket.\n"); ++ close(adapter->sco_socket); ++ return NULL; ++ } ++ while (1) { ++ if (option_debug) ++ ast_log(LOG_DEBUG, "About to accept() socket.\n"); ++ addrlen = sizeof(struct sockaddr_sco); ++ if ((ns = accept(adapter->sco_socket, (struct sockaddr *)&addr, &addrlen)) > -1) { ++ if (option_debug) ++ ast_log(LOG_DEBUG, "accept()ed socket.\n"); ++ len = sizeof(so); ++ getsockopt(ns, SOL_SCO, SCO_OPTIONS, &so, &len); ++ ++ ba2str(&addr.sco_bdaddr, saddr); ++ if (option_debug) ++ ast_log(LOG_DEBUG, "Incoming Audio Connection from device %s MTU is %d\n", saddr, so.mtu); ++ ++ pvt = NULL; ++ AST_LIST_TRAVERSE(&devices, pvt, entry) { ++ if (!bacmp(&pvt->addr, &addr.sco_bdaddr)) ++ break; ++ } ++ if (pvt) { ++ if (pvt->sco_socket != -1) ++ close(pvt->sco_socket); ++ pvt->sco_socket = ns; ++ } else ++ if (option_debug) ++ ast_log(LOG_DEBUG, "Could not find device for incoming Audio Connection.\n"); ++ } else { ++ ast_log(LOG_ERROR, "accept() failed %d\n", errno); ++ } ++ } ++ ++ return NULL; ++ ++} ++ ++/* ++ ++ Module ++ ++*/ ++ ++static int mbl_load_config(void) ++{ ++ ++ struct ast_config *cfg = NULL; ++ char *cat = NULL; ++ struct ast_variable *var; ++ const char *id, *address, *useadapter, *port, *context, *type, *skip, *group, *master, *nocallsetup, *aligndetect; ++ struct mbl_pvt *pvt; ++ struct adapter_pvt *adapter; ++ uint16_t vs; ++ struct hci_dev_req dr; ++ char nadapters = 0; ++ ++ cfg = ast_config_load(MBL_CONFIG); ++ if (!cfg) ++ return 0; ++ ++ for (var = ast_variable_browse(cfg, "general"); var; var = var->next) { ++ if (!strcasecmp(var->name, "interval")) ++ discovery_interval = atoi(var->value); ++ } ++ ++ /* load adapters first */ ++ cat = ast_category_browse(cfg, NULL); ++ while (cat) { ++ if (!strcasecmp(cat, "adapter")) { ++ id = ast_variable_retrieve(cfg, cat, "id"); ++ address = ast_variable_retrieve(cfg, cat, "address"); ++ master = ast_variable_retrieve(cfg, cat, "forcemaster"); ++ aligndetect = ast_variable_retrieve(cfg, cat, "alignmentdetection"); ++ if (option_debug) ++ ast_log(LOG_DEBUG, "Loading adapter %s %s.\n", id, address); ++ if (!ast_strlen_zero(id) && !ast_strlen_zero(address)) { ++ if ((adapter = ast_calloc(1, sizeof(*adapter)))) { ++ ast_copy_string(adapter->id, id, sizeof(adapter->id)); ++ str2ba(address, &adapter->addr); ++ if (!ast_strlen_zero(aligndetect)) { ++ if (*aligndetect == 'Y' || *aligndetect == 'y') ++ adapter->alignment_detection = 1; ++ } ++ adapter->dev_id = hci_devid(address); ++ adapter->hci_socket = hci_open_dev(adapter->dev_id); ++ if (adapter->dev_id < 0 || adapter->hci_socket < 0) { ++ ast_log(LOG_ERROR, "Unable to open adapter %s. It won't be enabled.\n", adapter->id); ++ ast_free(adapter); ++ } else { ++ if ((master) && (*master)) { ++ dr.dev_id = adapter->dev_id; ++ if (hci_strtolm("master", &dr.dev_opt)) { ++ if (ioctl(adapter->hci_socket, HCISETLINKMODE, (unsigned long) &dr) < 0) { ++ ast_log(LOG_WARNING, "Unable to set adapter %s link mode to MASTER.\n", adapter->id); ++ } ++ } ++ } ++ hci_read_voice_setting(adapter->hci_socket, &vs, 1000); ++ vs = htobs(vs); ++ if (vs != 0x0060) { ++ ast_log(LOG_ERROR, "Incorrect voice setting for adapter %s, it must be 0x0060 - see 'man hciconfig' for details.\n", adapter->id); ++ hci_close_dev(adapter->hci_socket); ++ ast_free(adapter); ++ } else { ++ AST_LIST_INSERT_HEAD(&adapters, adapter, entry); ++ nadapters++; ++ } ++ } ++ } ++ } else ++ ast_log(LOG_ERROR, "id/address missing for adapter %s. It won't be enabled.\n", cat); ++ } ++ cat = ast_category_browse(cfg, cat); ++ } ++ ++ if (!nadapters) { ++ ast_log(LOG_WARNING, "***********************************************************************\n"); ++ ast_log(LOG_WARNING, "No Adapters defined. Please review mobile.conf. See sample for details.\n"); ++ ast_log(LOG_WARNING, "***********************************************************************\n"); ++ } ++ ++ /* now load devices */ ++ cat = ast_category_browse(cfg, NULL); ++ while (cat) { ++ if (strcasecmp(cat, "general") && strcasecmp(cat, "adapter")) { ++ if (option_debug) ++ ast_log(LOG_DEBUG, "Loading device %s.\n", cat); ++ address = ast_variable_retrieve(cfg, cat, "address"); ++ useadapter = ast_variable_retrieve(cfg, cat, "adapter"); ++ port = ast_variable_retrieve(cfg, cat, "port"); ++ context = ast_variable_retrieve(cfg, cat, "context"); ++ type = ast_variable_retrieve(cfg, cat, "type"); ++ skip = ast_variable_retrieve(cfg, cat, "dtmfskip"); ++ group = ast_variable_retrieve(cfg, cat, "group"); ++ nocallsetup = ast_variable_retrieve(cfg, cat, "nocallsetup"); ++ if (!ast_strlen_zero(address) && !ast_strlen_zero(port) && !ast_strlen_zero(useadapter)) { ++ /* find the adapter */ ++ AST_LIST_TRAVERSE(&adapters, adapter, entry) { ++ if (!strcmp(adapter->id, useadapter)) ++ break; ++ } ++ if (!adapter) { ++ ast_log(LOG_ERROR, "Device %s configured to use unknown adapter %s. It won't be enabled.\n", cat, useadapter); ++ break; ++ } ++ if ((pvt = ast_calloc(1, sizeof(*pvt)))) { ++ if (type && !strcmp(type, "headset")) ++ pvt->type = MBL_TYPE_HEADSET; ++ else ++ pvt->type = MBL_TYPE_PHONE; ++ ast_copy_string(pvt->id, cat, sizeof(pvt->id)); ++ str2ba(address, &pvt->addr); ++ ast_copy_string(pvt->context, S_OR(context, "default"), sizeof(pvt->context)); ++ if (group) ++ pvt->group = atoi(group); /* group 0 if invalid */ ++ pvt->state = MBL_STATE_INIT; ++ pvt->rfcomm_socket = -1; ++ pvt->rfcomm_port = atoi(port); ++ pvt->sco_socket = -1; ++ pvt->monitor_thread = AST_PTHREADT_NULL; ++ pvt->sco_listener_thread = AST_PTHREADT_NULL; ++ if (!ast_strlen_zero(nocallsetup)) { ++ if ((*nocallsetup == 'y') || (*nocallsetup == 'Y')) { ++ pvt->no_callsetup = 1; ++ if (option_debug) ++ ast_log(LOG_DEBUG, "Setting nocallsetup mode for device %s.\n", pvt->id); ++ } ++ } ++ pvt->dsp = ast_dsp_new(); ++ if (skip) { ++ if ((pvt->dtmf_skip = atoi(skip)) == 0) ++ pvt->dtmf_skip = 200; ++ } else ++ pvt->dtmf_skip = 200; ++ ast_dsp_set_features(pvt->dsp, DSP_FEATURE_DTMF_DETECT); ++ ast_dsp_digitmode(pvt->dsp, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF); ++ pvt->adapter = adapter; ++ AST_LIST_INSERT_HEAD(&devices, pvt, entry); ++ } ++ } else { ++ ast_log(LOG_ERROR, "Device %s has no address/port/adapter configured. It won't be enabled.\n", cat); ++ } ++ } ++ cat = ast_category_browse(cfg, cat); ++ } ++ ++ ast_config_destroy(cfg); ++ ++ return 1; ++ ++} ++ ++static int unload_module(void) ++{ ++ ++ struct mbl_pvt *pvt; ++ struct adapter_pvt *adapter; ++ ++ /* First, take us out of the channel loop */ ++ ast_channel_unregister(&mbl_tech); ++ ++ /* Kill the discovery thread */ ++ if (discovery_thread != AST_PTHREADT_NULL) { ++ pthread_cancel(discovery_thread); ++ pthread_join(discovery_thread, NULL); ++ } ++ ++ /* Destroy the device list */ ++ while ((pvt = AST_LIST_REMOVE_HEAD(&devices, entry))) { ++ if (pvt->monitor_thread != AST_PTHREADT_NULL) { ++ pthread_cancel(pvt->monitor_thread); ++ pthread_join(pvt->monitor_thread, NULL); ++ } ++ if (pvt->sco_listener_thread != AST_PTHREADT_NULL) { ++ pthread_cancel(pvt->sco_listener_thread); ++ pthread_join(pvt->sco_listener_thread, NULL); ++ } ++ if (pvt->sco_socket > -1) { ++ close(pvt->sco_socket); ++ } ++ if (pvt->adapter->sco_socket > -1) { ++ close(pvt->adapter->sco_socket); ++ } ++ if (pvt->rfcomm_socket > -1) { ++ close(pvt->rfcomm_socket); ++ } ++ ast_dsp_free(pvt->dsp); ++ ast_free(pvt); ++ } ++ ++ /* Destroy the adapter list */ ++ while ((adapter = AST_LIST_REMOVE_HEAD(&adapters, entry))) { ++ hci_close_dev(adapter->hci_socket); ++ ast_free(adapter); ++ } ++ ++ if (sdp_session) ++ sdp_close(sdp_session); ++ ++ /* Unregister the CLI & APP */ ++ ast_cli_unregister_multiple(mbl_cli, sizeof(mbl_cli) / sizeof(mbl_cli[0])); ++ ast_unregister_application(app_mblstatus); ++ ast_unregister_application(app_mblsendsms); ++ ++ return 0; ++ ++} ++ ++static int load_module(void) ++{ ++ ++ int dev_id, s; ++ ++ /* Check if we have Bluetooth, no point loading otherwise... */ ++ dev_id = hci_get_route(NULL); ++ s = hci_open_dev(dev_id); ++ if (dev_id < 0 || s < 0) { ++ ast_log(LOG_ERROR, "No Bluetooth device found. Not loading module.\n"); ++ return AST_MODULE_LOAD_DECLINE; ++ } ++ ++ hci_close_dev(s); ++ ++ if (!mbl_load_config()) { ++ ast_log(LOG_ERROR, "Unable to read config file %s. Not loading module.\n", MBL_CONFIG); ++ return AST_MODULE_LOAD_DECLINE; ++ } ++ ++ sdp_session = sdp_register(); ++ ++ /* Spin the discovery thread */ ++ if (ast_pthread_create_background(&discovery_thread, NULL, do_discovery, NULL) < 0) { ++ ast_log(LOG_ERROR, "Unable to create discovery thread.\n"); ++ return AST_MODULE_LOAD_DECLINE; ++ } ++ ++ ast_cli_register_multiple(mbl_cli, sizeof(mbl_cli) / sizeof(mbl_cli[0])); ++ ast_register_application(app_mblstatus, mbl_status_exec, mblstatus_synopsis, mblstatus_desc); ++ ast_register_application(app_mblsendsms, mbl_sendsms_exec, mblsendsms_synopsis, mblsendsms_desc); ++ ++ /* Make sure we can register our channel type */ ++ if (ast_channel_register(&mbl_tech)) { ++ ast_log(LOG_ERROR, "Unable to register channel class %s\n", "Mobile"); ++ return AST_MODULE_LOAD_FAILURE; ++ } ++ ++ return 0; ++} ++ ++AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Bluetooth Mobile Device Channel Driver", ++ .load = load_module, ++ .unload = unload_module, ++); +diff --git a/configs/mobile.conf.sample b/configs/mobile.conf.sample +new file mode 100644 +index 0000000..1ea4182 +--- /dev/null ++++ b/configs/mobile.conf.sample +@@ -0,0 +1,60 @@ ++; ++; mobile.conf ++; configuration file for chan_mobile ++; ++ ++[general] ++interval=30 ; Number of seconds between trying to connect to devices. ++ ++; The following is a list of adapters we use. ++; id must be unique and address is the bdaddr of the adapter from hciconfig. ++; Each adapter may only have one device (headset or phone) connected at a time. ++; Add an [adapter] entry for each adapter you have. ++ ++[adapter] ++id=blue ++address=00:09:DD:60:01:A3 ++;forcemaster=yes ; attempt to force adapter into master mode. default is no. ++;alignmentdetection=yes ; enable this if you sometimes get 'white noise' on asterisk side of the call ++ ; its a bug in the bluetooth adapter firmware, enabling this will compensate for it. ++ ; default is no. ++ ++[adapter] ++id=dlink ++address=00:80:C8:35:52:78 ++ ++; The following is a list of the devices we deal with. ++; Every device listed below will be available for calls in and out of Asterisk. ++; Each device needs an adapter=xxxx entry which determines which bluetooth adapter is used. ++; Use the CLI command 'mobile search' to discover devices. ++; Use the CLI command 'mobile show devices' to see device status. ++; ++; To place a call out through a mobile phone use Dial(Mobile/[device]/NNN.....) or Dial(Mobile/gn/NNN......) in your dialplan. ++; To call a headset use Dial(Mobile/[device]). ++ ++[LGTU550] ++address=00:E0:91:7F:46:44 ; the address of the phone ++port=4 ; the rfcomm port number (from mobile search) ++context=incoming-mobile ; dialplan context for incoming calls ++adapter=dlink ; adapter to use ++group=1 ; this phone is in channel group 1 ++;nocallsetup=yes ; set this only if your phone reports that it supports call progress notification, but does not do it. Motorola L6 for example. ++ ++[6310i] ++address=00:60:57:32:7E:B1 ++port=13 ++context=incoming-mobile ++adapter=dlink ++group=1 ; this phone is in channel group 1 also. ++ ++[headset] ++address=00:0B:9E:11:AE:C6 ++port=1 ++type=headset ; This is a headset, not a Phone ! ++adapter=blue ++ ++[headset1] ++address=00:0B:9E:11:74:A5 ++port=1 ++type=headset ++adapter=dlink +diff --git a/configure.ac b/configure.ac +index cd4e87b..0bcf7af 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -173,6 +173,7 @@ AC_SUBST(AST_DEVMODE) + # by the --with option name, to make things easier for the users :-) + + AST_EXT_LIB_SETUP([ALSA], [Advanced Linux Sound Architecture], [asound]) ++AST_EXT_LIB_SETUP([BLUETOOTH], [Bluetooth Support], [bluetooth]) + AST_EXT_LIB_SETUP([CURL], [cURL], [curl]) + AST_EXT_LIB_SETUP([CAP], [POSIX 1.e capabilities], [cap]) + AST_EXT_LIB_SETUP([CURSES], [curses], [curses]) +@@ -399,6 +400,8 @@ AC_CHECK_SIZEOF(int) + + AST_EXT_LIB_CHECK([ALSA], [asound], [snd_spcm_init], [alsa/asoundlib.h], [-lm -ldl]) + ++AST_EXT_LIB_CHECK([BLUETOOTH], [bluetooth], [ba2str], [bluetooth/bluetooth.h]) ++ + AST_EXT_LIB_CHECK([CURSES], [curses], [initscr], [curses.h]) + + if test "x${host_os}" = "xlinux-gnu" ; then +diff --git a/doc/chan_mobile.txt b/doc/chan_mobile.txt +new file mode 100644 +index 0000000..5c8cafb +--- /dev/null ++++ b/doc/chan_mobile.txt +@@ -0,0 +1,240 @@ ++chan_mobile ++ ++Asterisk Channel Driver to allow Bluetooth Cell/Mobile Phones to be used as FXO devices, and Headsets as FXS devices. ++ ++ ++Features :- ++ ++Multiple Bluetooth Adapters supported. ++Multiple phones can be connected. ++Multiple headsets can be connected. ++Asterisk automatically connects to each configured mobile phone / headset when it comes in range. ++CLI command to discover bluetooth devices. ++Inbound calls on the mobile network to the mobile phones are handled by Asterisk, just like inbound calls on a Zap channel. ++CLI passed through on inbound calls. ++Dial outbound on a mobile phone using Dial(Mobile/device/nnnnnnn) in the dialplan. ++Dial a headset using Dial(Mobile/device) in the dialplan. ++Application MobileStatus can be used in the dialplan to see if a mobile phone / headset is connected. ++Supports devicestate for dialplan hinting. ++Supports Inbound and Outbound SMS. ++Supports 'channel' groups for implementing 'GSM Gateways' ++ ++ ++Requirements :- ++ ++In order to use chan_mobile, you must have a working bluetooth subsystem on your Asterisk box. ++This means one or more working bluetooth adapters, and the BlueZ packages. ++ ++Any bluetooth adapter supported by the Linux kernel will do, including usb bluetooth dongles. ++ ++The BlueZ package you need is bluez-utils. If you are using a GUI then you might want to install bluez-pin also. ++You also need libbluetooth, and libbluetooth-dev if you are compiling Asterisk from source. ++ ++You need to get bluetooth working with your phone before attempting to use chan_mobile. ++This means 'pairing' your phone or headset with your Asterisk box. I dont describe how to do this here as the process ++differs from distro to distro. You only need to pair once per adapter. ++ ++See www.bluez.org for details about setting up Bluetooth under Linux. ++ ++ ++Concepts :- ++ ++chan_mobile deals with both bluetooth adapters and bluetooth devices. This means you need to tell chan_mobile about the ++bluetooth adapters installed in your server as well as the devices (phones / headsets) you wish to use. ++ ++chan_mobile currently only allows one device (phone or headset) to be connected to an adapter at a time. This means you need ++one adapter for each device you wish to use simultaneously. Much effort has gone into trying to make multiple devices per adapter ++work, but in short it doesnt. ++ ++Periodically chan_mobile looks at each configured adapter, and if it is not in use (i.e. no device connected) will initiate a ++search for devices configured to use this adapater that may be in range. If it finds one it will connect the device and it ++will be available for Asterisk to use. When the device goes out of range, chan_mobile will disconnect the device and the adapter ++will become available for other devices. ++ ++ ++Configuring chan_mobile :- ++ ++The configuration file for chan_mobile is /etc/asterisk/mobile.conf. It is a normal Asterisk config file consisting of sections and key=value pairs. ++ ++See configs/mobile.conf.sample for an example and an explanation of the configuration. ++ ++ ++Using chan_mobile :- ++ ++chan_mobile.so must be loaded either by loading it using the Asterisk CLI, or by adding it to /etc/asterisk/modules.conf ++ ++Search for your bluetooth devices using the CLI command 'mobile search'. Be patient with this command as ++it will take 8 - 10 seconds to do the discovery. This requires a free adapter. ++ ++Headsets will generally have to be put into 'pairing' mode before they will show up here. ++ ++This will return something like the following :- ++ ++*CLI> mobile search ++Address Name Usable Type Port ++00:12:56:90:6E:00 LG TU500 Yes Phone 4 ++00:80:C8:35:52:78 Toaster No Headset 0 ++00:0B:9E:11:74:A5 Hello II Plus Yes Headset 1 ++00:0F:86:0E:AE:42 Daves Blackberry Yes Phone 7 ++ ++This is a list of all bluetooth devices seen and whether or not they are usable with chan_mobile. ++The Address field contains the 'bd address' of the device. This is like an ethernet mac address. ++The Name field is whatever is configured into the device as its name. ++The Usable field tells you whether or not the device supports the Bluetooth Handsfree Profile or Headset profile. ++The Type field tells you whether the device is usable as a Phone line (FXO) or a headset (FXS) ++The Port field is the number to put in the configuration file. ++ ++Choose which device(s) you want to use and edit /etc/asterisk/mobile.conf. There is a sample included ++with the Asterisk-addons source under configs/mobile.conf.sample. ++ ++Be sure to configure the right bd address and port number from the search. If you want inbound ++calls on a device to go to a specific context, add a context= line, otherwise the default will ++be used. The 'id' of the device [bitinbrackets] can be anything you like, just make it unique. ++ ++If you are configuring a Headset be sure to include the type=headset line, if left out it defaults ++to phone. ++ ++The CLI command 'mobile show devices' can be used at any time to show the status of configured devices, ++and whether or not the device is capable of sending / receiving SMS via bluetooth. ++ ++*CLI> mobile show devices ++ID Address Group Adapter Connected State SMS ++headset 00:0B:9E:11:AE:C6 0 blue No Init No ++LGTU550 00:E0:91:7F:46:44 1 dlink No Init No ++*CLI> ++ ++As each phone is connected you will see a message on the Asterisk console :- ++ ++ Loaded chan_mobile.so => (Bluetooth Mobile Device Channel Driver) ++ -- Bluetooth Device blackberry has connected. ++ -- Bluetooth Device dave has connected. ++ ++To make outbound calls, add something to you Dialplan like the following :- (modify to suit) ++ ++; Calls via LGTU5500 ++exten => _9X.,1,Dial(Mobile/LGTU550/${EXTEN:1},45) ++exten => _9X.,n,Hangup ++ ++To use channel groups, add an entry to each phones definition in mobile.conf like group=n ++where n is a number. ++ ++Then if you do something like Dial(Mobile/g1/123456) Asterisk will dial 123456 on the first ++connected free phone in group 1. ++ ++Phones which do not have a specific 'group=n' will be in group 0. ++ ++ ++To dial out on a headset, you need to use some other mechanism, because the headset is not likely ++to have all the needed buttons on it. res_clioriginate is good for this :- ++ ++*CLI> originate Mobile/headset extension NNNNN@context ++ ++This will call your headset, once you answer, Asterisk will call NNNNN at context context ++ ++Dialplan hints :- ++ ++chan_mobile supports 'device status' so you can do somthing like ++ ++exten => 1234,hint,SIP/30&Mobile/dave&Mobile/blackberry ++ ++ ++MobileStatus Application :- ++ ++chan_mobile also registers an application named MobileStatus. You can use this in your Dialplan ++to determine the 'state' of a device. ++ ++For example, suppose you wanted to call dave's extension, but only if he was in the office. You could ++test to see if his mobile phone was attached to Asterisk, if it is dial his extension, otherwise dial his ++mobile phone. ++ ++exten => 40,1,MobileStatus(dave,DAVECELL) ++exten => 40,2,GotoIf($["${DAVECELL}" = "1"]?3:5) ++exten => 40,3,Dial(ZAP/g1/0427466412,45,tT) ++exten => 40,4,Hangup ++exten => 40,5,Dial(SIP/40,45,tT) ++exten => 40,6,Hangup ++ ++MobileStatus sets the value of the given variable to :- ++ ++1 = Disconnected. i.e. Device not in range of Asterisk, or turned off etc etc ++2 = Connected and Not on a call. i.e. Free ++3 = Connected and on a call. i.e. Busy ++ ++ ++SMS Sending / Receiving ++ ++If Asterisk has detected your mobile phone is capable of SMS via bluetooth, you will be able to send and ++receive SMS. ++ ++Incoming SMS's cause Asterisk to create an inbound call to the context you defined in mobile.conf or the default ++context if you did not define one. The call will start at extension 'sms'. Two channel variables will be available, ++SMSSRC = the number of the originator of the SMS and SMSTXT which is the text of the SMS. ++This is not a voice call, so grab the values of the variables and hang the call up. ++ ++So, to handle incoming SMS's, do something like the following in your dialplan ++ ++[incoming-mobile] ++exten => sms,1,Verbose(Incoming SMS from ${SMSSRC} ${SMSTXT}) ++exten => sms,n,Hangup() ++ ++The above will just print the message on the console. ++ ++If you use res_jabber, you could do something like this :- ++ ++[incoming-mobile] ++exten => sms,1,JabberSend(transport,user@jabber.somewhere.com,SMS from ${SMSRC} ${SMSTXT}) ++exten => sms,2,Hangup() ++ ++To send an SMS, use the application MobileSendSMS like the following :- ++ ++exten => 99,1,MobileSendSMS(dave,0427123456,Hello World) ++ ++This will send 'Hello World' via device 'dave' to '0427123456' ++ ++ ++DTMF Debouncing :- ++ ++DTMF detection varies from phone to phone. There is a configuration variable that allows you to tune ++this to your needs. e.g. in mobile.conf ++ ++[LGTU550] ++address=00:12:56:90:6E:00 ++port=4 ++context=incoming-mobile ++dtmfskip=50 ++ ++change dtmfskip to suit your phone. The default is 200. The larger the number, the more chance of missed DTMF. ++The smaller the number the more chance of multiple digits being detected. ++ ++ ++Debugging :- ++ ++Different phone manufacturers have different interpretations of the Bluetooth Handsfree Profile Spec. ++This means that not all phones work the same way, particularly in the connection setup / initialisation ++sequence. I've tried to make chan_mobile as general as possible, but it may need modification to ++support some phone i've never tested. ++ ++Some phones, most notably Sony Ericsson 'T' series, dont quite conform to the Bluetooth HFP spec. ++chan_mobile will detect these and adapt accordingly. The T-610 and T-630 have been tested and ++work fine. ++ ++If your phone doesnt behave has expected, turn on Asterisk debugging with 'core set debug 1'. ++ ++This will log a bunch of debug messages indicating what the phone is doing, importantly the rfcomm ++conversation between Asterisk and the phone. This can be used to sort out what your phone is doing ++and make chan_mobile support it. ++ ++Be aware also, that just about all mobile phones behave differently. For example my LG TU500 wont dial unless ++the phone is a the 'idle' screen. i.e. if the phone is showing a 'menu' on the display, when you dial via ++Asterisk, the call will not work. chan_mobile handles this, but there may be other phones that do ++other things too... ++ ++Important: Watch what your mobile phone is doing the first few times. Asterisk wont make random calls but ++if chan_mobile fails to hangup for some reason and you get a huge bill from your telco, dont blame me ;) ++ ++ ++Feedback, Support, Please can you make Mobile Phone X work... etc :- ++ ++as always, bugs should be reported at http://bugs.digium.com ++ ++email me at david.bowerman at gmail.com or dseeb_ on #asterisk & #asterisk-dev irc. +diff --git a/makeopts.in b/makeopts.in +index a81503c..ac67901 100644 +--- a/makeopts.in ++++ b/makeopts.in +@@ -67,6 +67,9 @@ AST_DECLARATION_AFTER_STATEMENT=@AST_DECLARATION_AFTER_STATEMENT@ + ASOUND_INCLUDE=@ALSA_INCLUDE@ + ASOUND_LIB=@ALSA_LIB@ + ++BLUETOOTH_LIB=@BLUETOOTH_LIB@ ++BLUETOOTH_INCLUDE=@BLUETOOTH_INCLUDE@ ++ + CURL_INCLUDE=@CURL_INCLUDE@ + CURL_LIB=@CURL_LIB@ + +-- +1.5.3.7 + diff --git a/asterisk-1.4.16.2-initscripts.patch b/asterisk-1.4.16.2-initscripts.patch new file mode 100644 index 0000000..22b8659 --- /dev/null +++ b/asterisk-1.4.16.2-initscripts.patch @@ -0,0 +1,131 @@ +From e6189af3e4b019d399b2232bd043909f17d9ac05 Mon Sep 17 00:00:00 2001 +From: Jeffrey C. Ollie +Date: Sun, 18 Nov 2007 21:47:59 -0600 +Subject: [PATCH] Modify init scripts for better Fedora compatibility. + +--- + contrib/init.d/rc.redhat.asterisk | 54 +++++++++++++++---------------------- + contrib/sysconfig/asterisk | 9 ++++++ + 2 files changed, 31 insertions(+), 32 deletions(-) + create mode 100644 contrib/sysconfig/asterisk + +diff --git a/contrib/init.d/rc.redhat.asterisk b/contrib/init.d/rc.redhat.asterisk +index 27d633e..f160af4 100755 +--- a/contrib/init.d/rc.redhat.asterisk ++++ b/contrib/init.d/rc.redhat.asterisk +@@ -3,7 +3,7 @@ + # + # asterisk Starts, Stops and Reloads Asterisk. + # +-# chkconfig: 2345 90 60 ++# chkconfig: - 90 60 + # description: Asterisk PBX and telephony daemon. + # processname: asterisk + # pidfile: /var/run/asterisk.pid +@@ -20,59 +20,49 @@ + # - Added support for -U and -G command line options + # - Modified "reload" to call asterisk -rx 'reload' + +-# Use this option to specify a different configuration directory +-#AST_CONFIG=/etc/asterisk ++# Do not modify this script to change any of the settings, instead ++# edit /etc/sysconfig/asterisk ++ ++# Specify the configuration file ++AST_CONFIG=/etc/asterisk/asterisk.conf + + # Installation directory + AST_SBIN=/usr/sbin + +-# Uncomment the following and set them to the user/groups that you +-# want to run Asterisk as. NOTE: this requires substantial work to +-# be sure that Asterisk's environment has permission to write the +-# files required for its operation, including logs, its comm +-# socket, the asterisk database, etc. +-#AST_USER="asterisk" +-#AST_GROUP="asterisk" ++# The user/group that Asterisk will run as. ++AST_USER="asterisk" ++AST_GROUP="asterisk" + +-# Source function library. +-. /etc/rc.d/init.d/functions ++# Allow configuration overrides in /etc/sysconfig/asterisk ++CONFIG0=`readlink $0` ++if [ "$CONFIG0" = "" ]; then ++ CONFIGFILE=/etc/sysconfig/`basename $0` ++else ++ CONFIGFILE=/etc/sysconfig/`basename $CONFIG0` ++fi ++[ -f $CONFIGFILE ] && . $CONFIGFILE + + if ! [ -x $AST_SBIN/asterisk ] ; then + echo "ERROR: $AST_SBIN/asterisk not found" + exit 0 + fi + +-if ! [ -d $AST_CONFIG ] ; then +- echo "ERROR: $AST_CONFIG directory not found" ++if ! [ -f $AST_CONFIG ] ; then ++ echo "ERROR: $AST_CONFIG not found" + exit 0 + fi + +-# Uncomment this ONLY if you know what you are doing. +-# export LD_ASSUME_KERNEL=2.4.1 ++# Source function library. ++. /etc/rc.d/init.d/functions + + # Full path to asterisk binary + DAEMON=$AST_SBIN/asterisk + +-# Full path to safe_asterisk script +-SAFE_ASTERISK=$AST_SBIN/safe_asterisk +- +-# Allow configuration overrides in /etc/sysconfig/asterisk +-CONFIG0=`readlink $0` +-if [ "$CONFIG0" = "" ]; then +- CONFIGFILE=/etc/sysconfig/`basename $0` +-else +- CONFIGFILE=/etc/sysconfig/`basename $CONFIG0` +-fi +-[ -x $CONFIGFILE ] && . $CONFIGFILE +- + RETVAL=0 + + start() { + # Start daemons. + echo -n $"Starting asterisk: " +- if [ -f $SAFE_ASTERISK ] ; then +- DAEMON=$SAFE_ASTERISK +- fi + if [ $AST_USER ] ; then + ASTARGS="-U $AST_USER" + fi +@@ -91,7 +81,7 @@ start() { + + stop() { + # Stop daemons. +- echo -n $"Shutting down asterisk: " ++ echo -n $"Stopping asterisk: " + killproc asterisk + RETVAL=$? + [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/asterisk +diff --git a/contrib/sysconfig/asterisk b/contrib/sysconfig/asterisk +new file mode 100644 +index 0000000..05f5adb +--- /dev/null ++++ b/contrib/sysconfig/asterisk +@@ -0,0 +1,9 @@ ++# Specify the configuration file ++AST_CONFIG=/etc/asterisk/asterisk.conf ++ ++# Installation directory ++AST_SBIN=/usr/sbin ++ ++# The user/group that Asterisk will run as. ++AST_USER="asterisk" ++AST_GROUP="asterisk" +-- +1.5.3.7 + diff --git a/asterisk-1.4.16.2-libcap.patch b/asterisk-1.4.16.2-libcap.patch new file mode 100644 index 0000000..22a25dc --- /dev/null +++ b/asterisk-1.4.16.2-libcap.patch @@ -0,0 +1,160 @@ +From d29473ff4432822f8784d3b2142507ce7a2e8b55 Mon Sep 17 00:00:00 2001 +From: Jeffrey C. Ollie +Date: Thu, 8 Nov 2007 16:42:56 -0600 +Subject: [PATCH] Backport patch that lets Asterisk set the TOS byte even when running as non-root. + +--- + CHANGES | 5 +++++ + configure.ac | 5 +++++ + doc/security.txt | 7 +++++++ + main/Makefile | 3 +++ + main/asterisk.c | 30 +++++++++++++++++++++++++----- + makeopts.in | 3 +++ + 6 files changed, 48 insertions(+), 5 deletions(-) + +diff --git a/CHANGES b/CHANGES +index faa1855..7825d58 100644 +--- a/CHANGES ++++ b/CHANGES +@@ -339,3 +339,8 @@ Changes since Asterisk 1.2: + 1. aelparse -- compile .ael files outside of asterisk + * New manager events: + 1. OriginateResponse event comes to replace OriginateSuccess and OriginateFailure ++ ++ * Ability to use libcap to set high ToS bits when non-root on ++ Linux. If configure is unable to find libcap then you can use ++ --with-cap to specify the path. ++ +diff --git a/configure.ac b/configure.ac +index 26a5735..cd4e87b 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -174,6 +174,7 @@ AC_SUBST(AST_DEVMODE) + + AST_EXT_LIB_SETUP([ALSA], [Advanced Linux Sound Architecture], [asound]) + AST_EXT_LIB_SETUP([CURL], [cURL], [curl]) ++AST_EXT_LIB_SETUP([CAP], [POSIX 1.e capabilities], [cap]) + AST_EXT_LIB_SETUP([CURSES], [curses], [curses]) + AST_EXT_LIB_SETUP([GNUTLS], [GNU TLS support (used for iksemel only)], [gnutls]) + AST_EXT_LIB_SETUP([GSM], [GSM], [gsm], [, or 'internal']) +@@ -400,6 +401,10 @@ AST_EXT_LIB_CHECK([ALSA], [asound], [snd_spcm_init], [alsa/asoundlib.h], [-lm -l + + AST_EXT_LIB_CHECK([CURSES], [curses], [initscr], [curses.h]) + ++if test "x${host_os}" = "xlinux-gnu" ; then ++ AST_EXT_LIB_CHECK([CAP], [cap], [cap_from_text], [sys/capability.h]) ++fi ++ + GSM_INTERNAL="yes" + AC_SUBST(GSM_INTERNAL) + GSM_SYSTEM="yes" +diff --git a/doc/security.txt b/doc/security.txt +index 0801679..3adf536 100644 +--- a/doc/security.txt ++++ b/doc/security.txt +@@ -28,6 +28,13 @@ The IAX2 protocol supports strong RSA key authentication as well as + AES encryption of voice and signalling. The SIP channel does not + support encryption in this version of Asterisk. + ++By default, if you have libcap available, Asterisk will try to retain the ++CAP_NET_ADMIN capability when running as a non-root user. If you do not need ++that capability you may want to configure Asterisk with --without-cap; however, ++this will prevent Asterisk from being able to mark high ToS bits under Linux. ++More information on CAP_NET_ADMIN is available at: ++http://www.lids.org/lids-howto/node48.html ++ + * DIALPLAN SECURITY + + First and foremost remember this: +diff --git a/main/Makefile b/main/Makefile +index 4f405b5..cee9e13 100644 +--- a/main/Makefile ++++ b/main/Makefile +@@ -55,6 +55,9 @@ ifneq ($(findstring $(OSARCH), linux-gnu uclinux linux-uclibc ),) + ifneq ($(findstring LOADABLE_MODULES,$(MENUSELECT_CFLAGS)),) + AST_LIBS+=-ldl + endif ++ ifneq (x$(CAP_LIB),x) ++ AST_LIBS+=$(CAP_LIB) ++ endif + AST_LIBS+=-lpthread $(EDITLINE_LIB) -lm -lresolv + else + AST_LIBS+=$(EDITLINE_LIB) -lm +diff --git a/main/asterisk.c b/main/asterisk.c +index 202a190..bd8cd9b 100644 +--- a/main/asterisk.c ++++ b/main/asterisk.c +@@ -82,13 +82,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + #include + #ifdef linux + #include +-#endif ++#ifdef HAVE_CAP ++#include ++#endif /* HAVE_CAP */ ++#endif /* linux */ + #include + +-#ifdef linux +-#include +-#endif +- + #if defined(__FreeBSD__) || defined( __NetBSD__ ) || defined(SOLARIS) + #include + #if defined(SOLARIS) +@@ -2734,6 +2733,10 @@ int main(int argc, char *argv[]) + } + + if (runuser && !ast_test_flag(&ast_options, AST_OPT_FLAG_REMOTE)) { ++#ifdef HAVE_CAP ++ cap_t cap; ++ int has_cap = 1; ++#endif /* HAVE_CAP */ + struct passwd *pw; + pw = getpwnam(runuser); + if (!pw) { +@@ -2744,6 +2747,12 @@ int main(int argc, char *argv[]) + ast_log(LOG_ERROR, "Asterisk started as nonroot, but runuser '%s' requested.\n", runuser); + exit(1); + } ++#ifdef HAVE_CAP ++ if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) { ++ ast_log(LOG_WARNING, "Unable to keep capabilities.\n"); ++ has_cap = 0; ++ } ++#endif /* HAVE_CAP */ + if (!rungroup) { + if (setgid(pw->pw_gid)) { + ast_log(LOG_WARNING, "Unable to setgid to %d!\n", (int)pw->pw_gid); +@@ -2760,6 +2769,17 @@ int main(int argc, char *argv[]) + } + if (option_verbose) + ast_verbose("Running as user '%s'\n", runuser); ++#ifdef HAVE_CAP ++ if (has_cap) { ++ cap = cap_from_text("cap_net_admin=ep"); ++ if (cap_set_proc(cap)) { ++ ast_log(LOG_WARNING, "Unable to install capabilities.\n"); ++ } ++ if (cap_free(cap)) { ++ ast_log(LOG_WARNING, "Unable to drop capabilities.\n"); ++ } ++ } ++#endif /* HAVE_CAP */ + } + + #endif /* __CYGWIN__ */ +diff --git a/makeopts.in b/makeopts.in +index 8138dd8..a81503c 100644 +--- a/makeopts.in ++++ b/makeopts.in +@@ -189,3 +189,6 @@ TERMCAP_DIR=@TERMCAP_DIR@ + TINFO_INCLUDE=@TINFO_INCLUDE@ + TINFO_LIB=@TINFO_LIB@ + TINFO_DIR=@TINFO_DIR@ ++ ++CAP_LIB=@CAP_LIB@ ++CAP_INCLUDE=@CAP_INCLUDE@ +-- +1.5.3.7 + diff --git a/asterisk-1.4.16.2-optimization.patch b/asterisk-1.4.16.2-optimization.patch new file mode 100644 index 0000000..f6b1825 --- /dev/null +++ b/asterisk-1.4.16.2-optimization.patch @@ -0,0 +1,48 @@ +From d0667633dd063013592eee40a52b1af0af340420 Mon Sep 17 00:00:00 2001 +From: Jeffrey C. Ollie +Date: Thu, 8 Nov 2007 16:42:14 -0600 +Subject: [PATCH] Pick proper optimization flags for Fedora. + +--- + Makefile | 16 ++++++++++++---- + 1 files changed, 12 insertions(+), 4 deletions(-) + +diff --git a/Makefile b/Makefile +index 04a7b44..e8fae1d 100644 +--- a/Makefile ++++ b/Makefile +@@ -196,10 +196,16 @@ ifeq ($(OSARCH),linux-gnu) + endif + + ifeq ($(findstring -save-temps,$(ASTCFLAGS)),) +-ASTCFLAGS+=-pipe ++ ifeq ($(findstring -pipe,$(ASTCFLAGS)),) ++ ASTCFLAGS+=-pipe ++ endif ++endif ++ ++ifeq ($(findstring -Wall,$(ASTCFLAGS)),) ++ ASTCFLAGS+=-Wall + endif + +-ASTCFLAGS+=-Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations $(DEBUG) ++ASTCFLAGS+=-Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations $(DEBUG) + + ASTCFLAGS+=-include $(ASTTOPDIR)/include/asterisk/autoconfig.h + +@@ -212,8 +218,10 @@ ifneq ($(findstring BSD,$(OSARCH)),) + ASTLDFLAGS+=-L/usr/local/lib + endif + +-ifneq ($(PROC),ultrasparc) +- ASTCFLAGS+=$(shell if $(CC) -march=$(PROC) -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=$(PROC)"; fi) ++ifeq ($(findstring -march,$(ASTCFLAGS)),) ++ ifneq ($(PROC),ultrasparc) ++ ASTCFLAGS+=$(shell if $(CC) -march=$(PROC) -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=$(PROC)"; fi) ++ endif + endif + + ifeq ($(PROC),ppc) +-- +1.5.3.7 + diff --git a/asterisk-1.4.16.2-spandspfax.patch b/asterisk-1.4.16.2-spandspfax.patch new file mode 100644 index 0000000..11fc7b1 --- /dev/null +++ b/asterisk-1.4.16.2-spandspfax.patch @@ -0,0 +1,763 @@ +From 37948f65ec97ddbc323e2a92f4537a03f46c1559 Mon Sep 17 00:00:00 2001 +From: Jeffrey C. Ollie +Date: Mon, 12 Nov 2007 15:51:12 -0600 +Subject: [PATCH] Add FAX apps. + +--- + apps/app_rxfax.c | 380 ++++++++++++++++++++++++++++++++++++++++ + apps/app_txfax.c | 306 ++++++++++++++++++++++++++++++++ + build_tools/menuselect-deps.in | 1 + + configure.ac | 3 + + makeopts.in | 3 + + 5 files changed, 693 insertions(+), 0 deletions(-) + create mode 100644 apps/app_rxfax.c + create mode 100644 apps/app_txfax.c + +diff --git a/apps/app_rxfax.c b/apps/app_rxfax.c +new file mode 100644 +index 0000000..bd81ea1 +--- /dev/null ++++ b/apps/app_rxfax.c +@@ -0,0 +1,380 @@ ++/* ++ * Asterisk -- A telephony toolkit for Linux. ++ * ++ * Trivial application to receive a TIFF FAX file ++ * ++ * Copyright (C) 2003, Steve Underwood ++ * ++ * Steve Underwood ++ * ++ * This program is free software, distributed under the terms of ++ * the GNU General Public License ++ */ ++ ++/*** MODULEINFO ++ spandsp ++***/ ++ ++#include "asterisk.h" ++ ++ASTERISK_FILE_VERSION(__FILE__, "$Revision:$") ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "asterisk/lock.h" ++#include "asterisk/file.h" ++#include "asterisk/logger.h" ++#include "asterisk/channel.h" ++#include "asterisk/pbx.h" ++#include "asterisk/module.h" ++#include "asterisk/manager.h" ++ ++#ifndef AST_MODULE ++#define AST_MODULE "app_rxfax" ++#endif ++ ++static char *app = "RxFAX"; ++ ++static char *synopsis = "Receive a FAX to a file"; ++ ++static char *descrip = ++" RxFAX(filename[|caller][|debug]): Receives a FAX from the channel into the\n" ++"given filename. If the file exists it will be overwritten. The file\n" ++"should be in TIFF/F format.\n" ++"The \"caller\" option makes the application behave as a calling machine,\n" ++"rather than the answering machine. The default behaviour is to behave as\n" ++"an answering machine.\n" ++"Uses LOCALSTATIONID to identify itself to the remote end.\n" ++" LOCALHEADERINFO to generate a header line on each page.\n" ++"Sets REMOTESTATIONID to the sender CSID.\n" ++" FAXPAGES to the number of pages received.\n" ++" FAXBITRATE to the transmition rate.\n" ++" FAXRESOLUTION to the resolution.\n" ++"Returns -1 when the user hangs up.\n" ++"Returns 0 otherwise.\n"; ++ ++#define MAX_BLOCK_SIZE 240 ++ ++static void span_message(int level, const char *msg) ++{ ++ int ast_level; ++ ++ if (level == SPAN_LOG_WARNING) ++ ast_level = __LOG_WARNING; ++ else if (level == SPAN_LOG_WARNING) ++ ast_level = __LOG_WARNING; ++ else ++ ast_level = __LOG_DEBUG; ++ ast_log(ast_level, __FILE__, __LINE__, __PRETTY_FUNCTION__, msg); ++} ++/*- End of function --------------------------------------------------------*/ ++ ++#if 0 ++static void t30_flush(t30_state_t *s, int which) ++{ ++ /* TODO: */ ++} ++/*- End of function --------------------------------------------------------*/ ++#endif ++ ++static void phase_e_handler(t30_state_t *s, void *user_data, int result) ++{ ++ struct ast_channel *chan; ++ t30_stats_t t; ++ char local_ident[21]; ++ char far_ident[21]; ++ char buf[11]; ++ ++ chan = (struct ast_channel *) user_data; ++ if (result == T30_ERR_OK) ++ { ++ t30_get_transfer_statistics(s, &t); ++ t30_get_far_ident(s, far_ident); ++ t30_get_local_ident(s, local_ident); ++ ast_log(LOG_DEBUG, "==============================================================================\n"); ++ ast_log(LOG_DEBUG, "Fax successfully received.\n"); ++ ast_log(LOG_DEBUG, "Remote station id: %s\n", far_ident); ++ ast_log(LOG_DEBUG, "Local station id: %s\n", local_ident); ++ ast_log(LOG_DEBUG, "Pages transferred: %i\n", t.pages_transferred); ++ ast_log(LOG_DEBUG, "Image resolution: %i x %i\n", t.x_resolution, t.y_resolution); ++ ast_log(LOG_DEBUG, "Transfer Rate: %i\n", t.bit_rate); ++ ast_log(LOG_DEBUG, "==============================================================================\n"); ++ manager_event(EVENT_FLAG_CALL, ++ "FaxReceived", "Channel: %s\nExten: %s\nCallerID: %s\nRemoteStationID: %s\nLocalStationID: %s\nPagesTransferred: %i\nResolution: %i\nTransferRate: %i\nFileName: %s\n", ++ chan->name, ++ chan->exten, ++ (chan->cid.cid_num) ? chan->cid.cid_num : "", ++ far_ident, ++ local_ident, ++ t.pages_transferred, ++ t.y_resolution, ++ t.bit_rate, ++ s->rx_file); ++ pbx_builtin_setvar_helper(chan, "REMOTESTATIONID", far_ident); ++ snprintf(buf, sizeof(buf), "%i", t.pages_transferred); ++ pbx_builtin_setvar_helper(chan, "FAXPAGES", buf); ++ snprintf(buf, sizeof(buf), "%i", t.y_resolution); ++ pbx_builtin_setvar_helper(chan, "FAXRESOLUTION", buf); ++ snprintf(buf, sizeof(buf), "%i", t.bit_rate); ++ pbx_builtin_setvar_helper(chan, "FAXBITRATE", buf); ++ } ++ else ++ { ++ ast_log(LOG_DEBUG, "==============================================================================\n"); ++ ast_log(LOG_DEBUG, "Fax receive not successful - result (%d) %s.\n", result, t30_completion_code_to_str(result)); ++ ast_log(LOG_DEBUG, "==============================================================================\n"); ++ } ++} ++/*- End of function --------------------------------------------------------*/ ++ ++static void phase_d_handler(t30_state_t *s, void *user_data, int result) ++{ ++ struct ast_channel *chan; ++ t30_stats_t t; ++ ++ chan = (struct ast_channel *) user_data; ++ if (result) ++ { ++ t30_get_transfer_statistics(s, &t); ++ ast_log(LOG_DEBUG, "==============================================================================\n"); ++ ast_log(LOG_DEBUG, "Pages transferred: %i\n", t.pages_transferred); ++ ast_log(LOG_DEBUG, "Image size: %i x %i\n", t.width, t.length); ++ ast_log(LOG_DEBUG, "Image resolution %i x %i\n", t.x_resolution, t.y_resolution); ++ ast_log(LOG_DEBUG, "Transfer Rate: %i\n", t.bit_rate); ++ ast_log(LOG_DEBUG, "Bad rows %i\n", t.bad_rows); ++ ast_log(LOG_DEBUG, "Longest bad row run %i\n", t.longest_bad_row_run); ++ ast_log(LOG_DEBUG, "Compression type %i\n", t.encoding); ++ ast_log(LOG_DEBUG, "Image size (bytes) %i\n", t.image_size); ++ ast_log(LOG_DEBUG, "==============================================================================\n"); ++ } ++} ++/*- End of function --------------------------------------------------------*/ ++ ++static int rxfax_exec(struct ast_channel *chan, void *data) ++{ ++ int res = 0; ++ char template_file[256]; ++ char target_file[256]; ++ char *s; ++ char *t; ++ char *v; ++ const char *x; ++ int option; ++ int len; ++ int i; ++ fax_state_t fax; ++ int calling_party; ++ int verbose; ++ int samples; ++ ++ struct ast_module_user *u; ++ struct ast_frame *inf = NULL; ++ struct ast_frame outf; ++ ++ int original_read_fmt; ++ int original_write_fmt; ++ ++ uint8_t __buf[sizeof(uint16_t)*MAX_BLOCK_SIZE + 2*AST_FRIENDLY_OFFSET]; ++ uint8_t *buf = __buf + AST_FRIENDLY_OFFSET; ++ ++ if (chan == NULL) ++ { ++ ast_log(LOG_WARNING, "Fax receive channel is NULL. Giving up.\n"); ++ return -1; ++ } ++ ++ span_set_message_handler(span_message); ++ ++ /* The next few lines of code parse out the filename and header from the input string */ ++ if (data == NULL) ++ { ++ /* No data implies no filename or anything is present */ ++ ast_log(LOG_WARNING, "Rxfax requires an argument (filename)\n"); ++ return -1; ++ } ++ ++ calling_party = FALSE; ++ verbose = FALSE; ++ target_file[0] = '\0'; ++ ++ for (option = 0, v = s = data; v; option++, s++) ++ { ++ t = s; ++ v = strchr(s, '|'); ++ s = (v) ? v : s + strlen(s); ++ strncpy((char *) buf, t, s - t); ++ buf[s - t] = '\0'; ++ if (option == 0) ++ { ++ /* The first option is always the file name */ ++ len = s - t; ++ if (len > 255) ++ len = 255; ++ strncpy(target_file, t, len); ++ target_file[len] = '\0'; ++ /* Allow the use of %d in the file name for a wild card of sorts, to ++ create a new file with the specified name scheme */ ++ if ((x = strchr(target_file, '%')) && x[1] == 'd') ++ { ++ strcpy(template_file, target_file); ++ i = 0; ++ do ++ { ++ snprintf(target_file, 256, template_file, 1); ++ i++; ++ } ++ while (ast_fileexists(target_file, "", chan->language) != -1); ++ } ++ } ++ else if (strncmp("caller", t, s - t) == 0) ++ { ++ calling_party = TRUE; ++ } ++ else if (strncmp("debug", t, s - t) == 0) ++ { ++ verbose = TRUE; ++ } ++ } ++ ++ /* Done parsing */ ++ ++ u = ast_module_user_add(chan); ++ ++ if (chan->_state != AST_STATE_UP) ++ { ++ /* Shouldn't need this, but checking to see if channel is already answered ++ * Theoretically asterisk should already have answered before running the app */ ++ res = ast_answer(chan); ++ } ++ ++ if (!res) ++ { ++ original_read_fmt = chan->readformat; ++ if (original_read_fmt != AST_FORMAT_SLINEAR) ++ { ++ res = ast_set_read_format(chan, AST_FORMAT_SLINEAR); ++ if (res < 0) ++ { ++ ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n"); ++ return -1; ++ } ++ } ++ original_write_fmt = chan->writeformat; ++ if (original_write_fmt != AST_FORMAT_SLINEAR) ++ { ++ res = ast_set_write_format(chan, AST_FORMAT_SLINEAR); ++ if (res < 0) ++ { ++ ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up\n"); ++ res = ast_set_read_format(chan, original_read_fmt); ++ if (res) ++ ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); ++ return -1; ++ } ++ } ++ fax_init(&fax, calling_party); ++ if (verbose) ++ fax.logging.level = SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW; ++ x = pbx_builtin_getvar_helper(chan, "LOCALSTATIONID"); ++ if (x && x[0]) ++ t30_set_local_ident(&fax.t30_state, x); ++ x = pbx_builtin_getvar_helper(chan, "LOCALHEADERINFO"); ++ if (x && x[0]) ++ t30_set_header_info(&fax.t30_state, x); ++ t30_set_rx_file(&fax.t30_state, target_file, -1); ++ //t30_set_phase_b_handler(&fax.t30_state, phase_b_handler, chan); ++ t30_set_phase_d_handler(&fax.t30_state, phase_d_handler, chan); ++ t30_set_phase_e_handler(&fax.t30_state, phase_e_handler, chan); ++ t30_set_ecm_capability(&fax.t30_state, TRUE); ++ t30_set_supported_compressions(&fax.t30_state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION); ++ while (ast_waitfor(chan, -1) > -1) ++ { ++ inf = ast_read(chan); ++ if (inf == NULL) ++ { ++ res = -1; ++ break; ++ } ++ if (inf->frametype == AST_FRAME_VOICE) ++ { ++ if (fax_rx(&fax, inf->data, inf->samples)) ++ break; ++ samples = (inf->samples <= MAX_BLOCK_SIZE) ? inf->samples : MAX_BLOCK_SIZE; ++ len = fax_tx(&fax, (int16_t *) &buf[AST_FRIENDLY_OFFSET], samples); ++ if (len) ++ { ++ memset(&outf, 0, sizeof(outf)); ++ outf.frametype = AST_FRAME_VOICE; ++ outf.subclass = AST_FORMAT_SLINEAR; ++ outf.datalen = len*sizeof(int16_t); ++ outf.samples = len; ++ outf.data = &buf[AST_FRIENDLY_OFFSET]; ++ outf.offset = AST_FRIENDLY_OFFSET; ++ outf.src = "RxFAX"; ++ if (ast_write(chan, &outf) < 0) ++ { ++ ast_log(LOG_WARNING, "Unable to write frame to channel; %s\n", strerror(errno)); ++ break; ++ } ++ } ++ } ++ ast_frfree(inf); ++ } ++ if (inf == NULL) ++ { ++ ast_log(LOG_DEBUG, "Got hangup\n"); ++ res = -1; ++ } ++ if (original_read_fmt != AST_FORMAT_SLINEAR) ++ { ++ res = ast_set_read_format(chan, original_read_fmt); ++ if (res) ++ ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); ++ } ++ if (original_write_fmt != AST_FORMAT_SLINEAR) ++ { ++ res = ast_set_write_format(chan, original_write_fmt); ++ if (res) ++ ast_log(LOG_WARNING, "Unable to restore write format on '%s'\n", chan->name); ++ } ++ t30_terminate(&fax.t30_state); ++ } ++ else ++ { ++ ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name); ++ } ++ ast_module_user_remove(u); ++ return res; ++} ++/*- End of function --------------------------------------------------------*/ ++ ++static int unload_module(void) ++{ ++ int res; ++ ++ ast_module_user_hangup_all(); ++ ++ res = ast_unregister_application(app); ++ ++ ++ return res; ++} ++/*- End of function --------------------------------------------------------*/ ++ ++static int load_module(void) ++{ ++ return ast_register_application(app, rxfax_exec, synopsis, descrip); ++} ++/*- End of function --------------------------------------------------------*/ ++ ++AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Trivial FAX Receive Application"); ++ ++/*- End of file ------------------------------------------------------------*/ +diff --git a/apps/app_txfax.c b/apps/app_txfax.c +new file mode 100644 +index 0000000..713ecf1 +--- /dev/null ++++ b/apps/app_txfax.c +@@ -0,0 +1,306 @@ ++/* ++ * Asterisk -- A telephony toolkit for Linux. ++ * ++ * Trivial application to send a TIFF file as a FAX ++ * ++ * Copyright (C) 2003, Steve Underwood ++ * ++ * Steve Underwood ++ * ++ * This program is free software, distributed under the terms of ++ * the GNU General Public License ++ */ ++ ++/*** MODULEINFO ++ spandsp ++***/ ++ ++#include "asterisk.h" ++ ++ASTERISK_FILE_VERSION(__FILE__, "$Revision:$") ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "asterisk/lock.h" ++#include "asterisk/file.h" ++#include "asterisk/logger.h" ++#include "asterisk/channel.h" ++#include "asterisk/pbx.h" ++#include "asterisk/module.h" ++ ++#ifndef AST_MODULE ++#define AST_MODULE "app_txfax" ++#endif ++ ++static char *app = "TxFAX"; ++ ++static char *synopsis = "Send a FAX file"; ++ ++static char *descrip = ++" TxFAX(filename[|caller][|debug]): Send a given TIFF file to the channel as a FAX.\n" ++"The \"caller\" option makes the application behave as a calling machine,\n" ++"rather than the answering machine. The default behaviour is to behave as\n" ++"an answering machine.\n" ++"Uses LOCALSTATIONID to identify itself to the remote end.\n" ++" LOCALHEADERINFO to generate a header line on each page.\n" ++"Sets REMOTESTATIONID to the receiver CSID.\n" ++"Returns -1 when the user hangs up, or if the file does not exist.\n" ++"Returns 0 otherwise.\n"; ++ ++#define MAX_BLOCK_SIZE 240 ++ ++static void span_message(int level, const char *msg) ++{ ++ int ast_level; ++ ++ if (level == SPAN_LOG_WARNING) ++ ast_level = __LOG_WARNING; ++ else if (level == SPAN_LOG_WARNING) ++ ast_level = __LOG_WARNING; ++ else ++ ast_level = __LOG_DEBUG; ++ ast_log(ast_level, __FILE__, __LINE__, __PRETTY_FUNCTION__, msg); ++} ++/*- End of function --------------------------------------------------------*/ ++ ++#if 0 ++static void t30_flush(t30_state_t *s, int which) ++{ ++ /* TODO: */ ++} ++/*- End of function --------------------------------------------------------*/ ++#endif ++ ++static void phase_e_handler(t30_state_t *s, void *user_data, int result) ++{ ++ struct ast_channel *chan; ++ char far_ident[21]; ++ ++ chan = (struct ast_channel *) user_data; ++ if (result == T30_ERR_OK) ++ { ++ t30_get_far_ident(s, far_ident); ++ pbx_builtin_setvar_helper(chan, "REMOTESTATIONID", far_ident); ++ } ++ else ++ { ++ ast_log(LOG_DEBUG, "==============================================================================\n"); ++ ast_log(LOG_DEBUG, "Fax send not successful - result (%d) %s.\n", result, t30_completion_code_to_str(result)); ++ ast_log(LOG_DEBUG, "==============================================================================\n"); ++ } ++} ++/*- End of function --------------------------------------------------------*/ ++ ++static int txfax_exec(struct ast_channel *chan, void *data) ++{ ++ int res = 0; ++ char source_file[256]; ++ char *s; ++ char *t; ++ char *v; ++ const char *x; ++ int option; ++ int len; ++ fax_state_t fax; ++ int calling_party; ++ int verbose; ++ int samples; ++ ++ struct ast_module_user *u; ++ struct ast_frame *inf = NULL; ++ struct ast_frame outf; ++ ++ int original_read_fmt; ++ int original_write_fmt; ++ ++ uint8_t __buf[sizeof(uint16_t)*MAX_BLOCK_SIZE + 2*AST_FRIENDLY_OFFSET]; ++ uint8_t *buf = __buf + AST_FRIENDLY_OFFSET; ++ ++ if (chan == NULL) ++ { ++ ast_log(LOG_WARNING, "Fax transmit channel is NULL. Giving up.\n"); ++ return -1; ++ } ++ ++ span_set_message_handler(span_message); ++ ++ /* The next few lines of code parse out the filename and header from the input string */ ++ if (data == NULL) ++ { ++ /* No data implies no filename or anything is present */ ++ ast_log(LOG_WARNING, "Txfax requires an argument (filename)\n"); ++ return -1; ++ } ++ ++ calling_party = FALSE; ++ verbose = FALSE; ++ source_file[0] = '\0'; ++ ++ for (option = 0, v = s = data; v; option++, s++) ++ { ++ t = s; ++ v = strchr(s, '|'); ++ s = (v) ? v : s + strlen(s); ++ strncpy((char *) buf, t, s - t); ++ buf[s - t] = '\0'; ++ if (option == 0) ++ { ++ /* The first option is always the file name */ ++ len = s - t; ++ if (len > 255) ++ len = 255; ++ strncpy(source_file, t, len); ++ source_file[len] = '\0'; ++ } ++ else if (strncmp("caller", t, s - t) == 0) ++ { ++ calling_party = TRUE; ++ } ++ else if (strncmp("debug", t, s - t) == 0) ++ { ++ verbose = TRUE; ++ } ++ } ++ ++ /* Done parsing */ ++ ++ u = ast_module_user_add(chan); ++ ++ if (chan->_state != AST_STATE_UP) ++ { ++ /* Shouldn't need this, but checking to see if channel is already answered ++ * Theoretically asterisk should already have answered before running the app */ ++ res = ast_answer(chan); ++ } ++ ++ if (!res) ++ { ++ original_read_fmt = chan->readformat; ++ if (original_read_fmt != AST_FORMAT_SLINEAR) ++ { ++ res = ast_set_read_format(chan, AST_FORMAT_SLINEAR); ++ if (res < 0) ++ { ++ ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n"); ++ return -1; ++ } ++ } ++ original_write_fmt = chan->writeformat; ++ if (original_write_fmt != AST_FORMAT_SLINEAR) ++ { ++ res = ast_set_write_format(chan, AST_FORMAT_SLINEAR); ++ if (res < 0) ++ { ++ ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up\n"); ++ res = ast_set_read_format(chan, original_read_fmt); ++ if (res) ++ ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); ++ return -1; ++ } ++ } ++ fax_init(&fax, calling_party); ++ if (verbose) ++ fax.logging.level = SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW; ++ ++ x = pbx_builtin_getvar_helper(chan, "LOCALSTATIONID"); ++ if (x && x[0]) ++ t30_set_local_ident(&fax.t30_state, x); ++ x = pbx_builtin_getvar_helper(chan, "LOCALHEADERINFO"); ++ if (x && x[0]) ++ t30_set_header_info(&fax.t30_state, x); ++ t30_set_tx_file(&fax.t30_state, source_file, -1, -1); ++ //t30_set_phase_b_handler(&fax.t30_state, phase_b_handler, chan); ++ //t30_set_phase_d_handler(&fax.t30_state, phase_d_handler, chan); ++ t30_set_phase_e_handler(&fax.t30_state, phase_e_handler, chan); ++ t30_set_ecm_capability(&fax.t30_state, TRUE); ++ t30_set_supported_compressions(&fax.t30_state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION); ++ while (ast_waitfor(chan, -1) > -1) ++ { ++ inf = ast_read(chan); ++ if (inf == NULL) ++ { ++ res = -1; ++ break; ++ } ++ if (inf->frametype == AST_FRAME_VOICE) ++ { ++ if (fax_rx(&fax, inf->data, inf->samples)) ++ break; ++ samples = (inf->samples <= MAX_BLOCK_SIZE) ? inf->samples : MAX_BLOCK_SIZE; ++ len = fax_tx(&fax, (int16_t *) &buf[AST_FRIENDLY_OFFSET], samples); ++ if (len) ++ { ++ memset(&outf, 0, sizeof(outf)); ++ outf.frametype = AST_FRAME_VOICE; ++ outf.subclass = AST_FORMAT_SLINEAR; ++ outf.datalen = len*sizeof(int16_t); ++ outf.samples = len; ++ outf.data = &buf[AST_FRIENDLY_OFFSET]; ++ outf.offset = AST_FRIENDLY_OFFSET; ++ if (ast_write(chan, &outf) < 0) ++ { ++ ast_log(LOG_WARNING, "Unable to write frame to channel; %s\n", strerror(errno)); ++ break; ++ } ++ } ++ } ++ ast_frfree(inf); ++ } ++ if (inf == NULL) ++ { ++ ast_log(LOG_DEBUG, "Got hangup\n"); ++ res = -1; ++ } ++ if (original_read_fmt != AST_FORMAT_SLINEAR) ++ { ++ res = ast_set_read_format(chan, original_read_fmt); ++ if (res) ++ ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); ++ } ++ if (original_write_fmt != AST_FORMAT_SLINEAR) ++ { ++ res = ast_set_write_format(chan, original_write_fmt); ++ if (res) ++ ast_log(LOG_WARNING, "Unable to restore write format on '%s'\n", chan->name); ++ } ++ t30_terminate(&fax.t30_state); ++ } ++ else ++ { ++ ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name); ++ } ++ ast_module_user_remove(u); ++ return res; ++} ++/*- End of function --------------------------------------------------------*/ ++ ++static int unload_module(void) ++{ ++ int res; ++ ++ ast_module_user_hangup_all(); ++ ++ res = ast_unregister_application(app); ++ ++ ++ return res; ++} ++/*- End of function --------------------------------------------------------*/ ++ ++static int load_module(void) ++{ ++ return ast_register_application(app, txfax_exec, synopsis, descrip); ++} ++/*- End of function --------------------------------------------------------*/ ++ ++AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Trivial FAX Transmit Application"); ++ ++/*- End of file ------------------------------------------------------------*/ +diff --git a/build_tools/menuselect-deps.in b/build_tools/menuselect-deps.in +index 60a53b4..fb10545 100644 +--- a/build_tools/menuselect-deps.in ++++ b/build_tools/menuselect-deps.in +@@ -22,6 +22,7 @@ POPT=@PBX_POPT@ + PRI=@PBX_PRI@ + QT=@PBX_QT@ + RADIUS=@PBX_RADIUS@ ++SPANDSP=@PBX_SPANDSP@ + SPEEX=@PBX_SPEEX@ + SQLITE=@PBX_SQLITE@ + SSL=@PBX_OPENSSL@ +diff --git a/configure.ac b/configure.ac +index 62b5c83..26a5735 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -198,6 +198,7 @@ AST_EXT_LIB_SETUP([PWLIB], [PWlib], [pwlib]) + AST_EXT_LIB_SETUP([OPENH323], [OpenH323], [h323]) + AST_EXT_LIB_SETUP([QT], [Qt], [qt]) + AST_EXT_LIB_SETUP([RADIUS], [Radius Client], [radius]) ++AST_EXT_LIB_SETUP([SPANDSP], [spandsp Library], [spandsp]) + AST_EXT_LIB_SETUP([SPEEX], [Speex], [speex]) + AST_EXT_LIB_SETUP([SQLITE], [SQLite], [sqlite]) + AST_EXT_LIB_SETUP([SUPPSERV], [mISDN Supplemental Services], [suppserv]) +@@ -963,6 +964,8 @@ AC_LANG_POP + + AST_EXT_LIB_CHECK([RADIUS], [radiusclient-ng], [rc_read_config], [radiusclient-ng.h]) + ++AST_EXT_LIB_CHECK([SPANDSP], [spandsp], [fax_init], [spandsp.h], [-ltiff]) ++ + AST_EXT_LIB_CHECK([SPEEX], [speex], [speex_encode], [speex/speex.h], [-lm]) + + AST_EXT_LIB_CHECK([SQLITE], [sqlite], [sqlite_exec], [sqlite.h]) +diff --git a/makeopts.in b/makeopts.in +index 193ec3c..8138dd8 100644 +--- a/makeopts.in ++++ b/makeopts.in +@@ -141,6 +141,9 @@ QT_LIB=@QT_LIB@ + RADIUS_INCLUDE=@RADIUS_INCLUDE@ + RADIUS_LIB=@RADIUS_LIB@ + ++SPANDSP_INCLUDE=@SPANDSP_INCLUDE@ ++SPANDSP_LIB=@SPANDSP_LIB@ ++ + SPEEX_INCLUDE=@SPEEX_INCLUDE@ + SPEEX_LIB=@SPEEX_LIB@ + +-- +1.5.3.7 + diff --git a/asterisk-1.4.16.2-system-imap.patch b/asterisk-1.4.16.2-system-imap.patch new file mode 100644 index 0000000..4c8594d --- /dev/null +++ b/asterisk-1.4.16.2-system-imap.patch @@ -0,0 +1,254 @@ +From 567090b57c0cad0edb3b8a2ca27c4de2ffe09360 Mon Sep 17 00:00:00 2001 +From: Jeffrey C. Ollie +Date: Thu, 8 Nov 2007 15:47:38 -0600 +Subject: [PATCH] Patch so that system IMAP library can be used. + +--- + apps/app_voicemail.c | 10 ++++++++++ + configure | 37 ++++++++++++++++++++++++++++--------- + configure.ac | 36 +++++++++++++++++++++++++++--------- + doc/imapstorage.txt | 34 ++++++++++++++++------------------ + 4 files changed, 81 insertions(+), 36 deletions(-) + +diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c +index fec54fc..d455a0f 100644 +--- a/apps/app_voicemail.c ++++ b/apps/app_voicemail.c +@@ -70,10 +70,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + #include + #include + #include ++#ifdef USE_SYSTEM_IMAP ++#include ++#include ++#include ++#else + #include "c-client.h" + #include "imap4r1.h" + #include "linkage.h" + #endif ++#endif + #include "asterisk/lock.h" + #include "asterisk/file.h" + #include "asterisk/logger.h" +@@ -4708,7 +4714,11 @@ static int init_mailstream(struct vm_state *vms, int box) + + if (delimiter == '\0') { /* did not probe the server yet */ + char *cp; ++#if USE_SYSTEM_IMAP ++#include ++#else + #include "linkage.c" ++#endif + /* Connect to INBOX first to get folders delimiter */ + imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1); + stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL); +diff --git a/configure b/configure +index 9b9241f..17d2677 100755 +--- a/configure ++++ b/configure +@@ -18757,7 +18757,10 @@ fi + fi + + if test "${USE_IMAP_TK}" != "no"; then +- if test "${IMAP_TK_DIR}" = ""; then ++ if test "${IMAP_TK_DIR}" = "system" ; then ++ { echo "$as_me:$LINENO: Checking for system c-client library..." >&5 ++echo "$as_me: Checking for system c-client library..." >&6;} ++ elif test "${IMAP_TK_DIR}" = ""; then + IMAP_TK_DIR=`pwd`"/../imap-2004g" + if test -n "${IMAP_TK_MANDATORY}"; then + { echo "$as_me:$LINENO: The --with-imap option does not search your system for installed" >&5 +@@ -18770,22 +18773,34 @@ echo "$as_me: the configure script will assume you have placed built the c-clien + echo "$as_me: files at ${IMAP_TK_DIR}, as outlined in the doc/imapstorage.txt file." >&6;} + fi + fi +- { echo "$as_me:$LINENO: checking for UW IMAP Toolkit c-client library" >&5 ++ if test "${IMAP_TK_DIR}" != "system" ; then ++ { echo "$as_me:$LINENO: checking for UW IMAP Toolkit c-client library" >&5 + echo $ECHO_N "checking for UW IMAP Toolkit c-client library... $ECHO_C" >&6; } ++ fi + saved_cppflags="${CPPFLAGS}" + saved_libs="${LIBS}" +- if test -f ${IMAP_TK_DIR}/c-client/LDFLAGS ; then ++ if test "${IMAP_TK_DIR}" = "system" ; then ++ imap_ldflags="" ++ imap_libs="-lc-client" ++ imap_include="-DUSE_SYSTEM_IMAP" ++ elif test -f ${IMAP_TK_DIR}/c-client/LDFLAGS ; then + imap_ldflags=`cat ${IMAP_TK_DIR}/c-client/LDFLAGS` ++ imap_libs="${IMAP_TK_DIR}/c-client/c-client.a" ++ imap_include="-I${IMAP_TK_DIR}/c-client" + fi +- CPPFLAGS="${CPPFLAGS} -I${IMAP_TK_DIR}/c-client" +- LIBS="${LIBS} ${IMAP_TK_DIR}/c-client/c-client.a "`echo ${imap_ldflags}` ++ CPPFLAGS="${CPPFLAGS} ${imap_include}" ++ LIBS="${LIBS} ${imap_libs} "`echo ${imap_ldflags}` + cat >conftest.$ac_ext <<_ACEOF + /* confdefs.h. */ + _ACEOF + cat confdefs.h >>conftest.$ac_ext + cat >>conftest.$ac_ext <<_ACEOF + /* end confdefs.h. */ +-#include "c-client.h" ++#ifdef USE_SYSTEM_IMAP ++ #include ++ #else ++ #include "c-client.h" ++ #endif + void mm_searched (MAILSTREAM *stream,unsigned long number) + { + } +@@ -18878,7 +18893,11 @@ _ACEOF + cat confdefs.h >>conftest.$ac_ext + cat >>conftest.$ac_ext <<_ACEOF + /* end confdefs.h. */ +-#include "c-client.h" ++#ifdef USE_SYSTEM_IMAP ++ #include ++ #else ++ #include "c-client.h" ++ #endif + void mm_searched (MAILSTREAM *stream,unsigned long number) + { + } +@@ -18970,8 +18989,8 @@ rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + if test "${ac_cv_imap_tk}" = "yes"; then + { echo "$as_me:$LINENO: result: yes" >&5 + echo "${ECHO_T}yes" >&6; } +- IMAP_TK_LIB="${IMAP_TK_DIR}/c-client/c-client.a "`echo ${imap_ldflags}` +- IMAP_TK_INCLUDE="-I${IMAP_TK_DIR}/c-client" ++ IMAP_TK_LIB="${imap_libs}"`echo ${imap_ldflags}` ++ IMAP_TK_INCLUDE="${imap_include}" + PBX_IMAP_TK=1 + + cat >>confdefs.h <<\_ACEOF +diff --git a/configure.ac b/configure.ac +index cc065c6..62b5c83 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -472,7 +472,9 @@ if test "${PBX_IKSEMEL}" = 1; then + fi + + if test "${USE_IMAP_TK}" != "no"; then +- if test "${IMAP_TK_DIR}" = ""; then ++ if test "${IMAP_TK_DIR}" = "system" ; then ++ AC_MSG_NOTICE([Checking for system c-client library...]) ++ elif test "${IMAP_TK_DIR}" = ""; then + IMAP_TK_DIR=`pwd`"/../imap-2004g" + if test -n "${IMAP_TK_MANDATORY}"; then + AC_MSG_NOTICE([The --with-imap option does not search your system for installed]) +@@ -481,17 +483,29 @@ if test "${USE_IMAP_TK}" != "no"; then + AC_MSG_NOTICE([files at ${IMAP_TK_DIR}, as outlined in the doc/imapstorage.txt file.]) + fi + fi +- AC_MSG_CHECKING(for UW IMAP Toolkit c-client library) ++ if test "${IMAP_TK_DIR}" != "system" ; then ++ AC_MSG_CHECKING(for UW IMAP Toolkit c-client library) ++ fi + saved_cppflags="${CPPFLAGS}" + saved_libs="${LIBS}" +- if test -f ${IMAP_TK_DIR}/c-client/LDFLAGS ; then ++ if test "${IMAP_TK_DIR}" = "system" ; then ++ imap_ldflags="" ++ imap_libs="-lc-client" ++ imap_include="-DUSE_SYSTEM_IMAP" ++ elif test -f ${IMAP_TK_DIR}/c-client/LDFLAGS ; then + imap_ldflags=`cat ${IMAP_TK_DIR}/c-client/LDFLAGS` ++ imap_libs="${IMAP_TK_DIR}/c-client/c-client.a" ++ imap_include="-I${IMAP_TK_DIR}/c-client" + fi +- CPPFLAGS="${CPPFLAGS} -I${IMAP_TK_DIR}/c-client" +- LIBS="${LIBS} ${IMAP_TK_DIR}/c-client/c-client.a "`echo ${imap_ldflags}` ++ CPPFLAGS="${CPPFLAGS} ${imap_include}" ++ LIBS="${LIBS} ${imap_libs} "`echo ${imap_ldflags}` + AC_LINK_IFELSE( + AC_LANG_PROGRAM( +- [#include "c-client.h" ++ [#ifdef USE_SYSTEM_IMAP ++ #include ++ #else ++ #include "c-client.h" ++ #endif + void mm_searched (MAILSTREAM *stream,unsigned long number) + { + } +@@ -547,7 +561,11 @@ if test "${USE_IMAP_TK}" != "no"; then + if test "${ac_cv_imap_tk}" = "yes"; then + AC_LINK_IFELSE( + AC_LANG_PROGRAM( +- [#include "c-client.h" ++ [#ifdef USE_SYSTEM_IMAP ++ #include ++ #else ++ #include "c-client.h" ++ #endif + void mm_searched (MAILSTREAM *stream,unsigned long number) + { + } +@@ -605,8 +623,8 @@ if test "${USE_IMAP_TK}" != "no"; then + LIBS="${saved_libs}" + if test "${ac_cv_imap_tk}" = "yes"; then + AC_MSG_RESULT(yes) +- IMAP_TK_LIB="${IMAP_TK_DIR}/c-client/c-client.a "`echo ${imap_ldflags}` +- IMAP_TK_INCLUDE="-I${IMAP_TK_DIR}/c-client" ++ IMAP_TK_LIB="${imap_libs}"`echo ${imap_ldflags}` ++ IMAP_TK_INCLUDE="${imap_include}" + PBX_IMAP_TK=1 + AC_DEFINE([HAVE_IMAP_TK], 1, [Define if your system has the UW IMAP Toolkit c-client library.]) + if test "${ac_cv_imap_tk2006}" = "yes"; then +diff --git a/doc/imapstorage.txt b/doc/imapstorage.txt +index 1e5484b..d292b5d 100644 +--- a/doc/imapstorage.txt ++++ b/doc/imapstorage.txt +@@ -38,10 +38,11 @@ Installation Notes + -------------------------------------- + University of Washington IMAP C-Client + -------------------------------------- +-You will need a source distribution of University of Washington's IMAP +-c-client (http://www.washington.edu/imap/). Asterisk supports both the +-2004 and 2006 versions of c-client, however mail_expunge_full is enabled +-in the 2006 version. ++If you do not have the University of Washington's IMAP c-client ++installed on your system, you will need to download the c-client ++source distribution (http://www.washington.edu/imap/) and compile it. ++Asterisk supports both the 2004 and 2006 versions of c-client, however ++mail_expunge_full is enabled in the 2006 version. + + Note that Asterisk only uses the 'client' portion of the UW IMAP toolkit, + but building it also builds an IMAP server and various other utilities. +@@ -68,20 +69,17 @@ need to run 'make install'. + ------------------ + Compiling Asterisk + ------------------ +- +-Configure with ./configure --with-imap=/usr/src/imap +-or where ever you built thfe UWashington IMAP Toolkit. When you run +-'make menuselect', choose 'Voicemail Build Options' and the +-IMAP_STORAGE option should be available for selection. +- +-Note that the --with-imap option will NOT search your system for an +-installed copy of the IMAP Toolkit c-client library; the Asterisk +-Makefiles and configure script are designed to build against an +-unpacked and compiled source tree of the IMAP Toolkit, not a binary +-distribution. +- +-After selecting it, use the 'x' key to exit menuselect and save +-your changes, and the build/install Asterisk normally. ++To use the system c-client library, configure Asterisk with ++./configure --with-imap=system. If you downloaded the c-client source ++and compiled it according to the above instructions, configure ++Asterisk with with ./configure --with-imap=/usr/src/imap or where ever ++you built the UWashington IMAP Toolkit. When you run 'make ++menuselect', choose 'Voicemail Build Options' and the IMAP_STORAGE ++option should be available for selection. ++ ++After selecting the IMAP_STORAGE option, use the 'x' key to exit ++menuselect and save your changes, and the build/install Asterisk ++normally. + + --------------------- + Modify voicemail.conf +-- +1.5.3.7 + diff --git a/asterisk.spec b/asterisk.spec index 44d1680..605a765 100644 --- a/asterisk.spec +++ b/asterisk.spec @@ -2,8 +2,8 @@ Summary: The Open Source PBX Name: asterisk -Version: 1.4.16.1 -Release: 2%{?dist} +Version: 1.4.16.2 +Release: 1%{?dist} License: GPLv2 Group: Applications/Internet URL: http://www.asterisk.org/ @@ -20,13 +20,13 @@ ExcludeArch: ppc64 # # MD5 Sums # ======== -# 1b58b2a48bc65f651b66a2d1dbfc3cc3 asterisk-1.4.16.1.tar.gz -# 3162a3a2d90d1883e16bb5f0b552a9fd asterisk-1.4.16.1-stripped.tar.gz +# 1010a11dace37ae8e8b55864db73d0ef asterisk-1.4.16.2.tar.gz +# 7c728c41c74c263b35d160fbf660effa asterisk-1.4.16.2-stripped.tar.gz # # SHA1 Sums # ========= -# 0ad6a4c6ec49ac7b8f204798fb7bde7cb624cdc6 asterisk-1.4.16.1.tar.gz -# 76a2d5bf407a0d48eb9b5a3543a0624b069eb3b1 asterisk-1.4.16.1-stripped.tar.gz +# 37b6099e2ec50dcb8158fbd15d15ad93071f1d6f asterisk-1.4.16.2.tar.gz +# b3640624d77beaa4cdc87d8f6baa8a04f2cd1bbe asterisk-1.4.16.2-stripped.tar.gz Source0: asterisk-%{version}-stripped.tar.gz Source1: asterisk-logrotate @@ -34,16 +34,16 @@ Source2: menuselect.makedeps Source3: menuselect.makeopts Source4: asterisk-strip.sh -Patch1: asterisk-1.4.16-initscripts.patch -Patch2: asterisk-1.4.16-system-imap.patch -Patch3: asterisk-1.4.16-alternate-voicemail.patch -Patch4: asterisk-1.4.16-spandspfax.patch -Patch5: asterisk-1.4.16-appconference.patch -Patch6: asterisk-1.4.16-alternate-extensions.patch -Patch7: asterisk-1.4.16-optimization.patch -Patch8: asterisk-1.4.16-libcap.patch -Patch9: asterisk-1.4.16-chanmobile.patch -Patch10: asterisk-1.4.16-autoconf.patch +Patch1: asterisk-1.4.16.2-initscripts.patch +Patch2: asterisk-1.4.16.2-system-imap.patch +Patch3: asterisk-1.4.16.2-alternate-voicemail.patch +Patch4: asterisk-1.4.16.2-spandspfax.patch +Patch5: asterisk-1.4.16.2-appconference.patch +Patch6: asterisk-1.4.16.2-alternate-extensions.patch +Patch7: asterisk-1.4.16.2-optimization.patch +Patch8: asterisk-1.4.16.2-libcap.patch +Patch9: asterisk-1.4.16.2-chanmobile.patch +Patch10: asterisk-1.4.16.2-autoconf.patch BuildRoot: %{_tmppath}/%{name}-%{version}-root-%(%{__id_u} -n) @@ -876,6 +876,9 @@ fi %{_libdir}/asterisk/modules/codec_zap.so %changelog +* Fri Dec 28 2007 Jeffrey C. Ollie - 1.4.16.2-1 +- Update to 1.4.16.2 + * Sat Dec 22 2007 Jeffrey C. Ollie - 1.4.16.1-2 - Bump release and rebuild to fix broken dep on uw-imap. diff --git a/sources b/sources index df67f0c..3c1d268 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -3162a3a2d90d1883e16bb5f0b552a9fd asterisk-1.4.16.1-stripped.tar.gz +7c728c41c74c263b35d160fbf660effa asterisk-1.4.16.2-stripped.tar.gz