From 729de3c23872769b5d8028c0647e97aee890d76d Mon Sep 17 00:00:00 2001 From: Jeffrey C. Ollie Date: Dec 12 2007 05:32:48 +0000 Subject: Importing asterisk to devel --- diff --git a/.cvsignore b/.cvsignore index e69de29..1985265 100644 --- a/.cvsignore +++ b/.cvsignore @@ -0,0 +1 @@ +asterisk-1.4.15-stripped.tar.gz diff --git a/asterisk-1.4.14-alternate-extensions.patch b/asterisk-1.4.14-alternate-extensions.patch new file mode 100644 index 0000000..c9a8339 --- /dev/null +++ b/asterisk-1.4.14-alternate-extensions.patch @@ -0,0 +1,49 @@ +From 217e55cc56d6e880a790704abf8756c190fe78a5 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 4a106b9..2c3047a 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.4 + diff --git a/asterisk-1.4.14-alternate-voicemail.patch b/asterisk-1.4.14-alternate-voicemail.patch new file mode 100644 index 0000000..83b7099 --- /dev/null +++ b/asterisk-1.4.14-alternate-voicemail.patch @@ -0,0 +1,47 @@ +From e0d28a01fd6a92db44fb0ef3f9e73bb6a3aeeb4e 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.4 + diff --git a/asterisk-1.4.14-appconference.patch b/asterisk-1.4.14-appconference.patch new file mode 100644 index 0000000..9ca1ed6 --- /dev/null +++ b/asterisk-1.4.14-appconference.patch @@ -0,0 +1,10238 @@ +From a6e3ccb8909e4b3fb8d9a1decd501c1e531b0b83 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 d0e9215..0722e82 100644 +--- a/apps/Makefile ++++ b/apps/Makefile +@@ -38,4 +38,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.4 + diff --git a/asterisk-1.4.14-autoconf.patch b/asterisk-1.4.14-autoconf.patch new file mode 100644 index 0000000..72aa384 --- /dev/null +++ b/asterisk-1.4.14-autoconf.patch @@ -0,0 +1,1136 @@ +From 2ed92b863442694a22824c2cfe11afd2340a7cc7 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 | 959 +++++++++++++++++++++++++++++++++++++- + include/asterisk/autoconfig.h.in | 6 + + 2 files changed, 942 insertions(+), 23 deletions(-) + +diff --git a/configure b/configure +index 2d2a5cb..5bd2386 100755 +--- a/configure ++++ b/configure +@@ -1,5 +1,5 @@ + #! /bin/sh +-# From configure.ac Revision: 78166 . ++# From configure.ac Revision. + # Guess values for system-dependent variables and create Makefiles. + # Generated by GNU Autoconf 2.61. + # +@@ -720,10 +720,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 +@@ -1507,7 +1515,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 +@@ -7612,6 +7622,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" + +@@ -7640,6 +7678,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" + +@@ -11915,13 +11981,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; + } +@@ -11961,13 +12025,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; + } +@@ -16602,6 +16664,405 @@ 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;} ++ ++ ;; ++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;} ++ ++ ;; ++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 +@@ -17000,6 +17461,407 @@ 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;} ++ ++ ;; ++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;} ++ ++ ;; ++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" +@@ -27527,9 +28389,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 +@@ -34152,10 +35014,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 +@@ -34237,14 +35107,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 +@@ -34286,6 +35148,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 +@@ -34375,6 +35245,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 +@@ -34388,7 +35299,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 +@@ -34406,8 +35317,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 +@@ -34420,6 +35331,8 @@ N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n + ' >>$CONFIG_STATUS >$CONFIG_STATUS <<_ACEOF ++:end ++s/|#_!!_#|//g + CEOF$ac_eof + _ACEOF + +@@ -34667,7 +35580,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 5ab35d9..0be7024 100644 +--- a/include/asterisk/autoconfig.h.in ++++ b/include/asterisk/autoconfig.h.in +@@ -56,9 +56,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.4 + diff --git a/asterisk-1.4.14-chanmobile.patch b/asterisk-1.4.14-chanmobile.patch new file mode 100644 index 0000000..134ef7a --- /dev/null +++ b/asterisk-1.4.14-chanmobile.patch @@ -0,0 +1,2521 @@ +From 98bdecc2b792305d9d3fba2cfc42a02470a0dc36 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 6f9a7d5..5feb521 100644 +--- a/channels/Makefile ++++ b/channels/Makefile +@@ -118,4 +118,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 546febb..6c8cabe 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -171,6 +171,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]) +@@ -396,6 +397,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 bc7570f..cc93d0e 100644 +--- a/makeopts.in ++++ b/makeopts.in +@@ -66,6 +66,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.4 + diff --git a/asterisk-1.4.14-initscripts.patch b/asterisk-1.4.14-initscripts.patch new file mode 100644 index 0000000..fd39ec8 --- /dev/null +++ b/asterisk-1.4.14-initscripts.patch @@ -0,0 +1,131 @@ +From e574042d5337543c47d6cffe5daa7751bca9be63 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.4 + diff --git a/asterisk-1.4.14-libcap.patch b/asterisk-1.4.14-libcap.patch new file mode 100644 index 0000000..0d61647 --- /dev/null +++ b/asterisk-1.4.14-libcap.patch @@ -0,0 +1,160 @@ +From 820dac0b531f042bd632b98a6514dd931ea05e80 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 52d7033..546febb 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -172,6 +172,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']) +@@ -397,6 +398,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 10353da..9d71844 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 5ee3058..bc7570f 100644 +--- a/makeopts.in ++++ b/makeopts.in +@@ -188,3 +188,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.4 + diff --git a/asterisk-1.4.14-optimization.patch b/asterisk-1.4.14-optimization.patch new file mode 100644 index 0000000..15acf6c --- /dev/null +++ b/asterisk-1.4.14-optimization.patch @@ -0,0 +1,48 @@ +From 98e7fe66adec04f21f9703acb745a6175cce3421 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 2cee8c3..2a832dd 100644 +--- a/Makefile ++++ b/Makefile +@@ -195,10 +195,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 + +@@ -211,8 +217,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.4 + diff --git a/asterisk-1.4.14-spandspfax.patch b/asterisk-1.4.14-spandspfax.patch new file mode 100644 index 0000000..f53b662 --- /dev/null +++ b/asterisk-1.4.14-spandspfax.patch @@ -0,0 +1,1254 @@ +From 896c00799146b1f83afce86fee1b576e7f1a5bf4 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 b99da0f..2d2a5cb 100755 +--- a/configure ++++ b/configure +@@ -820,6 +820,10 @@ RADIUS_LIB + RADIUS_INCLUDE + RADIUS_DIR + PBX_RADIUS ++SPANDSP_LIB ++SPANDSP_INCLUDE ++SPANDSP_DIR ++PBX_SPANDSP + SPEEX_LIB + SPEEX_INCLUDE + SPEEX_DIR +@@ -1529,6 +1533,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 +@@ -8307,6 +8312,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" + +@@ -27490,6 +27523,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 +@@ -33861,6 +34293,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 4354c02..52d7033 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -196,6 +196,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]) +@@ -960,6 +961,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 92ac3f7..5ab35d9 100644 +--- a/include/asterisk/autoconfig.h.in ++++ b/include/asterisk/autoconfig.h.in +@@ -320,6 +320,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 0000f20..5ee3058 100644 +--- a/makeopts.in ++++ b/makeopts.in +@@ -140,6 +140,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.4 + diff --git a/asterisk-1.4.14-system-imap.patch b/asterisk-1.4.14-system-imap.patch new file mode 100644 index 0000000..d00da7c --- /dev/null +++ b/asterisk-1.4.14-system-imap.patch @@ -0,0 +1,254 @@ +From e11c3a30c2242380d5f1d4210e4541071ffcd895 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 e333fb2..e94d83c 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" +@@ -4675,7 +4681,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 a6c8ccb..b99da0f 100755 +--- a/configure ++++ b/configure +@@ -18447,7 +18447,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 +@@ -18460,22 +18463,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) + { + } +@@ -18568,7 +18583,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) + { + } +@@ -18660,8 +18679,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 4eeab85..4354c02 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -469,7 +469,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]) +@@ -478,17 +480,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) + { + } +@@ -544,7 +558,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) + { + } +@@ -602,8 +620,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.4 + diff --git a/asterisk-logrotate b/asterisk-logrotate new file mode 100644 index 0000000..b52a53f --- /dev/null +++ b/asterisk-logrotate @@ -0,0 +1,9 @@ +/var/log/asterisk/messages /var/log/asterisk/event_log /var/log/asterisk/queue_log { + missingok + notifempty + create 0640 root root + sharedscripts + postrotate + /usr/sbin/asterisk -rx 'logger rotate' >/dev/null 2>/dev/null || true + endscript +} diff --git a/asterisk-strip.sh b/asterisk-strip.sh new file mode 100644 index 0000000..99e439a --- /dev/null +++ b/asterisk-strip.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +version=$1 +if [ ! -f asterisk-$version.tar.gz ]; then + echo "Can't find asterisk-$version.tar.gz!" + exit 1 +fi + + +tar xf asterisk-$version.tar.gz +rm asterisk-$version/codecs/codec_ilbc.c +rm -rf asterisk-$version/codecs/ilbc +rm asterisk-$version/codecs/ilbc_slin_ex.h +rm asterisk-$version/codecs/slin_ilbc_ex.h +rm asterisk-$version/formats/format_ilbc.c +tar czf asterisk-$version-stripped.tar.gz asterisk-$version +rm -rf asterisk-$version + +echo "MD5 Sums" +echo "========" +md5sum asterisk-$version.tar.gz asterisk-$version-stripped.tar.gz +echo +echo "SHA1 Sums" +echo "=========" +sha1sum asterisk-$version.tar.gz asterisk-$version-stripped.tar.gz + diff --git a/asterisk.spec b/asterisk.spec new file mode 100644 index 0000000..00fbc45 --- /dev/null +++ b/asterisk.spec @@ -0,0 +1,1120 @@ +Summary: The Open Source PBX +Name: asterisk +Version: 1.4.15 +Release: 3%{?dist} +License: GPLv2 +Group: Applications/Internet +URL: http://www.asterisk.org/ + +# The asterisk tarball contains some items that we don't want in there, +# so start with the original tarball from here: +# http://downloads.digium.com/pub/telephony/asterisk/releases/asterisk-%{version}.tar.gz +# Then run the included script file to build the stripped tarball: +# +# sh asterisk-strip.sh %{version} +# +# MD5 Sums +# ======== +# c2f87a1343a7bf7eb4925c06ef962976 asterisk-1.4.15.tar.gz +# a548cfe60e86013eac0851ef585d01ed asterisk-1.4.15-stripped.tar.gz +# +# SHA1 Sums +# ========= +# c9d934a5a6ed374aebdc9152b65af1b76e603e39 asterisk-1.4.15.tar.gz +# a55daa6b9a991bcaa08417e0addf27274471dc3c asterisk-1.4.15-stripped.tar.gz + +Source0: asterisk-%{version}-stripped.tar.gz +Source1: asterisk-logrotate +Source2: menuselect.makedeps +Source3: menuselect.makeopts +Source4: asterisk-strip.sh + +Patch1: asterisk-1.4.14-initscripts.patch +Patch2: asterisk-1.4.14-system-imap.patch +Patch3: asterisk-1.4.14-alternate-voicemail.patch +Patch4: asterisk-1.4.14-spandspfax.patch +Patch5: asterisk-1.4.14-appconference.patch +Patch6: asterisk-1.4.14-alternate-extensions.patch +Patch7: asterisk-1.4.14-optimization.patch +Patch8: asterisk-1.4.14-libcap.patch +Patch9: asterisk-1.4.14-chanmobile.patch +Patch10: asterisk-1.4.14-autoconf.patch + +BuildRoot: %{_tmppath}/%{name}-%{version}-root-%(%{__id_u} -n) + +# core build requirements +BuildRequires: openssl-devel +BuildRequires: newt-devel +BuildRequires: libtermcap-devel +BuildRequires: ncurses-devel +BuildRequires: libcap-devel +BuildRequires: gtk2-devel + +# for building docs +BuildRequires: doxygen +BuildRequires: graphviz +BuildRequires: graphviz-gd + +# for codec_speex and app_conference +BuildRequires: speex-devel >= 1.2 + +# for format_ogg_vorbis +BuildRequires: libogg-devel +BuildRequires: libvorbis-devel + +# codec_gsm +BuildRequires: gsm-devel + +Requires(pre): %{_sbindir}/useradd +Requires(pre): %{_sbindir}/groupadd +Requires(post): /sbin/chkconfig +Requires(preun): /sbin/chkconfig +Requires(preun): /sbin/service + +%description +Asterisk is a complete PBX in software. It runs on Linux and provides +all of the features you would expect from a PBX and more. Asterisk +does voice over IP in three protocols, and can interoperate with +almost all standards-based telephony equipment using relatively +inexpensive hardware. + +%package alsa +Summary: Modules for Asterisk that use Alsa sound drivers +Group: Applications/Internet +Requires: asterisk = %{version}-%{release} +BuildRequires: alsa-lib-devel + +%description alsa +Modules for Asterisk that use Alsa sound drivers. + +%package apidoc +Summary: API documentation for Asterisk +Group: Applications/Internet +Requires: asterisk = %{version}-%{release} + +%description apidoc +API documentation for Asterisk. + +%package conference +Summary: Audio/video conferencing application for Asterisk +Group: Applications/Internet +Requires: asterisk = %{version}-%{release} +BuildRequires: speex-devel + +%description conference +Audio/video conferencing application for Asterisk. + +%package curl +Summary: Modules for Asterisk that use cURL +Group: Applications/Internet +Requires: asterisk = %{version}-%{release} +BuildRequires: curl-devel + +%description curl +Modules for Asterisk that use cURL. + +%package devel +Summary: Development files for Asterisk +Group: Development/Libraries +Requires: asterisk = %{version}-%{release} + +%description devel +Development files for Asterisk. + +%package fax +Summary: FAX applications for Asterisk +Group: Applications/Internet +Requires: asterisk = %{version}-%{release} +BuildRequires: spandsp-devel + +%description fax +FAX applications for Asterisk + +%package festival +Summary: Festival application for Asterisk +Group: Applications/Internet +Requires: asterisk = %{version}-%{release} +Requires: festival + +%description festival +Application for the Asterisk PBX that uses Festival to convert text to speech. + +%package firmware +Summary: Firmware for the Digium S101I (IAXy) +Group: Applications/Internet +License: Redistributable, no modification permitted +Requires: asterisk = %{version}-%{release} +Requires: festival + +%description firmware +Firmware for the Digium S101I (IAXy). + +%package jabber +Summary: Jabber/XMPP resources for Asterisk +Group: Applications/Internet +Requires: asterisk = %{version}-%{release} +BuildRequires: iksemel-devel + +%description jabber +Jabber/XMPP resources for Asterisk. + +%package misdn +Summary: mISDN channel for Asterisk +Group: Applications/Internet +Requires: asterisk = %{version}-%{release} +Requires(pre): %{_sbindir}/usermod +BuildRequires: mISDN-devel + +%description misdn +mISDN channel for Asterisk. + +%package mobile +Summary: Asterisk channel driver for bluetooth phones and headsets +Group: Applications/Internet +Requires: asterisk = %{version}-%{release} +BuildRequires: bluez-libs-devel + +%description mobile +Asterisk channel driver to allow Bluetooth cell/mobile phones to be +used as FXO devices, and headsets as FXS devices. + +%package odbc +Summary: Applications for Asterisk that use ODBC (except voicemail) +Group: Applications/Internet +Requires: asterisk = %{version}-%{release} +BuildRequires: libtool-ltdl-devel +BuildRequires: unixODBC-devel + +%description odbc +Applications for Asterisk that use ODBC (except voicemail) + +%package oss +Summary: Modules for Asterisk that use OSS sound drivers +Group: Applications/Internet +Requires: asterisk = %{version}-%{release} + +%description oss +Modules for Asterisk that use OSS sound drivers. + +%package postgresql +Summary: Applications for Asterisk that use PostgreSQL +Group: Applications/Internet +Requires: asterisk = %{version}-%{release} +BuildRequires: postgresql-devel + +%description postgresql +Applications for Asterisk that use PostgreSQL. + +%package radius +Summary: Applications for Asterisk that use RADIUS +Group: Applications/Internet +Requires: asterisk = %{version}-%{release} +BuildRequires: radiusclient-ng-devel + +%description radius +Applications for Asterisk that use RADIUS. + +%package skinny +Summary: Modules for Asterisk that support the SCCP/Skinny protocol +Group: Applications/Internet +Requires: asterisk = %{version}-%{release} + +%description skinny +Modules for Asterisk that support the SCCP/Skinny protocol. + +%package snmp +Summary: Module that enables SNMP monitoring of Asterisk +Group: Applications/Internet +Requires: asterisk = %{version}-%{release} +BuildRequires: net-snmp-devel +BuildRequires: lm_sensors-devel + +%description snmp +Module that enables SNMP monitoring of Asterisk. + +%package tds +Summary: Modules for Asterisk that use FreeTDS +Group: Applications/Internet +Requires: asterisk = %{version}-%{release} +BuildRequires: freetds-devel + +%description tds +Modules for Asterisk that use FreeTDS. + +%package voicemail +Summary: Common Voicemail Modules for Asterisk +Group: Applications/Internet +Requires: asterisk = %{version}-%{release} +Requires: asterisk-voicemail-implementation = %{version}-%{release} +Requires: /usr/bin/sox +Requires: /usr/sbin/sendmail + +%description voicemail +Common Voicemail Modules for Asterisk. + +%package voicemail-imap +Summary: Store voicemail on an IMAP server +Group: Applications/Internet +Requires: asterisk = %{version}-%{release} +Requires: asterisk-voicemail = %{version}-%{release} +Provides: asterisk-voicemail-implementation = %{version}-%{release} +BuildRequires: uw-imap-devel + +%description voicemail-imap +Voicemail implementation for Asterisk that stores voicemail on an IMAP +server. + +%package voicemail-odbc +Summary: Store voicemail in a database using ODBC +Group: Applications/Internet +Requires: asterisk = %{version}-%{release} +Requires: asterisk-voicemail = %{version}-%{release} +Provides: asterisk-voicemail-implementation = %{version}-%{release} + +%description voicemail-odbc +Voicemail implementation for Asterisk that uses ODBC to store +voicemail in a database. + +%package voicemail-plain +Summary: Store voicemail on the local filesystem +Group: Applications/Internet +Requires: asterisk = %{version}-%{release} +Requires: asterisk-voicemail = %{version}-%{release} +Provides: asterisk-voicemail-implementation = %{version}-%{release} + +%description voicemail-plain +Voicemail implementation for Asterisk that stores voicemail on the +local filesystem. + +%package zaptel +Summary: Modules for Asterisk that use Zaptel +Group: Applications/Internet +Requires: asterisk = %{version}-%{release} +Requires: zaptel >= 1.4.0 +Requires(pre): %{_sbindir}/usermod +BuildRequires: zaptel-devel >= 1.4.0 +BuildRequires: libpri-devel >= 1.4.0 + +%description zaptel +Modules for Asterisk that use Zaptel. + +%prep +%setup0 -q +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 +%patch7 -p1 +%patch8 -p1 +%patch9 -p1 +%patch10 -p1 + +cp %{SOURCE2} menuselect.makedeps +cp %{SOURCE3} menuselect.makeopts + +# Fixup makefile so sound archives aren't downloaded/installed +%{__perl} -pi -e 's/^all:.*$/all:/' sounds/Makefile +%{__perl} -pi -e 's/^install:.*$/install:/' sounds/Makefile + +# convert comments in one file to UTF-8 +mv main/fskmodem.c main/fskmodem.c.old +iconv -f iso-8859-1 -t utf-8 -o main/fskmodem.c main/fskmodem.c.old +touch -r main/fskmodem.c.old main/fskmodem.c + +%build + +pushd menuselect/mxml +%configure +popd + +pushd menuselect +%configure +popd + +pushd main/editline +%configure +popd + +%configure --with-imap=system --with-gsm=/usr + +ASTCFLAGS="%{optflags}" make DEBUG= OPTIMIZE= ASTVARRUNDIR=%{_localstatedir}/run/asterisk ASTDATADIR=%{_datadir}/asterisk NOISY_BUILD=1 + +rm apps/app_voicemail.o apps/app_directory.o +mv apps/app_voicemail.so apps/app_voicemail_plain.so +mv apps/app_directory.so apps/app_directory_plain.so + +%{__sed} -i -e 's/^MENUSELECT_OPTS_app_voicemail=.*$/MENUSELECT_OPTS_app_voicemail=IMAP_STORAGE/' menuselect.makeopts +ASTCFLAGS="%{optflags}" make DEBUG= OPTIMIZE= ASTVARRUNDIR=%{_localstatedir}/run/asterisk ASTDATADIR=%{_datadir}/asterisk NOISY_BUILD=1 + +rm apps/app_voicemail.o apps/app_directory.o +mv apps/app_voicemail.so apps/app_voicemail_imap.so +mv apps/app_directory.so apps/app_directory_imap.so + +%{__sed} -i -e 's/^MENUSELECT_OPTS_app_voicemail=.*$/MENUSELECT_OPTS_app_voicemail=ODBC_STORAGE/' menuselect.makeopts +ASTCFLAGS="%{optflags}" make DEBUG= OPTIMIZE= ASTVARRUNDIR=%{_localstatedir}/run/asterisk ASTDATADIR=%{_datadir}/asterisk NOISY_BUILD=1 + +rm apps/app_voicemail.o apps/app_directory.o +mv apps/app_voicemail.so apps/app_voicemail_odbc.so +mv apps/app_directory.so apps/app_directory_odbc.so + +# so that these modules don't get built again during the install phase +touch apps/app_voicemail.o apps/app_directory.o +touch apps/app_voicemail.so apps/app_directory.so + +ASTCFLAGS="%{optflags}" make progdocs DEBUG= OPTIMIZE= ASTVARRUNDIR=%{_localstatedir}/run/asterisk ASTDATADIR=%{_datadir}/asterisk NOISY_BUILD=1 + +# fix dates so that we don't get multilib conflicts +find doc/api/html -type f -print0 | xargs --null touch -r ChangeLog + +%install +rm -rf %{buildroot} + +ASTCFLAGS="%{optflags}" make install DEBUG= OPTIMIZE= DESTDIR=%{buildroot} ASTVARRUNDIR=%{_localstatedir}/run/asterisk ASTDATADIR=%{_datadir}/asterisk +ASTCFLAGS="%{optflags}" make samples DEBUG= OPTIMIZE= DESTDIR=%{buildroot} ASTVARRUNDIR=%{_localstatedir}/run/asterisk ASTDATADIR=%{_datadir}/asterisk + +install -D -p -m 0755 contrib/init.d/rc.redhat.asterisk %{buildroot}%{_initrddir}/asterisk +install -D -p -m 0644 contrib/sysconfig/asterisk %{buildroot}%{_sysconfdir}/sysconfig/asterisk +install -D -p -m 0644 %{S:1} %{buildroot}%{_sysconfdir}/logrotate.d/asterisk +install -D -p -m 0644 doc/asterisk-mib.txt %{buildroot}%{_datadir}/snmp/mibs/ASTERISK-MIB.txt +install -D -p -m 0644 doc/digium-mib.txt %{buildroot}%{_datadir}/snmp/mibs/DIGIUM-MIB.txt + +rm %{buildroot}%{_libdir}/asterisk/modules/app_directory.so +rm %{buildroot}%{_libdir}/asterisk/modules/app_voicemail.so +install -D -p -m 0755 apps/app_directory_imap.so %{buildroot}%{_libdir}/asterisk/modules/ +install -D -p -m 0755 apps/app_voicemail_imap.so %{buildroot}%{_libdir}/asterisk/modules/ +install -D -p -m 0755 apps/app_directory_odbc.so %{buildroot}%{_libdir}/asterisk/modules/ +install -D -p -m 0755 apps/app_voicemail_odbc.so %{buildroot}%{_libdir}/asterisk/modules/ +install -D -p -m 0755 apps/app_directory_plain.so %{buildroot}%{_libdir}/asterisk/modules/ +install -D -p -m 0755 apps/app_voicemail_plain.so %{buildroot}%{_libdir}/asterisk/modules/ + +# create some directories that need to be packaged +mkdir -p %{buildroot}%{_datadir}/asterisk/moh/ +mkdir -p %{buildroot}%{_datadir}/asterisk/sounds/ +mkdir -p %{buildroot}%{_localstatedir}/lib/asterisk +mkdir -p %{buildroot}%{_localstatedir}/log/asterisk/cdr-custom/ +mkdir -p %{buildroot}%{_localstatedir}/spool/asterisk/outgoing/ + +# We're not going to package any of the sample AGI scripts +rm -f %{buildroot}%{_datadir}/asterisk/agi-bin/* + +# Don't package the sample voicemail user +rm -rf %{buildroot}%{_localstatedir}/spool/asterisk/voicemail/default + +find doc/api/html -name \*.map -size 0 -delete + +%clean +rm -rf %{buildroot} + +%pre +%{_sbindir}/groupadd -r asterisk &>/dev/null || : +%{_sbindir}/useradd -r -s /sbin/nologin -d /var/lib/asterisk -M \ + -c 'Asterisk User' -g asterisk asterisk &>/dev/null || : + +%post +# Register the asterisk service +/sbin/chkconfig --add asterisk + +%preun +if [ "$1" -eq "0" ]; then + /sbin/service asterisk stop > /dev/null 2>&1 || : + /sbin/chkconfig --del asterisk +fi + +%pre misdn +%{_sbindir}/usermod -a -G misdn asterisk + +%pre zaptel +%{_sbindir}/usermod -a -G zaptel asterisk + +%files +%defattr(-,root,root,-) +%doc README* *.txt ChangeLog BUGS CREDITS configs + +%doc doc/00README.1st +%doc doc/ael.txt +%doc doc/ajam.txt +%doc doc/app-sms.txt +%doc doc/apps.txt +%doc doc/asterisk-conf.txt +%doc doc/asterisk.sgml +%doc doc/backtrace.txt +%doc doc/billing.txt +%doc doc/callfiles.txt +%doc doc/callingpres.txt +%doc doc/cdrdriver.txt +%doc doc/chaniax.txt +%doc doc/channels.txt +%doc doc/channelvariables.txt +%doc doc/cliprompt.txt +%doc doc/configuration.txt +%doc doc/cygwin.txt +%doc doc/dundi.txt +%doc doc/enum.txt +%doc doc/extconfig.txt +%doc doc/extensions.txt +%doc doc/externalivr.txt +%doc doc/h323.txt +%doc doc/hardware.txt +%doc doc/iax.txt +%doc doc/ices.txt +%doc doc/ip-tos.txt +%doc doc/jitterbuffer.txt +%doc doc/localchannel.txt +%doc doc/macroexclusive.txt +%doc doc/manager.txt +%doc doc/math.txt +%doc doc/model.txt +%doc doc/PEERING +%doc doc/privacy.txt +%doc doc/queuelog.txt +%doc doc/queues-with-callback-members.txt +%doc doc/realtime.txt +%doc doc/rtp-packetization.txt +%doc doc/security.txt +%doc doc/sla.pdf +%doc doc/sla.tex +%doc doc/smdi.txt +%doc doc/sms.txt +%doc doc/speechrec.txt +%doc doc/video.txt + +%{_initrddir}/asterisk +%config(noreplace) %{_sysconfdir}/sysconfig/asterisk + +%dir %{_libdir}/asterisk +%dir %{_libdir}/asterisk/modules + +%{_libdir}/asterisk/modules/app_adsiprog.so +%{_libdir}/asterisk/modules/app_alarmreceiver.so +%{_libdir}/asterisk/modules/app_amd.so +%{_libdir}/asterisk/modules/app_authenticate.so +%{_libdir}/asterisk/modules/app_cdr.so +%{_libdir}/asterisk/modules/app_chanisavail.so +%{_libdir}/asterisk/modules/app_channelredirect.so +%{_libdir}/asterisk/modules/app_chanspy.so +%{_libdir}/asterisk/modules/app_controlplayback.so +%{_libdir}/asterisk/modules/app_db.so +%{_libdir}/asterisk/modules/app_dial.so +%{_libdir}/asterisk/modules/app_dictate.so +%{_libdir}/asterisk/modules/app_directed_pickup.so +%{_libdir}/asterisk/modules/app_disa.so +%{_libdir}/asterisk/modules/app_dumpchan.so +%{_libdir}/asterisk/modules/app_echo.so +%{_libdir}/asterisk/modules/app_exec.so +%{_libdir}/asterisk/modules/app_externalivr.so +%{_libdir}/asterisk/modules/app_followme.so +%{_libdir}/asterisk/modules/app_forkcdr.so +%{_libdir}/asterisk/modules/app_getcpeid.so +%{_libdir}/asterisk/modules/app_ices.so +%{_libdir}/asterisk/modules/app_image.so +%{_libdir}/asterisk/modules/app_lookupblacklist.so +%{_libdir}/asterisk/modules/app_lookupcidname.so +%{_libdir}/asterisk/modules/app_macro.so +%{_libdir}/asterisk/modules/app_milliwatt.so +%{_libdir}/asterisk/modules/app_mixmonitor.so +%{_libdir}/asterisk/modules/app_morsecode.so +%{_libdir}/asterisk/modules/app_nbscat.so +%{_libdir}/asterisk/modules/app_parkandannounce.so +%{_libdir}/asterisk/modules/app_playback.so +%{_libdir}/asterisk/modules/app_privacy.so +%{_libdir}/asterisk/modules/app_queue.so +%{_libdir}/asterisk/modules/app_random.so +%{_libdir}/asterisk/modules/app_readfile.so +%{_libdir}/asterisk/modules/app_read.so +%{_libdir}/asterisk/modules/app_realtime.so +%{_libdir}/asterisk/modules/app_record.so +%{_libdir}/asterisk/modules/app_sayunixtime.so +%{_libdir}/asterisk/modules/app_senddtmf.so +%{_libdir}/asterisk/modules/app_sendtext.so +%{_libdir}/asterisk/modules/app_setcallerid.so +%{_libdir}/asterisk/modules/app_setcdruserfield.so +%{_libdir}/asterisk/modules/app_settransfercapability.so +%{_libdir}/asterisk/modules/app_sms.so +%{_libdir}/asterisk/modules/app_softhangup.so +%{_libdir}/asterisk/modules/app_speech_utils.so +%{_libdir}/asterisk/modules/app_stack.so +%{_libdir}/asterisk/modules/app_system.so +%{_libdir}/asterisk/modules/app_talkdetect.so +%{_libdir}/asterisk/modules/app_test.so +%{_libdir}/asterisk/modules/app_transfer.so +%{_libdir}/asterisk/modules/app_url.so +%{_libdir}/asterisk/modules/app_userevent.so +%{_libdir}/asterisk/modules/app_verbose.so +%{_libdir}/asterisk/modules/app_waitforring.so +%{_libdir}/asterisk/modules/app_waitforsilence.so +%{_libdir}/asterisk/modules/app_while.so +%{_libdir}/asterisk/modules/app_zapateller.so +%{_libdir}/asterisk/modules/cdr_csv.so +%{_libdir}/asterisk/modules/cdr_custom.so +%{_libdir}/asterisk/modules/cdr_manager.so +%{_libdir}/asterisk/modules/chan_agent.so +%{_libdir}/asterisk/modules/chan_features.so +%{_libdir}/asterisk/modules/chan_iax2.so +%{_libdir}/asterisk/modules/chan_local.so +%{_libdir}/asterisk/modules/chan_mgcp.so +%{_libdir}/asterisk/modules/chan_phone.so +%{_libdir}/asterisk/modules/chan_sip.so +%{_libdir}/asterisk/modules/codec_adpcm.so +%{_libdir}/asterisk/modules/codec_alaw.so +%{_libdir}/asterisk/modules/codec_a_mu.so +%{_libdir}/asterisk/modules/codec_g726.so +%{_libdir}/asterisk/modules/codec_gsm.so +%{_libdir}/asterisk/modules/codec_lpc10.so +%{_libdir}/asterisk/modules/codec_speex.so +%{_libdir}/asterisk/modules/codec_ulaw.so +%{_libdir}/asterisk/modules/format_g723.so +%{_libdir}/asterisk/modules/format_g726.so +%{_libdir}/asterisk/modules/format_g729.so +%{_libdir}/asterisk/modules/format_gsm.so +%{_libdir}/asterisk/modules/format_h263.so +%{_libdir}/asterisk/modules/format_h264.so +%{_libdir}/asterisk/modules/format_jpeg.so +%{_libdir}/asterisk/modules/format_ogg_vorbis.so +%{_libdir}/asterisk/modules/format_pcm.so +%{_libdir}/asterisk/modules/format_sln.so +%{_libdir}/asterisk/modules/format_vox.so +%{_libdir}/asterisk/modules/format_wav_gsm.so +%{_libdir}/asterisk/modules/format_wav.so +%{_libdir}/asterisk/modules/func_base64.so +%{_libdir}/asterisk/modules/func_callerid.so +%{_libdir}/asterisk/modules/func_cdr.so +%{_libdir}/asterisk/modules/func_channel.so +%{_libdir}/asterisk/modules/func_curl.so +%{_libdir}/asterisk/modules/func_cut.so +%{_libdir}/asterisk/modules/func_db.so +%{_libdir}/asterisk/modules/func_enum.so +%{_libdir}/asterisk/modules/func_env.so +%{_libdir}/asterisk/modules/func_global.so +%{_libdir}/asterisk/modules/func_groupcount.so +%{_libdir}/asterisk/modules/func_language.so +%{_libdir}/asterisk/modules/func_logic.so +%{_libdir}/asterisk/modules/func_math.so +%{_libdir}/asterisk/modules/func_md5.so +%{_libdir}/asterisk/modules/func_moh.so +%{_libdir}/asterisk/modules/func_rand.so +%{_libdir}/asterisk/modules/func_realtime.so +%{_libdir}/asterisk/modules/func_sha1.so +%{_libdir}/asterisk/modules/func_strings.so +%{_libdir}/asterisk/modules/func_timeout.so +%{_libdir}/asterisk/modules/func_uri.so +%{_libdir}/asterisk/modules/pbx_ael.so +%{_libdir}/asterisk/modules/pbx_config.so +%{_libdir}/asterisk/modules/pbx_dundi.so +%{_libdir}/asterisk/modules/pbx_loopback.so +%{_libdir}/asterisk/modules/pbx_realtime.so +%{_libdir}/asterisk/modules/pbx_spool.so +%{_libdir}/asterisk/modules/res_adsi.so +%{_libdir}/asterisk/modules/res_agi.so +%{_libdir}/asterisk/modules/res_clioriginate.so +%{_libdir}/asterisk/modules/res_convert.so +%{_libdir}/asterisk/modules/res_crypto.so +%{_libdir}/asterisk/modules/res_features.so +%{_libdir}/asterisk/modules/res_indications.so +%{_libdir}/asterisk/modules/res_monitor.so +%{_libdir}/asterisk/modules/res_musiconhold.so +%{_libdir}/asterisk/modules/res_smdi.so +%{_libdir}/asterisk/modules/res_speech.so + +%{_sbindir}/aelparse +%{_sbindir}/asterisk +%{_sbindir}/astgenkey +%{_sbindir}/astman +%{_sbindir}/autosupport +%{_sbindir}/muted +%{_sbindir}/rasterisk +%{_sbindir}/safe_asterisk +%{_sbindir}/smsq +%{_sbindir}/stereorize +%{_sbindir}/streamplayer + +%{_mandir}/man8/asterisk.8* +%{_mandir}/man8/astgenkey.8* +%{_mandir}/man8/autosupport.8* +%{_mandir}/man8/safe_asterisk.8* + +%dir %{_sysconfdir}/asterisk +%config(noreplace) %{_sysconfdir}/asterisk/adsi.conf +%config(noreplace) %{_sysconfdir}/asterisk/adtranvofr.conf +%config(noreplace) %{_sysconfdir}/asterisk/agents.conf +%config(noreplace) %{_sysconfdir}/asterisk/alarmreceiver.conf +%config(noreplace) %{_sysconfdir}/asterisk/amd.conf +%config(noreplace) %{_sysconfdir}/asterisk/asterisk.adsi +%config(noreplace) %{_sysconfdir}/asterisk/asterisk.conf +%config(noreplace) %{_sysconfdir}/asterisk/cdr.conf +%config(noreplace) %{_sysconfdir}/asterisk/cdr_custom.conf +%config(noreplace) %{_sysconfdir}/asterisk/cdr_manager.conf +%config(noreplace) %{_sysconfdir}/asterisk/codecs.conf +%config(noreplace) %{_sysconfdir}/asterisk/dnsmgr.conf +%config(noreplace) %{_sysconfdir}/asterisk/dundi.conf +%config(noreplace) %{_sysconfdir}/asterisk/enum.conf +%config(noreplace) %{_sysconfdir}/asterisk/extconfig.conf +%config(noreplace) %{_sysconfdir}/asterisk/extensions.ael +%config(noreplace) %{_sysconfdir}/asterisk/extensions.conf +%config(noreplace) %{_sysconfdir}/asterisk/features.conf +%config(noreplace) %{_sysconfdir}/asterisk/followme.conf +%config(noreplace) %{_sysconfdir}/asterisk/h323.conf +%config(noreplace) %{_sysconfdir}/asterisk/http.conf +%config(noreplace) %{_sysconfdir}/asterisk/iax.conf +%config(noreplace) %{_sysconfdir}/asterisk/iaxprov.conf +%config(noreplace) %{_sysconfdir}/asterisk/indications.conf +%config(noreplace) %{_sysconfdir}/asterisk/logger.conf +%config(noreplace) %{_sysconfdir}/asterisk/manager.conf +%config(noreplace) %{_sysconfdir}/asterisk/mgcp.conf +%config(noreplace) %{_sysconfdir}/asterisk/modules.conf +%config(noreplace) %{_sysconfdir}/asterisk/musiconhold.conf +%config(noreplace) %{_sysconfdir}/asterisk/muted.conf +%config(noreplace) %{_sysconfdir}/asterisk/osp.conf +%config(noreplace) %{_sysconfdir}/asterisk/phone.conf +%config(noreplace) %{_sysconfdir}/asterisk/privacy.conf +%config(noreplace) %{_sysconfdir}/asterisk/queues.conf +%config(noreplace) %{_sysconfdir}/asterisk/rpt.conf +%config(noreplace) %{_sysconfdir}/asterisk/rtp.conf +%config(noreplace) %{_sysconfdir}/asterisk/say.conf +%config(noreplace) %{_sysconfdir}/asterisk/sip.conf +%config(noreplace) %{_sysconfdir}/asterisk/sip_notify.conf +%config(noreplace) %{_sysconfdir}/asterisk/sla.conf +%config(noreplace) %{_sysconfdir}/asterisk/smdi.conf +%config(noreplace) %{_sysconfdir}/asterisk/telcordia-1.adsi +%config(noreplace) %{_sysconfdir}/asterisk/udptl.conf +%config(noreplace) %{_sysconfdir}/asterisk/users.conf +%config(noreplace) %{_sysconfdir}/asterisk/vpb.conf + +%config(noreplace) %{_sysconfdir}/logrotate.d/asterisk + +%dir %{_datadir}/asterisk/ +%dir %{_datadir}/asterisk/agi-bin/ +%{_datadir}/asterisk/images/ +%{_datadir}/asterisk/keys/ +%{_datadir}/asterisk/static-http/ +%dir %{_datadir}/asterisk/moh/ +%dir %{_datadir}/asterisk/sounds/ + +%attr(0750,asterisk,asterisk) %dir %{_localstatedir}/lib/asterisk/ + +%attr(0750,asterisk,asterisk) %dir %{_localstatedir}/log/asterisk/ +%attr(0750,asterisk,asterisk) %dir %{_localstatedir}/log/asterisk/cdr-csv/ +%attr(0750,asterisk,asterisk) %dir %{_localstatedir}/log/asterisk/cdr-custom/ + +%attr(0750,asterisk,asterisk) %dir %{_localstatedir}/spool/asterisk/ +%attr(0770,asterisk,asterisk) %dir %{_localstatedir}/spool/asterisk/outgoing/ +%attr(0750,asterisk,asterisk) %dir %{_localstatedir}/spool/asterisk/tmp/ +%attr(0750,asterisk,asterisk) %dir %{_localstatedir}/spool/asterisk/voicemail/ + +%attr(0755,asterisk,asterisk) %dir %{_localstatedir}/run/asterisk + +%files alsa +%defattr(-,root,root,-) +%config(noreplace) %{_sysconfdir}/asterisk/alsa.conf +%{_libdir}/asterisk/modules/chan_alsa.so + +%files apidoc +%defattr(-,root,root,-) +%doc doc/api/html/* + +%files conference +%defattr(-,root,root,-) +%doc apps/conference/CLI.txt +%doc apps/conference/Flags.txt +%doc apps/conference/LICENSE +%doc apps/conference/README +%doc apps/conference/README.videoswitch +%doc apps/conference/TODO +%{_libdir}/asterisk/modules/app_conference.so + +%files curl +%defattr(-,root,root,-) +%{_libdir}/asterisk/modules/func_curl.so + +%files devel +%defattr(-,root,root,-) +%doc doc/CODING-GUIDELINES +%doc doc/datastores.txt +%doc doc/linkedlists.txt +%doc doc/modules.txt +%doc doc/valgrind.txt + +%dir %{_includedir}/asterisk +%{_includedir}/asterisk.h +%{_includedir}/asterisk/*.h + +%files fax +%defattr(-,root,root,-) +%{_libdir}/asterisk/modules/app_rxfax.so +%{_libdir}/asterisk/modules/app_txfax.so + +%files festival +%defattr(-,root,root,-) +%config(noreplace) %{_sysconfdir}/asterisk/festival.conf +%{_libdir}/asterisk/modules/app_festival.so + +%files firmware +%defattr(-,root,root,-) +%{_datadir}/asterisk/firmware/ + +%files jabber +%defattr(-,root,root,-) +%doc doc/jabber.txt +%doc doc/jingle.txt +%config(noreplace) %{_sysconfdir}/asterisk/gtalk.conf +%config(noreplace) %{_sysconfdir}/asterisk/jabber.conf +%{_libdir}/asterisk/modules/chan_gtalk.so +%{_libdir}/asterisk/modules/res_jabber.so + +%files misdn +%defattr(-,root,root,-) +%doc doc/misdn.txt +%config(noreplace) %{_sysconfdir}/asterisk/misdn.conf +%{_libdir}/asterisk/modules/chan_misdn.so + +%files mobile +%defattr(-,root,root,-) +%doc doc/chan_mobile.txt +%config(noreplace) %{_sysconfdir}/asterisk/mobile.conf +%{_libdir}/asterisk/modules/chan_mobile.so + +%files odbc +%defattr(-,root,root,-) +%config(noreplace) %{_sysconfdir}/asterisk/cdr_odbc.conf +%config(noreplace) %{_sysconfdir}/asterisk/func_odbc.conf +%config(noreplace) %{_sysconfdir}/asterisk/res_odbc.conf +%{_libdir}/asterisk/modules/cdr_odbc.so +%{_libdir}/asterisk/modules/func_odbc.so +%{_libdir}/asterisk/modules/res_config_odbc.so +%{_libdir}/asterisk/modules/res_odbc.so + +%files oss +%defattr(-,root,root,-) +%config(noreplace) %{_sysconfdir}/asterisk/oss.conf +%{_libdir}/asterisk/modules/chan_oss.so + +%files postgresql +%defattr(-,root,root,-) +%config(noreplace) %{_sysconfdir}/asterisk/cdr_pgsql.conf +%config(noreplace) %{_sysconfdir}/asterisk/res_pgsql.conf +%{_libdir}/asterisk/modules/cdr_pgsql.so +%{_libdir}/asterisk/modules/res_config_pgsql.so + +%files radius +%defattr(-,root,root,-) +%doc doc/radius.txt +%{_libdir}/asterisk/modules/cdr_radius.so + +%files skinny +%defattr(-,root,root,-) +%config(noreplace) %{_sysconfdir}/asterisk/skinny.conf +%{_libdir}/asterisk/modules/chan_skinny.so + +%files snmp +%defattr(-,root,root,-) +%doc doc/asterisk-mib.txt +%doc doc/digium-mib.txt +%doc doc/snmp.txt +%config(noreplace) %{_sysconfdir}/asterisk/res_snmp.conf +%{_datadir}/snmp/mibs/ASTERISK-MIB.txt +%{_datadir}/snmp/mibs/DIGIUM-MIB.txt +%{_libdir}/asterisk/modules/res_snmp.so + +%files tds +%defattr(-,root,root,-) +%doc doc/freetds.txt +%config(noreplace) %{_sysconfdir}/asterisk/cdr_tds.conf +%{_libdir}/asterisk/modules/cdr_tds.so + +%files voicemail +%defattr(-,root,root,-) +%config(noreplace) %{_sysconfdir}/asterisk/voicemail.conf +%{_libdir}/asterisk/modules/app_hasnewvoicemail.so + +%files voicemail-imap +%defattr(-,root,root,) +%doc doc/imapstorage.txt +%{_libdir}/asterisk/modules/app_directory_imap.so +%{_libdir}/asterisk/modules/app_voicemail_imap.so + +%files voicemail-odbc +%defattr(-,root,root,-) +%doc doc/odbcstorage.txt +%doc doc/voicemail_odbc_postgresql.txt +%{_libdir}/asterisk/modules/app_directory_odbc.so +%{_libdir}/asterisk/modules/app_voicemail_odbc.so + +%files voicemail-plain +%defattr(-,root,root,-) +%{_libdir}/asterisk/modules/app_directory_plain.so +%{_libdir}/asterisk/modules/app_voicemail_plain.so + +%files zaptel +%defattr(-,root,root,-) +%config(noreplace) %{_sysconfdir}/asterisk/meetme.conf +%config(noreplace) %{_sysconfdir}/asterisk/zapata.conf +%{_libdir}/asterisk/modules/app_flash.so +%{_libdir}/asterisk/modules/app_meetme.so +%{_libdir}/asterisk/modules/app_page.so +%{_libdir}/asterisk/modules/app_zapbarge.so +%{_libdir}/asterisk/modules/app_zapras.so +%{_libdir}/asterisk/modules/app_zapscan.so +%{_libdir}/asterisk/modules/chan_zap.so +%{_libdir}/asterisk/modules/codec_zap.so + +%changelog +* Tue Dec 11 2007 Jeffrey C. Ollie - 1.4.15-3 +- Really get rid of zero length map files. + +* Mon Dec 10 2007 Jeffrey C. Ollie - 1.4.15-2 +- Get rid of zero length map files. +- Shorten descriptions of voicemail subpackages + +* Fri Nov 30 2007 Jeffrey C. Ollie - 1.4.15-1 +- Update to 1.4.15 + +* Tue Nov 20 2007 Jeffrey C. Ollie - 1.4.14-2 +- Fix license and other rpmlint warnings. + +* Mon Nov 19 2007 Jeffrey C. Ollie - 1.4.14-1 +- Update to 1.4.14 + +* Fri Nov 16 2007 Jeffrey C. Ollie - 1.4.13-7 +- Add chan_mobile + +* Tue Nov 13 2007 Jeffrey C. Ollie - 1.4.13-6 +- Don't build cdr_sqlite because sqlite2 has been orphaned. +- Rebase local patches to latest upstream SVN +- Update app_conference patch to latest from upstream SVN +- Apply post-1.4.13 patches from upstream SVN + +* Wed Oct 10 2007 Jeffrey C. Ollie - 1.4.13-1 +- Update to 1.4.13 + +* Tue Oct 9 2007 Jeffrey C. Ollie - 1.4.12.1-1 +- Update to 1.4.12.1 + +* Wed Aug 22 2007 Jeffrey C. Ollie - 1.4.11-1 +- Update to 1.4.11 + +* Fri Aug 10 2007 Jeffrey C. Ollie - 1.4.10.1-1 +- Update to 1.4.10.1. + +* Tue Aug 7 2007 Jeffrey C. Ollie - 1.4.10-1 +- Update to 1.4.10 (security update). + +* Tue Aug 7 2007 Jeffrey C. Ollie - 1.4.9-7 +- Add a patch that allows alternate extensions to be defined in users.conf + +* Mon Aug 6 2007 Jeffrey C. Ollie - 1.4.9-6 +- Update app_conference patch. Enter/leave sounds are now possible. + +* Fri Jul 27 2007 Jeffrey C. Ollie - 1.4.9-5 +- Update patches so we don't need to run auto* tools, because autoconf + 2.60 is required and FC-6 and RHEL5 only have autoconf 2.59. + +* Thu Jul 26 2007 Jeffrey C. Ollie - 1.4.9-4 +- Don't build app_mp3 + +* Wed Jul 25 2007 Jeffrey C. Ollie - 1.4.9-3 +- Add app_conference + +* Wed Jul 25 2007 Jeffrey C. Ollie - 1.4.9-2 +- Use plain useradd/groupadd rather than the fedora-usermgmt +- Clean up requirements +- Clean up build requirements by moving them to package sections + +* Tue Jul 24 2007 Jeffrey C. Ollie - 1.4.9-1 +- Update to 1.4.9 + +* Tue Jul 17 2007 Jeffrey C. Ollie - 1.4.8-1 +- Update to 1.4.8 +- Drop ixjuser patch. + +* Tue Jul 10 2007 Jeffrey C. Ollie - 1.4.7.1-1 +- Update to 1.4.7.1 + +* Mon Jul 9 2007 Jeffrey C. Ollie - 1.4.7-1 +- Update to 1.4.7 +- RxFAX/TxFAX applications + +* Sun Jul 1 2007 Jeffrey C. Ollie - 1.4.6-4 +- It's "sbin", not "bin" silly. + +* Sat Jun 30 2007 Jeffrey C. Ollie - 1.4.6-3 +- Add patch that lets us change TOS bits even when running non-root + +* Fri Jun 29 2007 Jeffrey C. Ollie - 1.4.6-2 +- voicemail needs to require /usr/bin/sox and /usr/bin/sendmail + +* Fri Jun 29 2007 Jeffrey C. Ollie - 1.4.6-1 +- Update to 1.4.6 +- Remove upstreamed patch. + +* Thu Jun 21 2007 Jeffrey C. Ollie - 1.4.5-10 +- Build the IMAP and ODBC storage options of voicemail and split + voicemail out into subpackages. +- Apply patch so that the system UW IMAP libray can be linked against. +- Patch modules.conf.sample so that alternal voicemail modules don't + get loaded simultaneously. +- Link against system GSM library rather than internal copy. +- Patch the Makefile so that it doesn't add redundant/wrong compiler + options. +- Force building with the standard RPM optimization flags. +- Install the Asterisk MIB in a location that net-snmp can find it. +- Only package docs in the main package that are relevant and that + haven't been packaged by a subpackage. +- Other minor cleanups. + +* Mon Jun 18 2007 Jeffrey C. Ollie - 1.4.5-9 +- Move sounds + +* Mon Jun 18 2007 Jeffrey C. Ollie - 1.4.5-8 +- Update some more ownership/permissions + +* Mon Jun 18 2007 Jeffrey C. Ollie - 1.4.5-7 +- Fix some permissions. + +* Mon Jun 18 2007 Jeffrey C. Ollie - 1.4.5-6 +- Update init script patch +- Move pid file to subdir of /var/run + +* Mon Jun 18 2007 Jeffrey C. Ollie - 1.4.5-5 +- Update init script patch to run as non-root + +* Sun Jun 17 2007 Jeffrey C. Ollie - 1.4.5-4 +- Build modules that depend on FreeTDS. +- Don't build voicemail with ODBC storage. + +* Sun Jun 17 2007 Jeffrey C. Ollie - 1.4.5-3 +- Have the build output the commands executing, rather than covering them up. + +* Fri Jun 15 2007 Jeffrey C. Ollie - 1.4.5-1 +- Update to 1.4.5 +- Remove upstreamed patch. + +* Wed May 9 2007 Jeffrey C. Ollie - 1.4.4-2 +- Add a patch to fix CVE-2007-2488/ASA-2007-013 + +* Fri Apr 27 2007 Jeffrey C. Ollie - 1.4.4-1 +- Update to 1.4.4 + +* Wed Mar 21 2007 Jeffrey C. Ollie - 1.4.2-1 +- Update to 1.4.2 + +* Tue Mar 6 2007 Jeffrey C. Ollie - 1.4.1-2 +- Package the IAXy firmware +- Minor clean-ups in files + +* Mon Mar 5 2007 Jeffrey C. Ollie - 1.4.1-1 +- Update to 1.4.1 +- Don't build/package codec_zap (zaptel 1.4.0 doesn't support it) + +* Fri Dec 15 2006 Jeffrey C. Ollie - 1.4.0-6.beta4 +- Update to 1.4.0-beta4 +- Various cleanups. + +* Fri Oct 20 2006 Jeffrey C. Ollie - 1.4.0-5.beta3 +- Don't package IAXy firmware because of license +- Don't build app_rpt +- Don't BR lm_sensors on PPC +- Better way to prevent download/installation of sound archives +- Redo tarball to eliminate non-free items + +* Thu Oct 19 2006 Jeffrey C. Ollie - 1.4.0-4.beta3 +- Remove explicit dependency on glibc-kernheaders. +- Build jabber modules on PPC + +* Wed Oct 18 2006 Jeffrey C. Ollie - 1.4.0-3.beta3 +- *Really* update to beta3 +- chan_jingle has been taken out of 1.4 +- Move misplaced binaries to where they should be + +* Wed Oct 18 2006 Jeffrey C. Ollie - 1.4.0-2.beta3 +- Remove requirement on asterisk-sounds-core until licensing can be + figured out. + +* Wed Oct 18 2006 Jeffrey C. Ollie - 1.4.0-1.beta3 +- Update to 1.4.0-beta3 + +* Sun Oct 15 2006 Jeffrey C. Ollie - 1.4.0-0.beta2 +- Update to 1.4.0-beta2 + +* Tue Jul 25 2006 Jeffrey C. Ollie - 1.2.10-1 +- Update to 1.2.10. + +* Wed Jun 7 2006 Jeffrey C. Ollie - 1.2.9.1 +- Update to 1.2.9.1 + +* Fri Jun 2 2006 Jeffrey C. Ollie - 1.2.8 +- Update to 1.2.8 +- Add misdn.conf to list of configs. +- Drop chan_bluetooth patch for now... + +* Tue May 2 2006 Jeffrey C. Ollie - 1.2.7.1-6 +- Zaptel subpackage shouldn't obsolete the sqlite subpackage. +- Remove mISDN until build issues can be figured out. + +* Mon Apr 24 2006 Jeffrey C. Ollie - 1.2.7.1-5 +- Build mISDN channel drivers, modelled after spec file from David Woodhouse + +* Thu Apr 20 2006 Jeffrey C. Ollie - 1.2.7.1-4 +- Update chan_bluetooth patch with some additional information as to + it's source and comment out more in the configuration file. + +* Thu Apr 20 2006 Jeffrey C. Ollie - 1.2.7.1-3 +- Add chan_bluetooth + +* Wed Apr 19 2006 Jeffrey C. Ollie - 1.2.7.1-2 +- Split off more stuff into subpackages. + +* Wed Apr 12 2006 Jeffrey C. Ollie - 1.2.7-1 +- Update to 1.2.7 + +* Mon Apr 10 2006 Jeffrey C. Ollie - 1.2.6-3 +- Fix detection of libpri on 64 bit arches (taken from Matthias Saou's rpmforge package) +- Change sqlite subpackage name to sqlite2 (there are sqlite3 modules in development). + +* Thu Apr 6 2006 Jeffrey C. Ollie - 1.2.6-2 +- Don't build GTK 1.X console since GTK 1.X is being moved out of core... + +* Mon Mar 27 2006 Jeffrey C. Ollie - 1.2.6-1 +- Update to 1.2.6 + +* Mon Mar 6 2006 Jeffrey C. Ollie - 1.2.5-1 +- Update to 1.2.5. +- Removed upstreamed MOH patch. +- Add full urls to the app_(r|t)xfax.c sources. +- Update spandsp patch. + +* Mon Feb 13 2006 Jeffrey C. Ollie - 1.2.4-4 +- Actually apply the patch. + +* Mon Feb 13 2006 Jeffrey C. Ollie - 1.2.4-3 +- Add patch to keep Asterisk from crashing when using MOH inside a MeetMe conference. + +* Mon Feb 6 2006 Jeffrey C. Ollie - 1.2.4-2 +- BR sqlite2-devel + +* Tue Jan 31 2006 Jeffrey C. Ollie - 1.2.4-1 +- Update to 1.2.4. + +* Wed Jan 25 2006 Jeffrey C. Ollie - 1.2.3-4 +- Took some tricks from Asterisk packages by Roy-Magne Mo. +- Enable gtk console module. +- BR gtk+-devel. +- Add logrotate script. +- BR sqlite2-devel and new sqlite subpackage. +- BR doxygen and graphviz for building duxygen documentation. (But don't build it yet.) + +* Wed Jan 25 2006 Jeffrey C. Ollie - 1.2.3-3 +- Completely eliminate the "asterisk" user from the spec file. +- Move more config files to subpackages. +- Consolidate two patches that patch the init script. +- BR curl-devel +- BR alsa-lib-devel +- alsa, curl, oss subpackages + +* Wed Jan 25 2006 Jeffrey C. Ollie - 1.2.3-2 +- Do not run as user "asterisk" as that prevents setting of IP TOS (which is bad for quality of service). +- Add patch for setting TOS separately for SIP and RTP packets. + +* Wed Jan 25 2006 Jeffrey C. Ollie - 1.2.3-1 +- First version for Fedora Extras. + diff --git a/menuselect.makedeps b/menuselect.makedeps new file mode 100644 index 0000000..c15ac84 --- /dev/null +++ b/menuselect.makedeps @@ -0,0 +1,51 @@ +MENUSELECT_DEPENDS_app_adsiprog=RES_ADSI +MENUSELECT_DEPENDS_app_flash=ZAPTEL +MENUSELECT_DEPENDS_app_meetme=ZAPTEL +MENUSELECT_DEPENDS_app_osplookup=OSPTK SSL +MENUSELECT_DEPENDS_app_page=ZAPTEL +MENUSELECT_DEPENDS_app_rpt=ZAPTEL TONEZONE +MENUSELECT_DEPENDS_app_voicemail=RES_ADSI +MENUSELECT_DEPENDS_app_zapbarge=ZAPTEL +MENUSELECT_DEPENDS_app_zapras=ZAPTEL +MENUSELECT_DEPENDS_app_zapscan=ZAPTEL +MENUSELECT_DEPENDS_cdr_odbc=UNIXODBC +MENUSELECT_DEPENDS_cdr_pgsql=PGSQL +MENUSELECT_DEPENDS_cdr_radius=RADIUS +MENUSELECT_DEPENDS_cdr_sqlite=SQLITE +MENUSELECT_DEPENDS_cdr_tds=FREETDS +MENUSELECT_DEPENDS_chan_alsa=ASOUND +MENUSELECT_DEPENDS_chan_gtalk=IKSEMEL RES_JABBER +MENUSELECT_DEPENDS_chan_h323=OPENH323 +MENUSELECT_DEPENDS_chan_iax2=ZAPTEL +MENUSELECT_DEPENDS_chan_misdn=ISDNNET MISDN SUPPSERV +MENUSELECT_DEPENDS_chan_nbs=NBS +MENUSELECT_DEPENDS_chan_oss=OSSAUDIO +MENUSELECT_DEPENDS_chan_phone=IXJUSER +MENUSELECT_DEPENDS_chan_vpb=VPBAPI +MENUSELECT_DEPENDS_chan_zap=ZAPTEL_VLDTMF ZAPTEL TONEZONE PRI +MENUSELECT_DEPENDS_codec_gsm=GSM +MENUSELECT_DEPENDS_codec_speex=SPEEX +MENUSELECT_DEPENDS_codec_zap=ZAPTEL_TRANSCODE ZAPTEL +MENUSELECT_DEPENDS_format_ogg_vorbis=VORBIS OGG +MENUSELECT_DEPENDS_func_curl=CURL +MENUSELECT_DEPENDS_func_odbc=UNIXODBC RES_ODBC +MENUSELECT_DEPENDS_pbx_dundi=ZLIB +MENUSELECT_DEPENDS_pbx_gtkconsole=GTK +MENUSELECT_DEPENDS_pbx_kdeconsole=QT +MENUSELECT_DEPENDS_res_config_odbc=UNIXODBC RES_ODBC +MENUSELECT_DEPENDS_res_config_pgsql=PGSQL +MENUSELECT_DEPENDS_res_crypto=SSL +MENUSELECT_DEPENDS_res_jabber=IKSEMEL +MENUSELECT_DEPENDS_res_musiconhold=ZAPTEL +MENUSELECT_DEPENDS_res_odbc=UNIXODBC +MENUSELECT_DEPENDS_res_snmp=NETSNMP +MENUSELECT_DEPENDS_ODBC_STORAGE=UNIXODBC +MENUSELECT_DEPENDS_IMAP_STORAGE=IMAP_TK SSL +MENUSELECT_DEPENDS_apps=GNU_LD +MENUSELECT_DEPENDS_cdr=GNU_LD +MENUSELECT_DEPENDS_channels=GNU_LD +MENUSELECT_DEPENDS_codecs=GNU_LD +MENUSELECT_DEPENDS_formats=GNU_LD +MENUSELECT_DEPENDS_funcs=GNU_LD +MENUSELECT_DEPENDS_pbx=GNU_LD +MENUSELECT_DEPENDS_res=GNU_LD diff --git a/menuselect.makeopts b/menuselect.makeopts new file mode 100644 index 0000000..c4f2bad --- /dev/null +++ b/menuselect.makeopts @@ -0,0 +1,18 @@ +MENUSELECT_APPS=app_ivrdemo app_mp3 app_osplookup app_rpt app_skel +MENUSELECT_CDR=cdr_sqlite +MENUSELECT_CHANNELS=chan_h323 chan_nbs chan_vpb +MENUSELECT_CODECS= +MENUSELECT_FORMATS= +MENUSELECT_FUNCS= +MENUSELECT_PBX=pbx_gtkconsole pbx_kdeconsole +MENUSELECT_RES= +MENUSELECT_OPTS_app_voicemail= +MENUSELECT_CFLAGS=LOADABLE_MODULES +MENUSELECT_EMBED= +MENUSELECT_CORE_SOUNDS= +MENUSELECT_MOH= +MENUSELECT_EXTRA_SOUNDS= +MENUSELECT_DEPSFAILED=MENUSELECT_APPS=app_osplookup +MENUSELECT_DEPSFAILED=MENUSELECT_CHANNELS=chan_h323 +MENUSELECT_DEPSFAILED=MENUSELECT_CHANNELS=chan_nbs +MENUSELECT_DEPSFAILED=MENUSELECT_CHANNELS=chan_vpb diff --git a/sources b/sources index e69de29..475e41c 100644 --- a/sources +++ b/sources @@ -0,0 +1 @@ +a548cfe60e86013eac0851ef585d01ed asterisk-1.4.15-stripped.tar.gz