commit de lancement : 2.0.1-1
authorThomas Noel <thomas@thomas-sandbox.(none)>
Tue, 4 Mar 2008 11:03:17 +0000 (11:03 +0000)
committerThomas Noel <thomas@thomas-sandbox.(none)>
Tue, 4 Mar 2008 11:03:17 +0000 (11:03 +0000)
52 files changed:
CLI.txt [new file with mode: 0644]
Flags.txt [new file with mode: 0644]
LICENSE [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
README.videoswitch [new file with mode: 0644]
TODO [new file with mode: 0644]
app_conference.c [new file with mode: 0644]
app_conference.h [new file with mode: 0644]
app_conference.so [new file with mode: 0755]
cli.c [new file with mode: 0644]
cli.h [new file with mode: 0644]
common.h [new file with mode: 0644]
conf_frame.h [new file with mode: 0644]
conference.c [new file with mode: 0644]
conference.h [new file with mode: 0644]
debian/README.Debian [new file with mode: 0644]
debian/asterisk-app-conference-default.ex [new file with mode: 0644]
debian/asterisk-app-conference.doc-base.EX [new file with mode: 0644]
debian/changelog [new file with mode: 0644]
debian/compat [new file with mode: 0644]
debian/control [new file with mode: 0644]
debian/copyright [new file with mode: 0644]
debian/cron.d.ex [new file with mode: 0644]
debian/dirs [new file with mode: 0644]
debian/docs [new file with mode: 0644]
debian/emacsen-install.ex [new file with mode: 0644]
debian/emacsen-remove.ex [new file with mode: 0644]
debian/emacsen-startup.ex [new file with mode: 0644]
debian/init.d.ex [new file with mode: 0644]
debian/manpage.1.ex [new file with mode: 0644]
debian/manpage.sgml.ex [new file with mode: 0644]
debian/manpage.xml.ex [new file with mode: 0644]
debian/menu.ex [new file with mode: 0644]
debian/postinst.ex [new file with mode: 0644]
debian/postrm.ex [new file with mode: 0644]
debian/preinst.ex [new file with mode: 0644]
debian/prerm.ex [new file with mode: 0644]
debian/rules [new file with mode: 0755]
debian/watch.ex [new file with mode: 0644]
frame.c [new file with mode: 0644]
frame.h [new file with mode: 0644]
libspeex/misc.c [new file with mode: 0644]
libspeex/misc.h [new file with mode: 0644]
libspeex/preprocess.c [new file with mode: 0644]
libspeex/smallft.c [new file with mode: 0644]
libspeex/smallft.h [new file with mode: 0644]
libspeex/speex_preprocess.h [new file with mode: 0644]
member.c [new file with mode: 0644]
member.h [new file with mode: 0644]
sounds/appconf-enter.gsm [new file with mode: 0644]
sounds/appconf-leave.gsm [new file with mode: 0644]

diff --git a/CLI.txt b/CLI.txt
new file mode 100644 (file)
index 0000000..48acf5a
--- /dev/null
+++ b/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 name>
+
+
+- conference debug: enable debugging for a conference
+  usage: conference debug <conference_name> [ on | off ]
+
+- conference end: stops a conference
+  usage: conference end <conference name>
+
+- conference kick: kick member from a conference
+  usage: conference kick <conference_name> <member id>
+
+- conference kickchannel: kick channel from a conference
+  usage: conference kickchannel <conference_name> <channel>
+
+- 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 name> <member id>
+
+- conference lockchannel: locks incoming video to a channel
+  usage: conference lockchannel <conference name> <channel>
+
+- conference mute: mute member in a conference
+  usage: conference mute <conference_name> <member id>
+
+- conference mutechannel: mute channel in a conference
+  usage: conference mutechannel <channel>
+
+- conference play sound: play a sound to a conference member
+  usage: conference play sound <channel-id> <sound-file> [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 <conference name> <member>
+  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 name> <channel>
+
+- 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 name> <member> <text>
+
+- conference textbroadcast: sends a text message to all members in a conference
+  usage: conference textbroadcast <conference name> <text>
+
+- conference textchannel: sends a text message to a channel
+  usage: conference textchannel <conference name> <channel> <text>
+
+- conference unlock: unlocks incoming video
+  usage: conference unlock <conference name>
+
+- conference unmute: unmute member in a conference
+  usage: conference unmute <conference_name> <member id>
+
+- conference unmutechannel: unmute channel in a conference
+  usage: conference unmutechannel <channel>
+
+- conference video mute: mutes video from a member
+  usage: conference video mute <conference name> <member>
+
+- conference video mutechannel: mutes video from a channel
+  usage: conference video mutechannel <conference name> <channel>
+
+- conference video unmute: unmutes video from a member
+  usage: conference video unmute <conference name> <member>
+
+- conference video unmutechannel: unmutes video from a channel
+  usage: conference video unmutechannel <conference name> <channel>
+
+- conference viewchannel: switch video for a channel in a conference
+  usage: conference viewchannel <conference_name> <dest channel> <src channel>
+
+- conference viewstream: switch video for a member a conference
+  usage: conference viewstream <conference_name> <member id> <stream no>
+
+- conference drive: drive VAD video switching of destination member using audio from source member
+  usage: conference drive <conference name> <source member> [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 <conference name> <source channel> [destination channel]
+  If destination channel is missing, break existing connection
diff --git a/Flags.txt b/Flags.txt
new file mode 100644 (file)
index 0000000..dbb9732
--- /dev/null
+++ b/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/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..a52b16e
--- /dev/null
+++ b/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.
+\f
+                   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.)
+\f
+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.
+\f
+  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.
+\f
+  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
+\f
+           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.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    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.
+
+  <signature of Ty Coon>, 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/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..2b7f80e
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,132 @@
+# $Id: Makefile 889 2007-08-09 14:42:48Z sbalea $
+
+#
+# Makefile, based on the Asterisk Makefile, Coypright (C) 1999, Mark Spencer
+#
+# Copyright (C) 2002,2003 Junghanns.NET GmbH
+#
+# Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
+#
+# This program is free software and may be modified and
+# distributed under the terms of the GNU Public License.
+#
+
+.EXPORT_ALL_VARIABLES:
+
+#
+# app_conference defines which can be passed on the command-line
+#
+
+INSTALL_PREFIX := $(DESTDIR)
+INSTALL_MODULES_DIR := $(INSTALL_PREFIX)/usr/lib/asterisk/modules
+INSTALL_SOUNDS := $(INSTALL_PREFIX)/usr/share/asterisk/sounds
+
+ASTERISK_INCLUDE_DIR ?= ../asterisk/include
+
+REVISION = $(shell svnversion -n .)
+
+# turn app_conference debugging on or off ( 0 == OFF, 1 == ON )
+APP_CONFERENCE_DEBUG ?= 0
+
+# 0 = OFF 1 = astdsp 2 = speex
+SILDET := 2
+
+#
+# app_conference objects to build
+#
+
+OBJS = app_conference.o conference.o member.o frame.o cli.o
+TARGET = app_conference.so
+
+
+#
+# standard compile settings
+#
+
+PROC = $(shell uname -m)
+INSTALL = install
+
+INCLUDE = -I$(ASTERISK_INCLUDE_DIR)
+DEBUG := -g
+
+CC = gcc
+
+CFLAGS = -pipe -Wall -Wmissing-prototypes -Wmissing-declarations -MD -MP $(DEBUG)
+CPPFLAGS = $(INCLUDE) -D_REENTRANT -D_GNU_SOURCE -DREVISION=\"$(REVISION)\"
+
+#CFLAGS += -O2
+#CFLAGS += -O3 -march=pentium3 -msse -mfpmath=sse,387 -ffast-math
+# PERF: below is 10% faster than -O2 or -O3 alone.
+#CFLAGS += -O3 -ffast-math -funroll-loops
+# below is another 5% faster or so.
+#CFLAGS += -O3 -ffast-math -funroll-all-loops -fsingle-precision-constant
+#CFLAGS += -mcpu=7450 -faltivec -mabi=altivec -mdynamic-no-pic
+# adding -msse -mfpmath=sse has little effect.
+#CFLAGS += -O3 -msse -mfpmath=sse
+#CFLAGS += $(shell if $(CC) -march=$(PROC) -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=$(PROC)"; fi)
+CFLAGS += $(shell if uname -m | grep -q ppc; then echo "-fsigned-char"; fi)
+CFLAGS += -fPIC
+CPPFLAGS += -DCRYPTO
+
+#
+# Uncomment this if you want G.729A support (need to have the actual codec installed
+#
+# CPPFLAGS += -DAC_USE_G729A
+
+
+ifeq ($(APP_CONFERENCE_DEBUG), 1)
+CPPFLAGS += -DAPP_CONFERENCE_DEBUG
+endif
+
+#
+# additional flag values for silence detection
+#
+
+ifeq ($(SILDET), 2)
+OBJS += libspeex/preprocess.o libspeex/misc.o libspeex/smallft.o
+CPPFLAGS += -Ilibspeex -DSILDET=2
+endif
+
+ifeq ($(SILDET), 1)
+CPPFLAGS += -DSILDET=1
+endif
+
+OSARCH=$(shell uname -s)
+ifeq (${OSARCH},Darwin)
+SOLINK=-dynamic -bundle -undefined suppress -force_flat_namespace
+else
+SOLINK=-shared -Xlinker -x
+endif
+
+DEPS += $(subst .o,.d,$(OBJS))
+
+#
+# targets
+#
+
+all: $(TARGET)
+
+.PHONY: clean
+clean:
+       $(RM) $(OBJS) $(DEPS)
+
+.PHONY: distclean
+distclean: clean
+       $(RM) $(TARGET)
+
+$(TARGET): $(OBJS)
+       $(CC) -pg $(SOLINK) -o $@ $(OBJS)
+
+vad_test: vad_test.o libspeex/preprocess.o libspeex/misc.o libspeex/smallft.o
+       $(CC) $(PROFILE) -o $@ $^ -lm
+
+install:
+       $(INSTALL) -m 755 $(TARGET) $(INSTALL_MODULES_DIR)
+       mkdir -p $(INSTALL_SOUNDS)
+       $(INSTALL) -o root -g root -m 644 sounds/*.gsm $(INSTALL_SOUNDS)
+
+# config: all
+#      cp conf.conf /etc/asterisk/
+
+-include $(DEPS)
+
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..4c103ce
--- /dev/null
+++ b/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
+
+<This section is outdated>
+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/README.videoswitch b/README.videoswitch
new file mode 100644 (file)
index 0000000..2612194
--- /dev/null
@@ -0,0 +1,87 @@
+VideoSwitch
+-----------
+(c) Vipadia Limited 2005-2006
+Neil Stratford <neils@vipadia.com>
+
+Based on app_conference, see README.
+
+Including contributions from John Martin <John.Martin@AuPix.com>
+
+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 <videoswitch_name> [ on | off ]
+       enable debugging for a videoswitch
+
+fidwell*CLI> help videoswitch kick
+usage: videoswitch kick <videoswitch_name> <member no>
+       kick member form a videoswitch
+
+fidwell*CLI> help videoswitch list
+usage: videoswitch list {<videoswitch_name>}
+       list members of a videoswitch or list of videoswitches if no name
+
+fidwell*CLI> help videoswitch mute
+usage: videoswitch mute <videoswitch_name> <member no>
+       mute member in a videoswitch
+
+fidwell*CLI> help videoswitch unmute
+usage: videoswitch unmute <videoswitch_name> <member no>
+       unmute member in a videoswitch
+
+fidwell*CLI> help videoswitch mutechannel
+usage: videoswitch mute <videoswitch_name> <channel>
+       mute channel in a videoswitch
+
+fidwell*CLI> help videoswitch unmutechannel
+usage: videoswitch unmute <videoswitch_name> <channel>
+       unmute channel in a videoswitch
+
+fidwell*CLI> help videoswitch viewchannel
+usage: videoswitch viewchannel <videoswitch_name> <dest channel> <src channel>
+       channel <dest channel> will receive video stream <src channel>
+
+fidwell*CLI> help videoswitch viewstream
+usage: videoswitch viewstream <videoswitch_name> <member no> <stream no>
+       member <member no> will receive video stream <stream no>
+
+
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..121e83e
--- /dev/null
+++ b/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/app_conference.c b/app_conference.c
new file mode 100644 (file)
index 0000000..a8359d8
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * 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 <kapejod@ns1.jnetdns.de>
+ *
+ * Video Conferencing support added by
+ * Neil Stratford <neils@vipadia.com>
+ * Copyright (C) 2005, 2005 Vipadia Limited
+ *
+ * VAD driven video conferencing, text message support
+ * and miscellaneous enhancements added by
+ * Mihai Balea <mihai at hates dot ms>
+ *
+ * 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 ;
+}
+
+#define AST_MODULE "Conference"
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY,
+               "Channel Independent Conference Application");
+#undef AST_MODULE
+
diff --git a/app_conference.h b/app_conference.h
new file mode 100644 (file)
index 0000000..8ac8353
--- /dev/null
@@ -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 <kapejod@ns1.jnetdns.de>
+ *
+ * Video Conferencing support added by
+ * Neil Stratford <neils@vipadia.com>
+ * Copyright (C) 2005, 2005 Vipadia Limited
+ *
+ * VAD driven video conferencing, text message support
+ * and miscellaneous enhancements added by
+ * Mihai Balea <mihai at hates dot ms>
+ *
+ * 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 <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <stdio.h>
+
+
+#include <pthread.h>
+
+/* asterisk includes */
+#include <asterisk/utils.h>
+#include <asterisk/pbx.h>
+#include <asterisk/module.h>
+#include <asterisk/logger.h>
+#include <asterisk/lock.h>
+#include <asterisk/frame.h>
+#include <asterisk/manager.h>
+#include <asterisk/dsp.h>
+#include <asterisk/translate.h>
+#include <asterisk/channel.h>
+#include <asterisk/file.h>
+//#include <asterisk/channel_pvt.h>
+#include <asterisk/cli.h>
+
+
+#if (SILDET == 2)
+#include "libspeex/speex_preprocess.h"
+#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/app_conference.so b/app_conference.so
new file mode 100755 (executable)
index 0000000..d90bf5c
Binary files /dev/null and b/app_conference.so differ
diff --git a/cli.c b/cli.c
new file mode 100644 (file)
index 0000000..59eccb8
--- /dev/null
+++ b/cli.c
@@ -0,0 +1,1225 @@
+
+// $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 <kapejod@ns1.jnetdns.de>
+ *
+ * Video Conferencing support added by
+ * Neil Stratford <neils@vipadia.com>
+ * Copyright (C) 2005, 2005 Vipadia Limited
+ *
+ * VAD driven video conferencing, text message support
+ * and miscellaneous enhancements added by
+ * Mihai Balea <mihai at hates dot ms>
+ *
+ * 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 <conference_name> [ 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 {<conference_name>}\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 <conference> <member id>\n"
+       "       kick member <member id> from conference <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 <conference_name> <channel>\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_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 <conference_name> <member id>\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 <channel>\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 <conference_name> <member id> <stream no>\n"
+       "       member <member id> will receive video stream <stream no>\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 <conference_name> <dest channel> <src channel>\n"
+       "       channel <dest channel> will receive video stream <src channel>\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 < 4 )
+               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 <conference_name> <member id>\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 <channel>\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 <channel-id> <sound-file> [mute]\n"
+       "       play sound <sound-file> to conference member <channel-id>.\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 <channel-id>\n"
+       "       stop sounds for conference member <channel-id>.\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 <conference name>\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 <conference name> <member id>\n"
+       "       locks incoming video stream for conference <conference name> to member <member id>\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 <conference name> <channel>\n"
+       "       locks incoming video stream for conference <conference name> to channel <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 <conference name>\n"
+       "       unlocks conference <conference name>\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 <conference name> <member id>\n"
+       "       sets the default video source for conference <conference name> to member <member id>\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 <conference name> <channel>\n"
+       "       sets the default video source channel for conference <conference name> to channel <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 <conference name> <member id>\n"
+       "       mutes video from member <member id> in conference <conference name>\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 <conference name> <member id>\n"
+       "       unmutes video from member <member id> in conference <conference name>\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 <conference name> <channel>\n"
+       "       mutes video from channel <channel> in conference <conference name>\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 <conference name> <channel>\n"
+       "       unmutes video from channel <channel> in conference <conference name>\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 <conference name> <member id> <text>\n"
+       "        Sends text message <text> to member <member id> in conference <conference name>\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 <conference name> <channel> <text>\n"
+       "        Sends text message <text> to channel <channel> in conference <conference name>\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 <conference name> <text>\n"
+       "        Sends text message <text> to all members in conference <conference name>\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 <conference name> <source member> [destination member]\n"
+       "        Drives VAD video switching of <destination member> using audio from <source member> in conference <conference name>\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 <conference name> <source channel> [destination channel]\n"
+       "        Drives VAD video switching of <destination member> using audio from <source channel> in conference <conference channel>\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_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_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/cli.h b/cli.h
new file mode 100644 (file)
index 0000000..344ae1d
--- /dev/null
+++ b/cli.h
@@ -0,0 +1,97 @@
+
+// $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 <kapejod@ns1.jnetdns.de>
+ *
+ * Video Conferencing support added by
+ * Neil Stratford <neils@vipadia.com>
+ * Copyright (C) 2005, 2005 Vipadia Limited
+ *
+ * VAD driven video conferencing, text message support
+ * and miscellaneous enhancements added by
+ * Mihai Balea <mihai at hates dot ms>
+ *
+ * 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_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/common.h b/common.h
new file mode 100644 (file)
index 0000000..989398c
--- /dev/null
+++ b/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 <kapejod@ns1.jnetdns.de>
+ *
+ * Video Conferencing support added by
+ * Neil Stratford <neils@vipadia.com>
+ * Copyright (C) 2005, 2005 Vipadia Limited
+ *
+ * VAD driven video conferencing, text message support
+ * and miscellaneous enhancements added by
+ * Mihai Balea <mihai at hates dot ms>
+ *
+ * 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 <asterisk/time.h>
+
+// 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/conf_frame.h b/conf_frame.h
new file mode 100644 (file)
index 0000000..e73e57a
--- /dev/null
@@ -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 <kapejod@ns1.jnetdns.de>
+ *
+ * Video Conferencing support added by
+ * Neil Stratford <neils@vipadia.com>
+ * Copyright (C) 2005, 2005 Vipadia Limited
+ *
+ * VAD driven video conferencing, text message support
+ * and miscellaneous enhancements added by
+ * Mihai Balea <mihai at hates dot ms>
+ *
+ * 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/conference.c b/conference.c
new file mode 100644 (file)
index 0000000..b8a4a7d
--- /dev/null
@@ -0,0 +1,2886 @@
+
+// $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 <kapejod@ns1.jnetdns.de>
+ *
+ * Video Conferencing support added by
+ * Neil Stratford <neils@vipadia.com>
+ * Copyright (C) 2005, 2005 Vipadia Limited
+ *
+ * VAD driven video conferencing, text message support
+ * and miscellaneous enhancements added by
+ * Mihai Balea <mihai at hates dot ms>
+ *
+ * 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( &notify, 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
+
+                       // 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 play_sound_channel(int fd, const char *channel, const char *file, int mute)
+{
+       struct ast_conf_member *member;
+       struct ast_conf_soundq *newsound;
+       struct ast_conf_soundq **q;
+
+       member = find_member(channel, 1);
+       if( !member )
+       {
+               ast_cli(fd, "Member %s not found\n", channel);
+               return 0;
+       }
+
+       newsound = calloc(1, sizeof(struct ast_conf_soundq));
+       newsound->stream = ast_openstream(member->chan, file, member->chan->language);
+       if( !newsound->stream )
+       {
+               ast_cli(fd, "Sound %s not found\n", file);
+               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);
+
+       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/conference.h b/conference.h
new file mode 100644 (file)
index 0000000..d028e7d
--- /dev/null
@@ -0,0 +1,193 @@
+
+// $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 <kapejod@ns1.jnetdns.de>
+ *
+ * Video Conferencing support added by
+ * Neil Stratford <neils@vipadia.com>
+ * Copyright (C) 2005, 2005 Vipadia Limited
+ *
+ * VAD driven video conferencing, text message support
+ * and miscellaneous enhancements added by
+ * Mihai Balea <mihai at hates dot ms>
+ *
+ * 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 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/debian/README.Debian b/debian/README.Debian
new file mode 100644 (file)
index 0000000..74e0bd6
--- /dev/null
@@ -0,0 +1,6 @@
+The Debian Package asterisk-app-conference
+----------------------------
+
+Comments regarding the Package... heu... none at this time...
+
+ -- Thomas Noel <thomas.noel@auf.org>  Tue, 18 Sep 2007 19:42:58 +0000
diff --git a/debian/asterisk-app-conference-default.ex b/debian/asterisk-app-conference-default.ex
new file mode 100644 (file)
index 0000000..37adb41
--- /dev/null
@@ -0,0 +1,10 @@
+# Defaults for asterisk-app-conference initscript
+# sourced by /etc/init.d/asterisk-app-conference
+# installed at /etc/default/asterisk-app-conference by the maintainer scripts
+
+#
+# This is a POSIX shell fragment
+#
+
+# Additional options that are passed to the Daemon.
+DAEMON_OPTS=""
diff --git a/debian/asterisk-app-conference.doc-base.EX b/debian/asterisk-app-conference.doc-base.EX
new file mode 100644 (file)
index 0000000..112ab07
--- /dev/null
@@ -0,0 +1,22 @@
+Document: asterisk-app-conference
+Title: Debian asterisk-app-conference Manual
+Author: <insert document author here>
+Abstract: This manual describes what asterisk-app-conference is
+ and how it can be used to
+ manage online manuals on Debian systems.
+Section: unknown
+
+Format: debiandoc-sgml
+Files: /usr/share/doc/asterisk-app-conference/asterisk-app-conference.sgml.gz
+
+Format: postscript
+Files: /usr/share/doc/asterisk-app-conference/asterisk-app-conference.ps.gz
+
+Format: text
+Files: /usr/share/doc/asterisk-app-conference/asterisk-app-conference.text.gz
+
+Format: HTML
+Index: /usr/share/doc/asterisk-app-conference/html/index.html
+Files: /usr/share/doc/asterisk-app-conference/html/*.html
+
+  
diff --git a/debian/changelog b/debian/changelog
new file mode 100644 (file)
index 0000000..f5828f4
--- /dev/null
@@ -0,0 +1,38 @@
+asterisk-app-conference (2.0.1-1) stable; urgency=low
+
+  * sortie de la 2.0.1 (correction du plus petit bug du monde)
+
+ -- Thomas Noel <thomas.noel@auf.org>  Tue, 26 Feb 2008 18:30:24 +0000
+
+asterisk-app-conference (2.0-2) stable; urgency=low
+
+  * allez, zou : stable !
+
+ -- Thomas Noel <thomas.noel@auf.org>  Tue, 19 Feb 2008 16:02:10 +0000
+
+asterisk-app-conference (2.0-1) unstable; urgency=low
+
+  * members.c : PATCH annonce les nouveaux arrivants et les departs
+  * conference.c: PATCH appel de ast_openstream avec argument "language"
+  * sounds/*gsm : sons associes a ces annonces (arrivee/depart)
+  * Makefile : ajout regle installation des sons
+
+ -- Thomas Noel <thomas.noel@auf.org>  Tue, 19 Feb 2008 15:44:20 +0000
+
+asterisk-app-conference (2.0) unstable; urgency=low
+
+  * version 2.0 (aucun changement depuis 2.0rc1)
+
+ -- Thomas Noel <thomas.noel@auf.org>  Thu,  7 Feb 2008 21:47:30 +0000
+
+asterisk-app-conference (0+svn939) unstable; urgency=low
+
+  * 2.0rc1 (svn 939)
+
+ -- Thomas Noel <thomas.noel@auf.org>  Mon, 14 Jan 2008 15:36:49 +0000
+
+asterisk-app-conference (0+svn898) unstable; urgency=low
+
+  * Initial Release.
+
+ -- Thomas Noel <thomas.noel@auf.org>  Tue, 18 Sep 2007 19:42:58 +0000
diff --git a/debian/compat b/debian/compat
new file mode 100644 (file)
index 0000000..7ed6ff8
--- /dev/null
@@ -0,0 +1 @@
+5
diff --git a/debian/control b/debian/control
new file mode 100644 (file)
index 0000000..26d905e
--- /dev/null
@@ -0,0 +1,14 @@
+Source: asterisk-app-conference
+Section: comm
+Priority: extra
+Maintainer: Thomas Noel <thomas.noel@auf.org>
+Build-Depends: debhelper (>= 5), asterisk-dev
+Standards-Version: 3.7.2
+
+Package: asterisk-app-conference
+Architecture: any
+Depends: asterisk, ${shlibs:Depends}, ${misc:Depends}
+Description: Asterisk voice/video conferencing plugin
+ AppConference is a high-performance Asterisk voice/video
+ conferencing plugin.
+ See : http://sourceforge.net/projects/appconference
diff --git a/debian/copyright b/debian/copyright
new file mode 100644 (file)
index 0000000..2289ee0
--- /dev/null
@@ -0,0 +1,26 @@
+This is asterisk-app-conference, written and maintained by Thomas Noel <thomas.noel@auf.org>
+on Tue, 18 Sep 2007 19:42:58 +0000.
+
+The original source can always be found at:
+       ftp://ftp.debian.org/dists/unstable/main/source/
+
+Copyright Holder:  Thomas Noel
+
+License:
+
+  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 package; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+
+On Debian systems, the complete text of the GNU General
+Public License can be found in `/usr/share/common-licenses/GPL'.
diff --git a/debian/cron.d.ex b/debian/cron.d.ex
new file mode 100644 (file)
index 0000000..2c27023
--- /dev/null
@@ -0,0 +1,4 @@
+#
+# Regular cron jobs for the asterisk-app-conference package
+#
+0 4    * * *   root    asterisk-app-conference_maintenance
diff --git a/debian/dirs b/debian/dirs
new file mode 100644 (file)
index 0000000..c1f4319
--- /dev/null
@@ -0,0 +1 @@
+usr/lib/asterisk/modules
diff --git a/debian/docs b/debian/docs
new file mode 100644 (file)
index 0000000..52310b8
--- /dev/null
@@ -0,0 +1,5 @@
+CLI.txt
+Flags.txt
+README
+README.videoswitch
+TODO
diff --git a/debian/emacsen-install.ex b/debian/emacsen-install.ex
new file mode 100644 (file)
index 0000000..64e5da2
--- /dev/null
@@ -0,0 +1,45 @@
+#! /bin/sh -e
+# /usr/lib/emacsen-common/packages/install/asterisk-app-conference
+
+# Written by Jim Van Zandt <jrv@debian.org>, borrowing heavily
+# from the install scripts for gettext by Santiago Vila
+# <sanvila@ctv.es> and octave by Dirk Eddelbuettel <edd@debian.org>.
+
+FLAVOR=$1
+PACKAGE=asterisk-app-conference
+
+if [ ${FLAVOR} = emacs ]; then exit 0; fi
+
+echo install/${PACKAGE}: Handling install for emacsen flavor ${FLAVOR}
+
+#FLAVORTEST=`echo $FLAVOR | cut -c-6`
+#if [ ${FLAVORTEST} = xemacs ] ; then
+#    SITEFLAG="-no-site-file"
+#else
+#    SITEFLAG="--no-site-file"
+#fi
+FLAGS="${SITEFLAG} -q -batch -l path.el -f batch-byte-compile"
+
+ELDIR=/usr/share/emacs/site-lisp/${PACKAGE}
+ELCDIR=/usr/share/${FLAVOR}/site-lisp/${PACKAGE}
+
+# Install-info-altdir does not actually exist. 
+# Maybe somebody will write it.
+if test -x /usr/sbin/install-info-altdir; then
+    echo install/${PACKAGE}: install Info links for ${FLAVOR}
+    install-info-altdir --quiet --section "" "" --dirname=${FLAVOR} /usr/info/${PACKAGE}.info.gz
+fi
+
+install -m 755 -d ${ELCDIR}
+cd ${ELDIR}
+FILES=`echo *.el`
+cp ${FILES} ${ELCDIR}
+cd ${ELCDIR}
+
+cat << EOF > path.el
+(setq load-path (cons "." load-path) byte-compile-warnings nil)
+EOF
+${FLAVOR} ${FLAGS} ${FILES}
+rm -f *.el path.el
+
+exit 0
diff --git a/debian/emacsen-remove.ex b/debian/emacsen-remove.ex
new file mode 100644 (file)
index 0000000..ac8cd40
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/sh -e
+# /usr/lib/emacsen-common/packages/remove/asterisk-app-conference
+
+FLAVOR=$1
+PACKAGE=asterisk-app-conference
+
+if [ ${FLAVOR} != emacs ]; then
+    if test -x /usr/sbin/install-info-altdir; then
+        echo remove/${PACKAGE}: removing Info links for ${FLAVOR}
+        install-info-altdir --quiet --remove --dirname=${FLAVOR} /usr/info/asterisk-app-conference.info.gz
+    fi
+
+    echo remove/${PACKAGE}: purging byte-compiled files for ${FLAVOR}
+    rm -rf /usr/share/${FLAVOR}/site-lisp/${PACKAGE}
+fi
diff --git a/debian/emacsen-startup.ex b/debian/emacsen-startup.ex
new file mode 100644 (file)
index 0000000..605ef58
--- /dev/null
@@ -0,0 +1,25 @@
+;; -*-emacs-lisp-*-
+;;
+;; Emacs startup file, e.g.  /etc/emacs/site-start.d/50asterisk-app-conference.el
+;; for the Debian asterisk-app-conference package
+;;
+;; Originally contributed by Nils Naumann <naumann@unileoben.ac.at>
+;; Modified by Dirk Eddelbuettel <edd@debian.org>
+;; Adapted for dh-make by Jim Van Zandt <jrv@debian.org>
+
+;; The asterisk-app-conference package follows the Debian/GNU Linux 'emacsen' policy and
+;; byte-compiles its elisp files for each 'emacs flavor' (emacs19,
+;; xemacs19, emacs20, xemacs20...).  The compiled code is then
+;; installed in a subdirectory of the respective site-lisp directory.
+;; We have to add this to the load-path:
+(let ((package-dir (concat "/usr/share/"
+                           (symbol-name flavor)
+                           "/site-lisp/asterisk-app-conference")))
+;; If package-dir does not exist, the asterisk-app-conference package must have
+;; removed but not purged, and we should skip the setup.
+  (when (file-directory-p package-dir)
+        (setq load-path (cons package-dir load-path))
+       (autoload 'asterisk-app-conference-mode "asterisk-app-conference-mode"
+         "Major mode for editing asterisk-app-conference files." t)
+       (add-to-list 'auto-mode-alist '("\\.asterisk-app-conference$" . asterisk-app-conference-mode))))
+
diff --git a/debian/init.d.ex b/debian/init.d.ex
new file mode 100644 (file)
index 0000000..ff9dfa5
--- /dev/null
@@ -0,0 +1,81 @@
+#! /bin/sh
+#
+# skeleton     example file to build /etc/init.d/ scripts.
+#              This file should be used to construct scripts for /etc/init.d.
+#
+#              Written by Miquel van Smoorenburg <miquels@cistron.nl>.
+#              Modified for Debian 
+#              by Ian Murdock <imurdock@gnu.ai.mit.edu>.
+#
+# Version:     @(#)skeleton  1.9  26-Feb-2001  miquels@cistron.nl
+#
+
+PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
+DAEMON=/usr/sbin/asterisk-app-conference
+NAME=asterisk-app-conference
+DESC=asterisk-app-conference
+
+test -x $DAEMON || exit 0
+
+# Include asterisk-app-conference defaults if available
+if [ -f /etc/default/asterisk-app-conference ] ; then
+       . /etc/default/asterisk-app-conference
+fi
+
+set -e
+
+case "$1" in
+  start)
+       echo -n "Starting $DESC: "
+       start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \
+               --exec $DAEMON -- $DAEMON_OPTS
+       echo "$NAME."
+       ;;
+  stop)
+       echo -n "Stopping $DESC: "
+       start-stop-daemon --stop --quiet --pidfile /var/run/$NAME.pid \
+               --exec $DAEMON
+       echo "$NAME."
+       ;;
+  #reload)
+       #
+       #       If the daemon can reload its config files on the fly
+       #       for example by sending it SIGHUP, do it here.
+       #
+       #       If the daemon responds to changes in its config file
+       #       directly anyway, make this a do-nothing entry.
+       #
+       # echo "Reloading $DESC configuration files."
+       # start-stop-daemon --stop --signal 1 --quiet --pidfile \
+       #       /var/run/$NAME.pid --exec $DAEMON
+  #;;
+  force-reload)
+       #
+       #       If the "reload" option is implemented, move the "force-reload"
+       #       option to the "reload" entry above. If not, "force-reload" is
+       #       just the same as "restart" except that it does nothing if the
+       #   daemon isn't already running.
+       # check wether $DAEMON is running. If so, restart
+       start-stop-daemon --stop --test --quiet --pidfile \
+               /var/run/$NAME.pid --exec $DAEMON \
+       && $0 restart \
+       || exit 0
+       ;;
+  restart)
+    echo -n "Restarting $DESC: "
+       start-stop-daemon --stop --quiet --pidfile \
+               /var/run/$NAME.pid --exec $DAEMON
+       sleep 1
+       start-stop-daemon --start --quiet --pidfile \
+               /var/run/$NAME.pid --exec $DAEMON -- $DAEMON_OPTS
+       echo "$NAME."
+       ;;
+  *)
+       N=/etc/init.d/$NAME
+       # echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2
+       echo "Usage: $N {start|stop|restart|force-reload}" >&2
+       exit 1
+       ;;
+esac
+
+exit 0
diff --git a/debian/manpage.1.ex b/debian/manpage.1.ex
new file mode 100644 (file)
index 0000000..70dfa40
--- /dev/null
@@ -0,0 +1,59 @@
+.\"                                      Hey, EMACS: -*- nroff -*-
+.\" First parameter, NAME, should be all caps
+.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
+.\" other parameters are allowed: see man(7), man(1)
+.TH ASTERISK-APP-CONFERENCE SECTION "septembre 18, 2007"
+.\" Please adjust this date whenever revising the manpage.
+.\"
+.\" Some roff macros, for reference:
+.\" .nh        disable hyphenation
+.\" .hy        enable hyphenation
+.\" .ad l      left justify
+.\" .ad b      justify to both left and right margins
+.\" .nf        disable filling
+.\" .fi        enable filling
+.\" .br        insert line break
+.\" .sp <n>    insert n+1 empty lines
+.\" for manpage-specific macros, see man(7)
+.SH NAME
+asterisk-app-conference \- program to do something
+.SH SYNOPSIS
+.B asterisk-app-conference
+.RI [ options ] " files" ...
+.br
+.B bar
+.RI [ options ] " files" ...
+.SH DESCRIPTION
+This manual page documents briefly the
+.B asterisk-app-conference
+and
+.B bar
+commands.
+.PP
+.\" TeX users may be more comfortable with the \fB<whatever>\fP and
+.\" \fI<whatever>\fP escape sequences to invode bold face and italics, 
+.\" respectively.
+\fBasterisk-app-conference\fP is a program that...
+.SH OPTIONS
+These programs follow the usual GNU command line syntax, with long
+options starting with two dashes (`-').
+A summary of options is included below.
+For a complete description, see the Info files.
+.TP
+.B \-h, \-\-help
+Show summary of options.
+.TP
+.B \-v, \-\-version
+Show version of program.
+.SH SEE ALSO
+.BR bar (1),
+.BR baz (1).
+.br
+The programs are documented fully by
+.IR "The Rise and Fall of a Fooish Bar" ,
+available via the Info system.
+.SH AUTHOR
+asterisk-app-conference was written by <upstream author>.
+.PP
+This manual page was written by Thomas Noel <thomas.noel@auf.org>,
+for the Debian project (but may be used by others).
diff --git a/debian/manpage.sgml.ex b/debian/manpage.sgml.ex
new file mode 100644 (file)
index 0000000..465506e
--- /dev/null
@@ -0,0 +1,156 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [
+
+<!-- Process this file with docbook-to-man to generate an nroff manual
+     page: `docbook-to-man manpage.sgml > manpage.1'.  You may view
+     the manual page with: `docbook-to-man manpage.sgml | nroff -man |
+     less'.  A typical entry in a Makefile or Makefile.am is:
+
+manpage.1: manpage.sgml
+       docbook-to-man $< > $@
+
+    
+       The docbook-to-man binary is found in the docbook-to-man package.
+       Please remember that if you create the nroff version in one of the
+       debian/rules file targets (such as build), you will need to include
+       docbook-to-man in your Build-Depends control field.
+
+  -->
+
+  <!-- Fill in your name for FIRSTNAME and SURNAME. -->
+  <!ENTITY dhfirstname "<firstname>FIRSTNAME</firstname>">
+  <!ENTITY dhsurname   "<surname>SURNAME</surname>">
+  <!-- Please adjust the date whenever revising the manpage. -->
+  <!ENTITY dhdate      "<date>septembre 18, 2007</date>">
+  <!-- SECTION should be 1-8, maybe w/ subsection other parameters are
+       allowed: see man(7), man(1). -->
+  <!ENTITY dhsection   "<manvolnum>SECTION</manvolnum>">
+  <!ENTITY dhemail     "<email>thomas.noel@auf.org</email>">
+  <!ENTITY dhusername  "Thomas Noel">
+  <!ENTITY dhucpackage "<refentrytitle>ASTERISK-APP-CONFERENCE</refentrytitle>">
+  <!ENTITY dhpackage   "asterisk-app-conference">
+
+  <!ENTITY debian      "<productname>Debian</productname>">
+  <!ENTITY gnu         "<acronym>GNU</acronym>">
+  <!ENTITY gpl         "&gnu; <acronym>GPL</acronym>">
+]>
+
+<refentry>
+  <refentryinfo>
+    <address>
+      &dhemail;
+    </address>
+    <author>
+      &dhfirstname;
+      &dhsurname;
+    </author>
+    <copyright>
+      <year>2003</year>
+      <holder>&dhusername;</holder>
+    </copyright>
+    &dhdate;
+  </refentryinfo>
+  <refmeta>
+    &dhucpackage;
+
+    &dhsection;
+  </refmeta>
+  <refnamediv>
+    <refname>&dhpackage;</refname>
+
+    <refpurpose>program to do something</refpurpose>
+  </refnamediv>
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>&dhpackage;</command>
+
+      <arg><option>-e <replaceable>this</replaceable></option></arg>
+
+      <arg><option>--example <replaceable>that</replaceable></option></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+  <refsect1>
+    <title>DESCRIPTION</title>
+
+    <para>This manual page documents briefly the
+      <command>&dhpackage;</command> and <command>bar</command>
+      commands.</para>
+
+    <para>This manual page was written for the &debian; distribution
+      because the original program does not have a manual page.
+      Instead, it has documentation in the &gnu;
+      <application>Info</application> format; see below.</para>
+
+    <para><command>&dhpackage;</command> is a program that...</para>
+
+  </refsect1>
+  <refsect1>
+    <title>OPTIONS</title>
+
+    <para>These programs follow the usual &gnu; command line syntax,
+      with long options starting with two dashes (`-').  A summary of
+      options is included below.  For a complete description, see the
+      <application>Info</application> files.</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><option>-h</option>
+          <option>--help</option>
+        </term>
+        <listitem>
+          <para>Show summary of options.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><option>-v</option>
+          <option>--version</option>
+        </term>
+        <listitem>
+          <para>Show version of program.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+  <refsect1>
+    <title>SEE ALSO</title>
+
+    <para>bar (1), baz (1).</para>
+
+    <para>The programs are documented fully by <citetitle>The Rise and
+      Fall of a Fooish Bar</citetitle> available via the
+      <application>Info</application> system.</para>
+  </refsect1>
+  <refsect1>
+    <title>AUTHOR</title>
+
+    <para>This manual page was written by &dhusername; &dhemail; for
+      the &debian; system (but may be used by others).  Permission is
+      granted to copy, distribute and/or modify this document under
+      the terms of the &gnu; General Public License, Version 2 any 
+         later version published by the Free Software Foundation.
+    </para>
+       <para>
+         On Debian systems, the complete text of the GNU General Public
+         License can be found in /usr/share/common-licenses/GPL.
+       </para>
+
+  </refsect1>
+</refentry>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:2
+sgml-indent-data:t
+sgml-parent-document:nil
+sgml-default-dtd-file:nil
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+-->
+
+
diff --git a/debian/manpage.xml.ex b/debian/manpage.xml.ex
new file mode 100644 (file)
index 0000000..6d39779
--- /dev/null
@@ -0,0 +1,148 @@
+<?xml version='1.0' encoding='ISO-8859-1'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
+
+<!--
+
+Process this file with an XSLT processor: `xsltproc \
+-''-nonet /usr/share/sgml/docbook/stylesheet/xsl/nwalsh/\
+manpages/docbook.xsl manpage.dbk'.  A manual page
+<package>.<section> will be generated.  You may view the
+manual page with: nroff -man <package>.<section> | less'.  A
+typical entry in a Makefile or Makefile.am is:
+
+DB2MAN=/usr/share/sgml/docbook/stylesheet/xsl/nwalsh/\
+manpages/docbook.xsl
+XP=xsltproc -''-nonet
+
+manpage.1: manpage.dbk
+        $(XP) $(DB2MAN) $<
+    
+The xsltproc binary is found in the xsltproc package.  The
+XSL files are in docbook-xsl.  Please remember that if you
+create the nroff version in one of the debian/rules file
+targets (such as build), you will need to include xsltproc
+and docbook-xsl in your Build-Depends control field.
+
+-->
+
+  <!-- Fill in your name for FIRSTNAME and SURNAME. -->
+  <!ENTITY dhfirstname "<firstname>FIRSTNAME</firstname>">
+  <!ENTITY dhsurname   "<surname>SURNAME</surname>">
+  <!-- Please adjust the date whenever revising the manpage. -->
+  <!ENTITY dhdate      "<date>septembre 18, 2007</date>">
+  <!-- SECTION should be 1-8, maybe w/ subsection other parameters are
+       allowed: see man(7), man(1). -->
+  <!ENTITY dhsection   "<manvolnum>SECTION</manvolnum>">
+  <!ENTITY dhemail     "<email>thomas.noel@auf.org</email>">
+  <!ENTITY dhusername  "Thomas Noel">
+  <!ENTITY dhucpackage "<refentrytitle>ASTERISK-APP-CONFERENCE</refentrytitle>">
+  <!ENTITY dhpackage   "asterisk-app-conference">
+
+  <!ENTITY debian      "<productname>Debian</productname>">
+  <!ENTITY gnu         "<acronym>GNU</acronym>">
+  <!ENTITY gpl         "&gnu; <acronym>GPL</acronym>">
+]>
+
+<refentry>
+  <refentryinfo>
+    <address>
+      &dhemail;
+    </address>
+    <author>
+      &dhfirstname;
+      &dhsurname;
+    </author>
+    <copyright>
+      <year>2003</year>
+      <holder>&dhusername;</holder>
+    </copyright>
+    &dhdate;
+  </refentryinfo>
+  <refmeta>
+    &dhucpackage;
+
+    &dhsection;
+  </refmeta>
+  <refnamediv>
+    <refname>&dhpackage;</refname>
+
+    <refpurpose>program to do something</refpurpose>
+  </refnamediv>
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>&dhpackage;</command>
+
+      <arg><option>-e <replaceable>this</replaceable></option></arg>
+
+      <arg><option>--example <replaceable>that</replaceable></option></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+  <refsect1>
+    <title>DESCRIPTION</title>
+
+    <para>This manual page documents briefly the
+      <command>&dhpackage;</command> and <command>bar</command>
+      commands.</para>
+
+    <para>This manual page was written for the &debian; distribution
+      because the original program does not have a manual page.
+      Instead, it has documentation in the &gnu;
+      <application>Info</application> format; see below.</para>
+
+    <para><command>&dhpackage;</command> is a program that...</para>
+
+  </refsect1>
+  <refsect1>
+    <title>OPTIONS</title>
+
+    <para>These programs follow the usual &gnu; command line syntax,
+      with long options starting with two dashes (`-').  A summary of
+      options is included below.  For a complete description, see the
+      <application>Info</application> files.</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><option>-h</option>
+          <option>--help</option>
+        </term>
+        <listitem>
+          <para>Show summary of options.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><option>-v</option>
+          <option>--version</option>
+        </term>
+        <listitem>
+          <para>Show version of program.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+  <refsect1>
+    <title>SEE ALSO</title>
+
+    <para>bar (1), baz (1).</para>
+
+    <para>The programs are documented fully by <citetitle>The Rise and
+      Fall of a Fooish Bar</citetitle> available via the
+      <application>Info</application> system.</para>
+  </refsect1>
+  <refsect1>
+    <title>AUTHOR</title>
+
+    <para>This manual page was written by &dhusername; &dhemail; for
+      the &debian; system (but may be used by others).  Permission is
+      granted to copy, distribute and/or modify this document under
+      the terms of the &gnu; General Public License, Version 2 any 
+         later version published by the Free Software Foundation.
+    </para>
+       <para>
+         On Debian systems, the complete text of the GNU General Public
+         License can be found in /usr/share/common-licenses/GPL.
+       </para>
+
+  </refsect1>
+</refentry>
+
diff --git a/debian/menu.ex b/debian/menu.ex
new file mode 100644 (file)
index 0000000..74f5ef1
--- /dev/null
@@ -0,0 +1,2 @@
+?package(asterisk-app-conference):needs="X11|text|vc|wm" section="Apps/see-menu-manual"\
+  title="asterisk-app-conference" command="/usr/bin/asterisk-app-conference"
diff --git a/debian/postinst.ex b/debian/postinst.ex
new file mode 100644 (file)
index 0000000..39e785e
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/sh
+# postinst script for asterisk-app-conference
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#        * <postinst> `configure' <most-recently-configured-version>
+#        * <old-postinst> `abort-upgrade' <new version>
+#        * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+#          <new-version>
+#        * <postinst> `abort-remove'
+#        * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+#          <failed-install-package> <version> `removing'
+#          <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+    configure)
+    ;;
+
+    abort-upgrade|abort-remove|abort-deconfigure)
+    ;;
+
+    *)
+        echo "postinst called with unknown argument \`$1'" >&2
+        exit 1
+    ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/debian/postrm.ex b/debian/postrm.ex
new file mode 100644 (file)
index 0000000..6b999ca
--- /dev/null
@@ -0,0 +1,39 @@
+#!/bin/sh
+# postrm script for asterisk-app-conference
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#        * <postrm> `remove'
+#        * <postrm> `purge'
+#        * <old-postrm> `upgrade' <new-version>
+#        * <new-postrm> `failed-upgrade' <old-version>
+#        * <new-postrm> `abort-install'
+#        * <new-postrm> `abort-install' <old-version>
+#        * <new-postrm> `abort-upgrade' <old-version>
+#        * <disappearer's-postrm> `disappear' <overwriter>
+#          <overwriter-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+    purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+    ;;
+
+    *)
+        echo "postrm called with unknown argument \`$1'" >&2
+        exit 1
+    ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/debian/preinst.ex b/debian/preinst.ex
new file mode 100644 (file)
index 0000000..1ef6698
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/sh
+# preinst script for asterisk-app-conference
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#        * <new-preinst> `install'
+#        * <new-preinst> `install' <old-version>
+#        * <new-preinst> `upgrade' <old-version>
+#        * <old-preinst> `abort-upgrade' <new-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+    install|upgrade)
+    ;;
+
+    abort-upgrade)
+    ;;
+
+    *)
+        echo "preinst called with unknown argument \`$1'" >&2
+        exit 1
+    ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/debian/prerm.ex b/debian/prerm.ex
new file mode 100644 (file)
index 0000000..290cc78
--- /dev/null
@@ -0,0 +1,40 @@
+#!/bin/sh
+# prerm script for asterisk-app-conference
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#        * <prerm> `remove'
+#        * <old-prerm> `upgrade' <new-version>
+#        * <new-prerm> `failed-upgrade' <old-version>
+#        * <conflictor's-prerm> `remove' `in-favour' <package> <new-version>
+#        * <deconfigured's-prerm> `deconfigure' `in-favour'
+#          <package-being-installed> <version> `removing'
+#          <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+    remove|upgrade|deconfigure)
+    ;;
+
+    failed-upgrade)
+    ;;
+
+    *)
+        echo "prerm called with unknown argument \`$1'" >&2
+        exit 1
+    ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/debian/rules b/debian/rules
new file mode 100755 (executable)
index 0000000..69b5305
--- /dev/null
@@ -0,0 +1,99 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+# Sample debian/rules that uses debhelper.
+# This file was originally written by Joey Hess and Craig Small.
+# As a special exception, when this file is copied by dh-make into a
+# dh-make output file, you may use that output file without restriction.
+# This special exception was added by Craig Small in version 0.37 of dh-make.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+
+
+
+CFLAGS = -Wall -g
+
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+       CFLAGS += -O0
+else
+       CFLAGS += -O2
+endif
+
+configure: configure-stamp
+configure-stamp:
+       dh_testdir
+       # Add here commands to configure the package.
+
+       touch configure-stamp
+
+
+build: build-stamp
+
+build-stamp: configure-stamp 
+       dh_testdir
+
+       # Add here commands to compile the package.
+       ASTERISK_INCLUDE_DIR= $(MAKE)
+
+       #docbook-to-man debian/asterisk-app-conference.sgml > asterisk-app-conference.1
+
+       touch $@
+
+clean:
+       dh_testdir
+       dh_testroot
+       rm -f build-stamp configure-stamp
+
+       # Add here commands to clean up after the build process.
+       -$(MAKE) clean
+
+       dh_clean 
+
+install: build
+       dh_testdir
+       dh_testroot
+       dh_clean -k 
+       dh_installdirs
+
+       # Add here commands to install the package into debian/asterisk-app-conference.
+       $(MAKE) DESTDIR=$(CURDIR)/debian/asterisk-app-conference install
+
+
+# Build architecture-independent files here.
+binary-indep: build install
+# We have nothing to do by default.
+
+# Build architecture-dependent files here.
+binary-arch: build install
+       dh_testdir
+       dh_testroot
+       dh_installchangelogs 
+       dh_installdocs
+       dh_installexamples
+#      dh_install
+#      dh_installmenu
+#      dh_installdebconf       
+#      dh_installlogrotate
+#      dh_installemacsen
+#      dh_installpam
+#      dh_installmime
+#      dh_python
+#      dh_installinit
+#      dh_installcron
+#      dh_installinfo
+       dh_installman
+       dh_link
+       dh_strip
+       dh_compress
+       dh_fixperms
+#      dh_perl
+#      dh_makeshlibs
+       dh_installdeb
+       dh_shlibdeps
+       dh_gencontrol
+       dh_md5sums
+       dh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install configure
diff --git a/debian/watch.ex b/debian/watch.ex
new file mode 100644 (file)
index 0000000..1727b70
--- /dev/null
@@ -0,0 +1,22 @@
+# Example watch control file for uscan
+# Rename this file to "watch" and then you can run the "uscan" command
+# to check for upstream updates and more.
+# See uscan(1) for format
+
+# Compulsory line, this is a version 3 file
+version=3
+
+# Uncomment to examine a Webpage 
+# <Webpage URL> <string match>
+#http://www.example.com/downloads.php asterisk-app-conference-(.*)\.tar\.gz
+
+# Uncomment to examine a Webserver directory
+#http://www.example.com/pub/asterisk-app-conference-(.*)\.tar\.gz
+
+# Uncommment to examine a FTP server
+#ftp://ftp.example.com/pub/asterisk-app-conference-(.*)\.tar\.gz debian uupdate
+
+# Uncomment to find new files on sourceforge, for debscripts >= 2.9
+# http://sf.net/asterisk-app-conference/asterisk-app-conference-(.*)\.tar\.gz
+
+
diff --git a/frame.c b/frame.c
new file mode 100644 (file)
index 0000000..24e04bd
--- /dev/null
+++ b/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 <kapejod@ns1.jnetdns.de>
+ *
+ * Video Conferencing support added by
+ * Neil Stratford <neils@vipadia.com>
+ * Copyright (C) 2005, 2005 Vipadia Limited
+ *
+ * VAD driven video conferencing, text message support
+ * and miscellaneous enhancements added by
+ * Mihai Balea <mihai at hates dot ms>
+ *
+ * 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/frame.h b/frame.h
new file mode 100644 (file)
index 0000000..2b77805
--- /dev/null
+++ b/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 <kapejod@ns1.jnetdns.de>
+ *
+ * Video Conferencing support added by
+ * Neil Stratford <neils@vipadia.com>
+ * Copyright (C) 2005, 2005 Vipadia Limited
+ *
+ * VAD driven video conferencing, text message support
+ * and miscellaneous enhancements added by
+ * Mihai Balea <mihai at hates dot ms>
+ *
+ * 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/libspeex/misc.c b/libspeex/misc.c
new file mode 100644 (file)
index 0000000..5572809
--- /dev/null
@@ -0,0 +1,145 @@
+/* Copyright (C) 2002 Jean-Marc Valin 
+   File: mics.c
+   Various utility routines for Speex
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+   
+   - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   
+   - Neither the name of the Xiph.org Foundation nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+   
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "misc.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifndef RELEASE
+void print_vec(float *vec, int len, char *name)
+{
+   int i;
+   printf ("%s ", name);
+   for (i=0;i<len;i++)
+      printf (" %f", vec[i]);
+   printf ("\n");
+}
+#endif
+
+unsigned int be_int(unsigned int i)
+{
+   unsigned int ret=i;
+#ifndef WORDS_BIGENDIAN
+   ret =  i>>24;
+   ret += (i>>8)&0x0000ff00;
+   ret += (i<<8)&0x00ff0000;
+   ret += (i<<24);
+#endif
+   return ret;
+}
+
+unsigned int le_int(unsigned int i)
+{
+   unsigned int ret=i;
+#ifdef WORDS_BIGENDIAN
+   ret =  i>>24;
+   ret += (i>>8)&0x0000ff00;
+   ret += (i<<8)&0x00ff0000;
+   ret += (i<<24);
+#endif
+   return ret;
+}
+
+unsigned short be_short(unsigned short s)
+{
+   unsigned short ret=s;
+#ifndef WORDS_BIGENDIAN
+   ret =  s>>8;
+   ret += s<<8;
+#endif
+   return ret;
+}
+
+unsigned short le_short(unsigned short s)
+{
+   unsigned short ret=s;
+#ifdef WORDS_BIGENDIAN
+   ret =  s>>8;
+   ret += s<<8;
+#endif
+   return ret;
+}
+
+void *speex_alloc (int size)
+{
+   return calloc(size,1);
+}
+
+void *speex_realloc (void *ptr, int size)
+{
+   return realloc(ptr, size);
+}
+
+void speex_free (void *ptr)
+{
+   free(ptr);
+}
+
+void *speex_move (void *dest, void *src, int n)
+{
+   return memmove(dest,src,n);
+}
+
+void speex_error(char *str)
+{
+   fprintf (stderr, "Fatal error: %s\n", str);
+   exit(1);
+}
+
+void speex_warning(char *str)
+{
+   fprintf (stderr, "warning: %s\n", str);
+}
+
+void speex_warning_int(char *str, int val)
+{
+   fprintf (stderr, "warning: %s %d\n", str, val);
+}
+
+void speex_rand_vec(float std, spx_sig_t *data, int len)
+{
+   int i;
+   for (i=0;i<len;i++)
+      data[i]+=SIG_SCALING*3*std*((((float)rand())/RAND_MAX)-.5);
+}
+
+float speex_rand(float std)
+{
+   return 3*std*((((float)rand())/RAND_MAX)-.5);
+}
+
+void _speex_putc(int ch, void *file)
+{
+   FILE *f = (FILE *)file;
+   fputc(ch, f);
+}
diff --git a/libspeex/misc.h b/libspeex/misc.h
new file mode 100644 (file)
index 0000000..af8c2ab
--- /dev/null
@@ -0,0 +1,147 @@
+/* Copyright (C) 2002 Jean-Marc Valin */
+/**
+   @file misc.h
+   @brief Various compatibility routines for Speex
+*/
+/*
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+   
+   - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   
+   - Neither the name of the Xiph.org Foundation nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+   
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MISC_H
+#define MISC_H
+
+#ifndef VERSION
+#define VERSION "speex-1.1"
+#endif
+
+#ifdef FIXED_POINT
+
+typedef signed short spx_word16_t;
+typedef signed int   spx_word32_t;
+typedef spx_word32_t spx_mem_t;
+typedef spx_word16_t spx_coef_t;
+typedef spx_word16_t spx_lsp_t;
+typedef spx_word32_t spx_sig_t;
+
+#define LPC_SCALING  8192.
+#define SIG_SCALING  16384
+#define LSP_SCALING  8192.
+
+#define LPC_SHIFT    13
+#define SIG_SHIFT    14
+
+#define PSHR(a,shift) (((a)+(1<<((shift)-1))) >> (shift))
+#define SHR(a,shift) ((a) >> (shift))
+#define SHL(a,shift) ((a) << (shift))
+
+/* result fits in 16 bits */
+#define MULT16_16_16(a,b)     (((short)(a))*(b))
+
+#define MULT16_16(a,b)     (((signed int)(a))*(b))
+#define MULT16_32_Q13(a,b) (((a)*((b)>>13)) + ((a)*((signed int)((b)&0x00001fff))>>13))
+#define MULT16_32_Q14(a,b) (((a)*((b)>>14)) + ((a)*((signed int)((b)&0x00003fff))>>14))
+#define MULT16_32_Q15(a,b) (((a)*((b)>>15)) + ((a)*((signed int)((b)&0x00007fff))>>15))
+
+#define MULT16_16_Q13(a,b) (((signed int)(a))*(b)>>13)
+#define MULT16_16_Q14(a,b) (((signed int)(a))*(b)>>14)
+#define MULT16_16_Q15(a,b) (((signed int)(a))*(b)>>15)
+
+#define MULT16_16_P14(a,b) ((8192+((signed int)(a))*(b))>>14)
+
+
+#define DIV32_16(a,b) (((signed int)(a))/(b))
+
+#else
+
+typedef float spx_mem_t;
+typedef float spx_coef_t;
+typedef float spx_lsp_t;
+typedef float spx_sig_t;
+typedef float spx_word16_t;
+typedef float spx_word32_t;
+
+#define LPC_SCALING  1.
+#define SIG_SCALING  1.
+#define LSP_SCALING  1.
+
+#define LPC_SHIFT    0
+#define SIG_SHIFT    0
+
+#define PSHR(a,shift)       (a)
+#define SHR(a,shift)       (a)
+#define SHL(a,shift)       (a)
+#define MULT16_16_16(a,b)     ((a)*(b))
+#define MULT16_16(a,b)     ((a)*(b))
+
+#define MULT16_32_Q13(a,b)     ((a)*(b))
+#define MULT16_32_Q14(a,b)     ((a)*(b))
+#define MULT16_32_Q15(a,b)     ((a)*(b))
+
+#define MULT16_16_Q13(a,b)     ((a)*(b))
+#define MULT16_16_Q14(a,b)     ((a)*(b))
+#define MULT16_16_Q15(a,b)     ((a)*(b))
+
+
+#endif
+
+#ifndef RELEASE
+void print_vec(float *vec, int len, char *name);
+#endif
+
+unsigned int be_int(unsigned int i);
+unsigned int le_int(unsigned int i);
+
+
+unsigned short be_short(unsigned short s);
+unsigned short le_short(unsigned short s);
+
+/** Speex wrapper for calloc. To do your own dynamic allocation, all you need to do is replace this function, speex_realloc and speex_free */
+void *speex_alloc (int size);
+
+/** Speex wrapper for realloc. To do your own dynamic allocation, all you need to do is replace this function, speex_alloc and speex_free */
+void *speex_realloc (void *ptr, int size);
+
+/** Speex wrapper for calloc. To do your own dynamic allocation, all you need to do is replace this function, speex_realloc and speex_alloc */
+void speex_free (void *ptr);
+
+/** Speex wrapper for mem_move */
+void *speex_move (void *dest, void *src, int n);
+
+void speex_error(char *str);
+
+void speex_warning(char *str);
+
+void speex_warning_int(char *str, int val);
+
+void speex_rand_vec(float std, spx_sig_t *data, int len);
+
+float speex_rand(float std);
+
+void _speex_putc(int ch, void *file);
+
+#endif
diff --git a/libspeex/preprocess.c b/libspeex/preprocess.c
new file mode 100644 (file)
index 0000000..4e8a547
--- /dev/null
@@ -0,0 +1,1136 @@
+/* Copyright (C) 2003 Epic Games 
+   Written by Jean-Marc Valin
+
+   File: preprocess.c
+   Preprocessor with denoising based on the algorithm by Ephraim and Malah
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+   1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+   2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+   3. The name of the author may not be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+   IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+   POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <math.h>
+#include "speex_preprocess.h"
+#include "misc.h"
+#include "smallft.h"
+
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#define min(a,b) ((a) < (b) ? (a) : (b))
+
+/* use single-precision math funcs.  (1.3% perf improvement). */
+#if 1
+#define exp(a)   expf(a)
+#define sqrt(a)          sqrtf(a)
+#define floor(a)  floorf(a)
+#define log(a)   logf(a)
+#endif
+
+
+
+#ifndef M_PI
+#define M_PI 3.14159263
+#endif
+
+#define SQRT_M_PI_2 0.88623
+#define LOUDNESS_EXP 2.5
+
+#define NB_BANDS 8
+
+#define ZMIN .1
+#define ZMAX .316
+#define ZMIN_1 10
+#define LOG_MIN_MAX_1 0.86859
+
+static void conj_window(float *w, int len)
+{
+   int i;
+   for (i=0;i<len;i++)
+   {
+      float x=4*((float)i)/len;
+      int inv=0;
+      if (x<1)
+      {
+      } else if (x<2)
+      {
+         x=2-x;
+         inv=1;
+      } else if (x<3)
+      {
+         x=x-2;
+         inv=1;
+      } else {
+         x=4-x;
+      }
+      x*=1.9979;
+      w[i]=(.5-.5*cos(x))*(.5-.5*cos(x));
+      if (inv)
+         w[i]=1-w[i];
+      w[i]=sqrt(w[i]);
+   }
+}
+
+/* This function approximates the gain function 
+   y = gamma(1.25)^2 * M(-.25;1;-x) / sqrt(x)  
+   which multiplied by xi/(1+xi) is the optimal gain
+   in the loudness domain ( sqrt[amplitude] )
+*/
+static float hypergeom_gain(float x)
+{
+   int ind;
+   float integer, frac;
+   static float table[21] = {
+      0.82157, 1.02017, 1.20461, 1.37534, 1.53363, 1.68092, 1.81865, 
+      1.94811, 2.07038, 2.18638, 2.29688, 2.40255, 2.50391, 2.60144, 
+      2.69551, 2.78647, 2.87458, 2.96015, 3.04333, 3.12431, 3.20326};
+   
+   if (x>9.5)
+      return 1+.12/x;
+
+   integer = floor(x);
+   frac = x-integer;
+   ind = (int)integer;
+   
+   return ((1-frac)*table[ind] + frac*table[ind+1])/sqrt(x+.0001);
+}
+
+SpeexPreprocessState *speex_preprocess_state_init(int frame_size, int sampling_rate)
+{
+   int i;
+   int N, N3, N4;
+
+   SpeexPreprocessState *st = (SpeexPreprocessState *)speex_alloc(sizeof(SpeexPreprocessState));
+   st->frame_size = frame_size;
+
+   /* Round ps_size down to the nearest power of two */
+#if 0
+   i=1;
+   st->ps_size = st->frame_size;
+   while(1)
+   {
+      if (st->ps_size & ~i)
+      {
+         st->ps_size &= ~i;
+         i<<=1;
+      } else {
+         break;
+      }
+   }
+   
+   
+   if (st->ps_size < 3*st->frame_size/4)
+      st->ps_size = st->ps_size * 3 / 2;
+#else
+   st->ps_size = st->frame_size;
+#endif
+
+   N = st->ps_size;
+   N3 = 2*N - st->frame_size;
+   N4 = st->frame_size - N3;
+   
+   st->sampling_rate = sampling_rate;
+   st->denoise_enabled = 1;
+   st->agc_enabled = 0;
+   st->agc_level = 8000;
+   st->vad_enabled = 0;
+
+   st->speech_prob_start = SPEEX_PROB_START ;
+   st->speech_prob_continue = SPEEX_PROB_CONTINUE ;
+   
+   st->frame = (float*)speex_alloc(2*N*sizeof(float));
+   st->ps = (float*)speex_alloc(N*sizeof(float));
+   st->gain2 = (float*)speex_alloc(N*sizeof(float));
+   st->window = (float*)speex_alloc(2*N*sizeof(float));
+   st->noise = (float*)speex_alloc(N*sizeof(float));
+   st->old_ps = (float*)speex_alloc(N*sizeof(float));
+   st->gain = (float*)speex_alloc(N*sizeof(float));
+   st->prior = (float*)speex_alloc(N*sizeof(float));
+   st->post = (float*)speex_alloc(N*sizeof(float));
+   st->loudness_weight = (float*)speex_alloc(N*sizeof(float));
+   st->inbuf = (float*)speex_alloc(N3*sizeof(float));
+   st->outbuf = (float*)speex_alloc(N3*sizeof(float));
+   st->echo_noise = (float*)speex_alloc(N*sizeof(float));
+
+   st->S = (float*)speex_alloc(N*sizeof(float));
+   st->Smin = (float*)speex_alloc(N*sizeof(float));
+   st->Stmp = (float*)speex_alloc(N*sizeof(float));
+   st->update_prob = (float*)speex_alloc(N*sizeof(float));
+
+   st->zeta = (float*)speex_alloc(N*sizeof(float));
+   st->Zpeak = 0;
+   st->Zlast = 0;
+
+   st->noise_bands = (float*)speex_alloc(NB_BANDS*sizeof(float));
+   st->noise_bands2 = (float*)speex_alloc(NB_BANDS*sizeof(float));
+   st->speech_bands = (float*)speex_alloc(NB_BANDS*sizeof(float));
+   st->speech_bands2 = (float*)speex_alloc(NB_BANDS*sizeof(float));
+   st->noise_bandsN = st->speech_bandsN = 1;
+
+   conj_window(st->window, 2*N3);
+   for (i=2*N3;i<2*st->ps_size;i++)
+      st->window[i]=1;
+   
+   if (N4>0)
+   {
+      for (i=N3-1;i>=0;i--)
+      {
+         st->window[i+N3+N4]=st->window[i+N3];
+         st->window[i+N3]=1;
+      }
+   }
+   for (i=0;i<N;i++)
+   {
+      st->noise[i]=1e4;
+      st->old_ps[i]=1e4;
+      st->gain[i]=1;
+      st->post[i]=1;
+      st->prior[i]=1;
+   }
+
+   for (i=0;i<N3;i++)
+   {
+      st->inbuf[i]=0;
+      st->outbuf[i]=0;
+   }
+
+   for (i=0;i<N;i++)
+   {
+      float ff=((float)i)*.5*sampling_rate/((float)N);
+      st->loudness_weight[i] = .35-.35*ff/16000+.73*exp(-.5*(ff-3800)*(ff-3800)/9e5);
+      if (st->loudness_weight[i]<.01)
+         st->loudness_weight[i]=.01;
+      st->loudness_weight[i] *= st->loudness_weight[i];
+   }
+
+   st->speech_prob = 0;
+   st->last_speech = 1000;
+   st->loudness = pow(6000,LOUDNESS_EXP);
+   st->loudness2 = 6000;
+   st->nb_loudness_adapt = 0;
+
+   st->fft_lookup = (struct drft_lookup*)speex_alloc(sizeof(struct drft_lookup));
+   drft_init(st->fft_lookup,2*N);
+
+   st->nb_adapt=0;
+   st->consec_noise=0;
+   st->nb_preprocess=0;
+   return st;
+}
+
+void speex_preprocess_state_destroy(SpeexPreprocessState *st)
+{
+   speex_free(st->frame);
+   speex_free(st->ps);
+   speex_free(st->gain2);
+   speex_free(st->window);
+   speex_free(st->noise);
+   speex_free(st->old_ps);
+   speex_free(st->gain);
+   speex_free(st->prior);
+   speex_free(st->post);
+   speex_free(st->loudness_weight);
+   speex_free(st->echo_noise);
+
+   speex_free(st->S);
+   speex_free(st->Smin);
+   speex_free(st->Stmp);
+   speex_free(st->update_prob);
+
+   speex_free(st->noise_bands);
+   speex_free(st->noise_bands2);
+   speex_free(st->speech_bands);
+   speex_free(st->speech_bands2);
+
+   speex_free(st->inbuf);
+   speex_free(st->outbuf);
+
+   drft_clear(st->fft_lookup);
+   speex_free(st->fft_lookup);
+
+   speex_free(st);
+}
+
+static void update_noise(SpeexPreprocessState *st, float *ps, float *echo)
+{
+   int i;
+   float beta;
+   st->nb_adapt++;
+   beta=1.0/st->nb_adapt;
+   if (beta < .05)
+      beta=.05;
+   
+   if (!echo)
+   {
+      for (i=0;i<st->ps_size;i++)
+         st->noise[i] = (1-beta)*st->noise[i] + beta*ps[i];   
+   } else {
+      for (i=0;i<st->ps_size;i++)
+         st->noise[i] = (1-beta)*st->noise[i] + beta*max(0,ps[i]-echo[i]); 
+#if 0
+      for (i=0;i<st->ps_size;i++)
+         st->noise[i] = 0;
+#endif
+   }
+}
+
+static int speex_compute_vad(SpeexPreprocessState *st, float *ps, float mean_prior, float mean_post)
+{
+   int i, is_speech=0;
+   int N = st->ps_size;
+   float scale=.5/N;
+
+   /* FIXME: Clean this up a bit */
+   {
+      float bands[NB_BANDS];
+      int j;
+      float p0, p1;
+      float tot_loudness=0;
+      float x = sqrt(mean_post);
+
+      for (i=5;i<N-10;i++)
+      {
+         tot_loudness += scale*st->ps[i] * st->loudness_weight[i];
+      }
+
+      for (i=0;i<NB_BANDS;i++)
+      {
+         bands[i]=1e4;
+         for (j=i*N/NB_BANDS;j<(i+1)*N/NB_BANDS;j++)
+         {
+            bands[i] += ps[j];
+         }
+         bands[i]=log(bands[i]);
+      }
+      
+      /*p1 = .0005+.6*exp(-.5*(x-.4)*(x-.4)*11)+.1*exp(-1.2*x);
+      if (x<1.5)
+         p0=.1*exp(2*(x-1.5));
+      else
+         p0=.02+.1*exp(-.2*(x-1.5));
+      */
+
+      p0=1/(1+exp(3*(1.5-x)));
+      p1=1-p0;
+
+      /*fprintf (stderr, "%f %f ", p0, p1);*/
+      /*p0 *= .99*st->speech_prob + .01*(1-st->speech_prob);
+      p1 *= .01*st->speech_prob + .99*(1-st->speech_prob);
+      
+      st->speech_prob = p0/(p1+p0);
+      */
+
+      if (st->noise_bandsN < 50 || st->speech_bandsN < 50)
+      {
+         if (mean_post > 5)
+         {
+            float adapt = 1./st->speech_bandsN++;
+            if (adapt<.005)
+               adapt = .005;
+            for (i=0;i<NB_BANDS;i++)
+            {
+               st->speech_bands[i] = (1-adapt)*st->speech_bands[i] + adapt*bands[i];
+               /*st->speech_bands2[i] = (1-adapt)*st->speech_bands2[i] + adapt*bands[i]*bands[i];*/
+               st->speech_bands2[i] = (1-adapt)*st->speech_bands2[i] + adapt*(bands[i]-st->speech_bands[i])*(bands[i]-st->speech_bands[i]);
+            }
+         } else {
+            float adapt = 1./st->noise_bandsN++;
+            if (adapt<.005)
+               adapt = .005;
+            for (i=0;i<NB_BANDS;i++)
+            {
+               st->noise_bands[i] = (1-adapt)*st->noise_bands[i] + adapt*bands[i];
+               /*st->noise_bands2[i] = (1-adapt)*st->noise_bands2[i] + adapt*bands[i]*bands[i];*/
+               st->noise_bands2[i] = (1-adapt)*st->noise_bands2[i] + adapt*(bands[i]-st->noise_bands[i])*(bands[i]-st->noise_bands[i]);
+            }
+         }
+      }
+      p0=p1=1;
+      for (i=0;i<NB_BANDS;i++)
+      {
+         float noise_var, speech_var;
+         float noise_mean, speech_mean;
+         float tmp1, tmp2, pr;
+
+         /*noise_var = 1.01*st->noise_bands2[i] - st->noise_bands[i]*st->noise_bands[i];
+           speech_var = 1.01*st->speech_bands2[i] - st->speech_bands[i]*st->speech_bands[i];*/
+         noise_var = st->noise_bands2[i];
+         speech_var = st->speech_bands2[i];
+         if (noise_var < .1)
+            noise_var = .1;
+         if (speech_var < .1)
+            speech_var = .1;
+         
+         /*speech_var = sqrt(speech_var*noise_var);
+           noise_var = speech_var;*/
+         if (speech_var < .05*speech_var)
+            noise_var = .05*speech_var; 
+         if (speech_var < .05*noise_var)
+            speech_var = .05*noise_var;
+         
+         if (bands[i] < st->noise_bands[i])
+            speech_var = noise_var;
+         if (bands[i] > st->speech_bands[i])
+            noise_var = speech_var;
+
+         speech_mean = st->speech_bands[i];
+         noise_mean = st->noise_bands[i];
+         if (noise_mean < speech_mean - 5)
+            noise_mean = speech_mean - 5;
+
+         tmp1 = exp(-.5*(bands[i]-speech_mean)*(bands[i]-speech_mean)/speech_var)/sqrt(2*M_PI*speech_var);
+         tmp2 = exp(-.5*(bands[i]-noise_mean)*(bands[i]-noise_mean)/noise_var)/sqrt(2*M_PI*noise_var);
+         /*fprintf (stderr, "%f ", (float)(p0/(.01+p0+p1)));*/
+         /*fprintf (stderr, "%f ", (float)(bands[i]));*/
+         pr = tmp1/(1e-25+tmp1+tmp2);
+         /*if (bands[i] < st->noise_bands[i])
+            pr=.01;
+         if (bands[i] > st->speech_bands[i] && pr < .995)
+         pr=.995;*/
+         if (pr>.999)
+            pr=.999;
+         if (pr<.001)
+            pr=.001;
+         /*fprintf (stderr, "%f ", pr);*/
+         p0 *= pr;
+         p1 *= (1-pr);
+      }
+
+      p0 = pow(p0,.2);
+      p1 = pow(p1,.2);      
+      
+#if 1
+      p0 *= 2;
+      p0=p0/(p1+p0);
+      if (st->last_speech>20) 
+      {
+         float tmp = sqrt(tot_loudness)/st->loudness2;
+         tmp = 1-exp(-10*tmp);
+         if (p0>tmp)
+            p0=tmp;
+      }
+      p1=1-p0;
+#else
+      if (sqrt(tot_loudness) < .6*st->loudness2 && p0>15*p1)
+         p0=15*p1;
+      if (sqrt(tot_loudness) < .45*st->loudness2 && p0>7*p1)
+         p0=7*p1;
+      if (sqrt(tot_loudness) < .3*st->loudness2 && p0>3*p1)
+         p0=3*p1;
+      if (sqrt(tot_loudness) < .15*st->loudness2 && p0>p1)
+         p0=p1;
+      /*fprintf (stderr, "%f %f ", (float)(sqrt(tot_loudness) /( .25*st->loudness2)), p0/(p1+p0));*/
+#endif
+
+      p0 *= .99*st->speech_prob + .01*(1-st->speech_prob);
+      p1 *= .01*st->speech_prob + .99*(1-st->speech_prob);
+      
+      st->speech_prob = p0/(1e-25+p1+p0);
+      /*fprintf (stderr, "%f %f %f ", tot_loudness, st->loudness2, st->speech_prob);*/
+
+       /* decide if frame is speech using speech probability settings */
+
+/*      if (st->speech_prob> .35 || (st->last_speech < 20 && st->speech_prob>.1)) */
+       if (
+               st->speech_prob > st->speech_prob_start
+               || ( st->last_speech < 20 && st->speech_prob > st->speech_prob_continue ) 
+       )
+       {
+         is_speech = 1;
+         st->last_speech = 0;
+       } 
+       else 
+       {
+               st->last_speech++;
+               if ( st->last_speech < 20 )
+                       is_speech = 1;
+       }
+
+      if (st->noise_bandsN > 50 && st->speech_bandsN > 50)
+      {
+         if (mean_post > 5)
+         {
+            float adapt = 1./st->speech_bandsN++;
+            if (adapt<.005)
+               adapt = .005;
+            for (i=0;i<NB_BANDS;i++)
+            {
+               st->speech_bands[i] = (1-adapt)*st->speech_bands[i] + adapt*bands[i];
+               /*st->speech_bands2[i] = (1-adapt)*st->speech_bands2[i] + adapt*bands[i]*bands[i];*/
+               st->speech_bands2[i] = (1-adapt)*st->speech_bands2[i] + adapt*(bands[i]-st->speech_bands[i])*(bands[i]-st->speech_bands[i]);
+            }
+         } else {
+            float adapt = 1./st->noise_bandsN++;
+            if (adapt<.005)
+               adapt = .005;
+            for (i=0;i<NB_BANDS;i++)
+            {
+               st->noise_bands[i] = (1-adapt)*st->noise_bands[i] + adapt*bands[i];
+               /*st->noise_bands2[i] = (1-adapt)*st->noise_bands2[i] + adapt*bands[i]*bands[i];*/
+               st->noise_bands2[i] = (1-adapt)*st->noise_bands2[i] + adapt*(bands[i]-st->noise_bands[i])*(bands[i]-st->noise_bands[i]);
+            }
+         }
+      }
+
+
+   }
+
+   return is_speech;
+}
+
+static void speex_compute_agc(SpeexPreprocessState *st, float mean_prior)
+{
+   int i;
+   int N = st->ps_size;
+   float scale=.5/N;
+   float agc_gain;
+   int freq_start, freq_end;
+   float active_bands = 0;
+
+   freq_start = (int)(300.0*2*N/st->sampling_rate);
+   freq_end   = (int)(2000.0*2*N/st->sampling_rate);
+   for (i=freq_start;i<freq_end;i++)
+   {
+      if (st->S[i] > 20*st->Smin[i]+1000)
+         active_bands+=1;
+   }
+   active_bands /= (freq_end-freq_start+1);
+
+   if (active_bands > .2)
+   {
+      float loudness=0;
+      float rate, rate2=.2;
+      st->nb_loudness_adapt++;
+      rate=2.0/(1+st->nb_loudness_adapt);
+      if (rate < .05)
+         rate = .05;
+      if (rate < .1 && pow(loudness, LOUDNESS_EXP) > st->loudness)
+         rate = .1;
+      if (rate < .2 && pow(loudness, LOUDNESS_EXP) > 3*st->loudness)
+         rate = .2;
+      if (rate < .4 && pow(loudness, LOUDNESS_EXP) > 10*st->loudness)
+         rate = .4;
+
+      for (i=2;i<N;i++)
+      {
+         loudness += scale*st->ps[i] * st->gain2[i] * st->gain2[i] * st->loudness_weight[i];
+      }
+      loudness=sqrt(loudness);
+      /*if (loudness < 2*pow(st->loudness, 1.0/LOUDNESS_EXP) &&
+        loudness*2 > pow(st->loudness, 1.0/LOUDNESS_EXP))*/
+      st->loudness = (1-rate)*st->loudness + (rate)*pow(loudness, LOUDNESS_EXP);
+      
+      st->loudness2 = (1-rate2)*st->loudness2 + rate2*pow(st->loudness, 1.0/LOUDNESS_EXP);
+
+      loudness = pow(st->loudness, 1.0/LOUDNESS_EXP);
+
+      /*fprintf (stderr, "%f %f %f\n", loudness, st->loudness2, rate);*/
+   }
+   
+   agc_gain = st->agc_level/st->loudness2;
+   /*fprintf (stderr, "%f %f %f %f\n", active_bands, st->loudness, st->loudness2, agc_gain);*/
+   if (agc_gain>200)
+      agc_gain = 200;
+
+   for (i=0;i<N;i++)
+      st->gain2[i] *= agc_gain;
+   
+}
+
+static void preprocess_analysis(SpeexPreprocessState *st, short *x)
+{
+   int i;
+   int N = st->ps_size;
+   int N3 = 2*N - st->frame_size;
+   int N4 = st->frame_size - N3;
+   float *ps=st->ps;
+
+   /* 'Build' input frame */
+   for (i=0;i<N3;i++)
+      st->frame[i]=st->inbuf[i];
+   for (i=0;i<st->frame_size;i++)
+      st->frame[N3+i]=x[i];
+   
+   /* Update inbuf */
+   for (i=0;i<N3;i++)
+      st->inbuf[i]=x[N4+i];
+
+   /* Windowing */
+   for (i=0;i<2*N;i++)
+      st->frame[i] *= st->window[i];
+
+   /* Perform FFT */
+   drft_forward(st->fft_lookup, st->frame);
+
+   /* Power spectrum */
+   ps[0]=1;
+   for (i=1;i<N;i++)
+      ps[i]=1+st->frame[2*i-1]*st->frame[2*i-1] + st->frame[2*i]*st->frame[2*i];
+
+}
+
+static void update_noise_prob(SpeexPreprocessState *st)
+{
+   int i;
+   int N = st->ps_size;
+
+   for (i=1;i<N-1;i++)
+      st->S[i] = 100+ .8*st->S[i] + .05*st->ps[i-1]+.1*st->ps[i]+.05*st->ps[i+1];
+   
+   if (st->nb_preprocess<1)
+   {
+      for (i=1;i<N-1;i++)
+         st->Smin[i] = st->Stmp[i] = st->S[i]+100;
+   }
+
+   if (st->nb_preprocess%200==0)
+   {
+      for (i=1;i<N-1;i++)
+      {
+         st->Smin[i] = min(st->Stmp[i], st->S[i]);
+         st->Stmp[i] = st->S[i];
+      }
+   } else {
+      for (i=1;i<N-1;i++)
+      {
+         st->Smin[i] = min(st->Smin[i], st->S[i]);
+         st->Stmp[i] = min(st->Stmp[i], st->S[i]);      
+      }
+   }
+   for (i=1;i<N-1;i++)
+   {
+      st->update_prob[i] *= .2;
+      if (st->S[i] > 5*st->Smin[i])
+         st->update_prob[i] += .8;
+      /*fprintf (stderr, "%f ", st->S[i]/st->Smin[i]);*/
+      /*fprintf (stderr, "%f ", st->update_prob[i]);*/
+   }
+
+}
+
+inline void ephraim_malah(SpeexPreprocessState *st, int N, float Pframe)
+{
+   int i;   
+
+   /* defactor loop for i=1,1 < i < N-1, and i= N-1 cases */ 
+   /* i=1 case */
+   {
+      float MM;
+      float theta;
+      float prior_ratio;
+      float p, q;
+      float zeta1;
+      float P1;
+                                                                                
+      prior_ratio = st->prior[1]/(1.0001+st->prior[1]);
+      theta = (1+st->post[1])*prior_ratio;
+
+      zeta1 = st->zeta[1];
+
+      if (zeta1<ZMIN)
+         P1 = 0;
+      else if (zeta1>ZMAX)
+         P1 = 1;
+      else
+         P1 = LOG_MIN_MAX_1 * log(ZMIN_1*zeta1);
+                                                                                
+      /*P1 = log(zeta1/ZMIN)/log(ZMAX/ZMIN);*/
+                                                                                
+      /* FIXME: add global prop (P2) */
+      q = 1-Pframe*P1;
+      if (q>.95)
+         q=.95;
+      p=1/(1 + (q/(1-q))*(1+st->prior[1])*exp(-theta));
+                                                                                
+      /* Optimal estimator for loudness domain */
+      MM = hypergeom_gain(theta);
+                                                                                
+      st->gain[1] = prior_ratio * MM;
+      /*Put some (very arbitraty) limit on the gain*/
+      if (st->gain[1]>2)
+      {
+         st->gain[1]=2;
+      }
+                                                                                
+      if (st->denoise_enabled)
+      {
+         st->gain2[1]=p*p*st->gain[1];
+      } else {
+         st->gain2[1]=1;
+      }
+   }
+
+   for (i=2;i<(N-1);i++)
+   {
+      float MM;
+      float theta;
+      float prior_ratio;
+      float p, q;
+      float zeta1;
+      float P1;
+      
+    
+      zeta1 = .25*st->zeta[i-1] + .5*st->zeta[i] + .25*st->zeta[i+1];
+
+      prior_ratio = st->prior[i]/(1.0001+st->prior[i]);
+      theta = (1+st->post[i])*prior_ratio;
+
+                                                                                
+
+      if (zeta1<ZMIN)
+         P1 = 0;
+      else if (zeta1>ZMAX)
+         P1 = 1;
+      else
+         P1 = LOG_MIN_MAX_1 * log(ZMIN_1*zeta1);
+                                                                                
+      /*P1 = log(zeta1/ZMIN)/log(ZMAX/ZMIN);*/
+
+                                                                                
+      /* FIXME: add global prop (P2) */
+      q = 1-Pframe*P1;
+      if (q>.95)
+         q=.95;
+      p=1/(1 + (q/(1-q))*(1+st->prior[i])*exp(-theta));
+                                                                                
+      /* Optimal estimator for loudness domain */
+      MM = hypergeom_gain(theta);
+                                                                                
+      st->gain[i] = prior_ratio * MM;
+      /*Put some (very arbitraty) limit on the gain*/
+      if (st->gain[i]>2)
+      {
+         st->gain[i]=2;
+      }
+                                                                                
+      if (st->denoise_enabled)
+      {
+         st->gain2[i]=p*p*st->gain[i];
+      } else {
+         st->gain2[i]=1;
+      }
+   }
+
+   /* i = N-1 case */
+   {
+      float MM;
+      float theta;
+      float prior_ratio;
+      float p, q;
+      float zeta1;
+      float P1;
+                                                                                
+      prior_ratio = st->prior[N-1]/(1.0001+st->prior[N-1]);
+      theta = (1+st->post[N-1])*prior_ratio;
+                                                                                
+      zeta1 = st->zeta[N-1];
+
+      if (zeta1<ZMIN)
+         P1 = 0;
+      else if (zeta1>ZMAX)
+         P1 = 1;
+      else
+         P1 = LOG_MIN_MAX_1 * log(ZMIN_1*zeta1);
+                                                                                
+      /*P1 = log(zeta1/ZMIN)/log(ZMAX/ZMIN);*/
+                                                                                
+      /* FIXME: add global prop (P2) */
+      q = 1-Pframe*P1;
+      if (q>.95)
+         q=.95;
+      p=1/(1 + (q/(1-q))*(1+st->prior[N-1])*exp(-theta));
+                                                                                
+      /* Optimal estimator for loudness domain */
+      MM = hypergeom_gain(theta);
+                                                                                
+      st->gain[N-1] = prior_ratio * MM;
+      /*Put some (very arbitraty) limit on the gain*/
+      if (st->gain[N-1]>2)
+      {
+         st->gain[N-1]=2;
+      }
+                                                                                
+      if (st->denoise_enabled)
+      {
+         st->gain2[N-1]=p*p*st->gain[N-1];
+      } else {
+         st->gain2[N-1]=1;
+      }
+   }
+   st->gain2[0]=st->gain[0]=0;
+   st->gain2[N-1]=st->gain[N-1]=0;
+}
+
+
+int speex_preprocess(SpeexPreprocessState *st, short *x, float *echo)
+{
+   int i;
+   int is_speech=1;
+   float mean_post=0;
+   float mean_prior=0;
+   int N = st->ps_size;
+   int N3 = 2*N - st->frame_size;
+   int N4 = st->frame_size - N3;
+   float scale=.5/N;
+   float *ps=st->ps;
+   float Zframe=0, Pframe;
+
+   preprocess_analysis(st, x);
+
+   update_noise_prob(st);
+
+   st->nb_preprocess++;
+
+   /* Noise estimation always updated for the 20 first times */
+   if (st->nb_adapt<10)
+   {
+      update_noise(st, ps, echo);
+   }
+
+   /* Deal with residual echo if provided */
+   if (echo)
+      for (i=1;i<N;i++)
+         st->echo_noise[i] = (.7*st->echo_noise[i] + .3* echo[i]);
+
+   /* Compute a posteriori SNR */
+   for (i=1;i<N;i++)
+   {
+      st->post[i] = ps[i]/(1+st->noise[i]+st->echo_noise[i]) - 1;
+      if (st->post[i]>100)
+         st->post[i]=100;
+      /*if (st->post[i]<0)
+        st->post[i]=0;*/
+      mean_post+=st->post[i];
+   }
+   mean_post /= N;
+   if (mean_post<0)
+      mean_post=0;
+
+   /* Special case for first frame */
+   if (st->nb_adapt==1)
+      for (i=1;i<N;i++)
+         st->old_ps[i] = ps[i];
+
+   /* Compute a priori SNR */
+   {
+      /* A priori update rate */
+      float gamma;
+      float min_gamma=0.12;
+      gamma = 1.0/st->nb_preprocess;
+
+      /*Make update rate smaller when there's no speech*/
+#if 0
+      if (mean_post<3.5 && mean_prior < 1)
+         min_gamma *= (mean_post+.5);
+      else
+         min_gamma *= 4.;
+#else
+      min_gamma = .1*fabs(mean_prior - mean_post)*fabs(mean_prior - mean_post);
+      if (min_gamma>.15)
+         min_gamma = .15;
+      if (min_gamma<.02)
+         min_gamma = .02;
+#endif
+      /*min_gamma = .08;*/
+
+      /*if (gamma<min_gamma)*/
+         gamma=min_gamma;
+      
+      for (i=1;i<N;i++)
+      {
+         
+         /* A priori SNR update */
+         st->prior[i] = gamma*max(0.0,st->post[i]) +
+         (1-gamma)*st->gain[i]*st->gain[i]*st->old_ps[i]/(1+st->noise[i]+st->echo_noise[i]);
+         
+         if (st->prior[i]>100)
+            st->prior[i]=100;
+         
+         mean_prior+=st->prior[i];
+      }
+   }
+   mean_prior /= N;
+
+#if 0
+   for (i=0;i<N;i++)
+   {
+      fprintf (stderr, "%f ", st->prior[i]);
+   }
+   fprintf (stderr, "\n");
+#endif
+   /*fprintf (stderr, "%f %f\n", mean_prior,mean_post);*/
+
+   if (st->nb_preprocess>=20)
+   {
+      int do_update = 0;
+      float noise_ener=0, sig_ener=0;
+      /* If SNR is low (both a priori and a posteriori), update the noise estimate*/
+      /*if (mean_prior<.23 && mean_post < .5)*/
+      if (mean_prior<.23 && mean_post < .5)
+         do_update = 1;
+      for (i=1;i<N;i++)
+      {
+         noise_ener += st->noise[i];
+