Blob Blame History Raw
From a6e3ccb8909e4b3fb8d9a1decd501c1e531b0b83 Mon Sep 17 00:00:00 2001
From: Jeffrey C. Ollie <jeff@ocjtech.us>
Date: Sun, 18 Nov 2007 22:23:17 -0600
Subject: [PATCH] Add app_conference rev 911

---
 apps/Makefile                      |   14 +
 apps/app_conference.c              |  113 ++
 apps/conference/CLI.txt            |   96 +
 apps/conference/Flags.txt          |   31 +
 apps/conference/LICENSE            |  341 ++++
 apps/conference/README             |  125 ++
 apps/conference/README.videoswitch |   87 +
 apps/conference/TODO               |    4 +
 apps/conference/app_conference.h   |  244 +++
 apps/conference/cli.c              | 1265 ++++++++++++++
 apps/conference/cli.h              |   99 ++
 apps/conference/common.h           |   63 +
 apps/conference/conf_frame.h       |   73 +
 apps/conference/conference.c       | 2923 +++++++++++++++++++++++++++++++
 apps/conference/conference.h       |  194 +++
 apps/conference/frame.c            |  679 ++++++++
 apps/conference/frame.h            |   75 +
 apps/conference/member.c           | 3332 ++++++++++++++++++++++++++++++++++++
 apps/conference/member.h           |  315 ++++
 19 files changed, 10073 insertions(+), 0 deletions(-)
 create mode 100644 apps/app_conference.c
 create mode 100644 apps/conference/CLI.txt
 create mode 100644 apps/conference/Flags.txt
 create mode 100644 apps/conference/LICENSE
 create mode 100644 apps/conference/README
 create mode 100644 apps/conference/README.videoswitch
 create mode 100644 apps/conference/TODO
 create mode 100644 apps/conference/app_conference.h
 create mode 100644 apps/conference/cli.c
 create mode 100644 apps/conference/cli.h
 create mode 100644 apps/conference/common.h
 create mode 100644 apps/conference/conf_frame.h
 create mode 100644 apps/conference/conference.c
 create mode 100644 apps/conference/conference.h
 create mode 100644 apps/conference/frame.c
 create mode 100644 apps/conference/frame.h
 create mode 100644 apps/conference/member.c
 create mode 100644 apps/conference/member.h

diff --git a/apps/Makefile b/apps/Makefile
index d0e9215..0722e82 100644
--- a/apps/Makefile
+++ b/apps/Makefile
@@ -38,4 +38,18 @@ endif
 
 all: _all
 
+app_conference.o: ASTCFLAGS+=-DSILDET=2 -Iconference
+
+conference/conference.o: ASTCFLAGS+=-DSILDET=2 -Iconference
+
+conference/member.o: ASTCFLAGS+=-DSILDET=2 -Iconference
+
+conference/frame.o: ASTCFLAGS+=-DSILDET=2 -Iconference
+
+conference/cli.o: ASTCFLAGS+=-DSILDET=2 -Iconference
+
+app_conference.so: app_conference.o conference/conference.o conference/member.o conference/frame.o conference/cli.o
+	$(ECHO_PREFIX) echo "   [LD] $^ -> $@"
+	$(CMD_PREFIX) $(CXX) $(PTHREAD_CFLAGS) $(ASTLDFLAGS) $(SOLINK) -o $@ $^ -lspeex
+
 include $(ASTTOPDIR)/Makefile.moddir_rules
diff --git a/apps/app_conference.c b/apps/app_conference.c
new file mode 100644
index 0000000..824d5dd
--- /dev/null
+++ b/apps/app_conference.c
@@ -0,0 +1,113 @@
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <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 ;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY,
+		"Channel Independent Conference Application");
diff --git a/apps/conference/CLI.txt b/apps/conference/CLI.txt
new file mode 100644
index 0000000..48acf5a
--- /dev/null
+++ b/apps/conference/CLI.txt
@@ -0,0 +1,96 @@
+Current command line used by app_conference
+Please note that app_conference is still work in progress, so this document might be outdated. As always, the source code is the definitive reference (cli.[ch] and conference.[ch]).  You can also obtain help/usage information by using Asterisk CLI help system ("help conference")
+
+A member in a conference can be referred to by its id or by its channel.  Id is a positive number assigned automatically when the member joins the conference.  Channel is Asterisk channel identifier. To obtain a list of member ids and channels in a conference, do:
+
+*CLI> conference list <conference 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/apps/conference/Flags.txt b/apps/conference/Flags.txt
new file mode 100644
index 0000000..dbb9732
--- /dev/null
+++ b/apps/conference/Flags.txt
@@ -0,0 +1,31 @@
+Current dialplan flags used by app_conference
+Please note that app_conference is still work in progress, so this document might be outdated. As always, the source code is the definitive reference (member.c in create_member())
+
+Mute/no receive options:
+'C' : member starts with video muted
+'c' : member starts unable to receive video
+'L' : member starts with audio muted
+'l' : member starts unable to receive audio
+
+Speex preprocessing options (right now app_conference does preprocessing only for Zaptel members):
+'V' : enable speex preprocessing Voice Activity Detection 
+'D' : enable speex preprocessing De-noise
+'A' : enable speex preprocessing Automatic Gain Control
+'T' : member connects through Zaptel, so speex preprocessing should be enabled
+
+DTMF options:
+'X' : enable DTMF switch: video can be switched by users using DTMF. Do not use with 'S'.
+'R' : enable DTMF relay: DTMF tones generate a manager event
+If neither 'X' nor 'R' are present, DTMF tones will be forwarded to all members in the conference
+
+Moderator/video switch options:
+'M' : member is a "moderator". When a moderator quits, all members are kicked and the conference is disabled.
+'S' : member accepts VAD controlled video switching.  Do not use with 'X'.
+
+Miscellaneous:
+'t' : member accepts text based control messages.  The messages are described in a separate document
+'N' : Assume that the member starts off with camera disabled.
+
+Future development (these are not implemented yet):
+'x' : marked member.  We plan to change the behavior so that when ALL moderators quit, all members that are marked will get kicked. Other members in the conference are not affected.
+
diff --git a/apps/conference/LICENSE b/apps/conference/LICENSE
new file mode 100644
index 0000000..a52b16e
--- /dev/null
+++ b/apps/conference/LICENSE
@@ -0,0 +1,341 @@
+
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <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/apps/conference/README b/apps/conference/README
new file mode 100644
index 0000000..4c103ce
--- /dev/null
+++ b/apps/conference/README
@@ -0,0 +1,125 @@
+Introduction
+
+App_conference is a channel-independent conference application.
+It features efficient audio mixing algorithms as well as video selection
+support based on VAD, DTMF or CLI. 
+
+
+Design goals
+
+Appconference has several design goals which are different than Meetme:
+
+    * It does not require a zap channel for timing.
+    * It is very efficient when used with channels which support DTX (silence
+	detection/discontinuous transmission).
+
+    * It can do VAD on channels which do not support DTX (although this 
+	is more expensive than just mixing them, but less expensive then
+	encoding; therefore it might still be a win). 
+    * It presents messages on the Monitor interface for determine which 
+	speakers are active. 
+
+Mixing design
+
+    * Minimize encoding/decoding, minimize mixing.
+    * Minimize generational loss from trancoding.
+    * Usual cases are handled very efficiently:
+          o One speaker: That speaker's frame is sent directly to each 
+		participant which uses the same codec. It is trancoded 
+		_once_ for each additional codec type used by participants.
+          o Two speakers: Each speaker gets the other speaker's frames.  
+		The two speaker's frames are decoded and mixed, and then 
+		encoded _once_ for each codec type used by participants. 
+
+Video features
+
+    * Video passthrough: video from selected member is passed to every
+        member of the conference.
+    * Multiple ways to select video
+        - VAD
+	- DTMF from conference members
+	- CLI 
+    * Ability to set default video sources and to lock/unlock video sources.
+
+
+License
+
+Naturally, app_conference is GPL. The SVN repository also includes parts of 
+libspeex, which is distributed under a BSD-style license. See LICENSE for more
+details.
+
+
+Getting app_conference
+
+app_conference is available via SVN from its own home on sourceforge:
+
+    * http://sourceforge.net/projects/appconference
+
+
+Compiling app_conference
+
+    * Checkout sources
+    * Modify Makefile to point to your Asterisk include directory
+    * make
+    * sudo make install 
+
+
+Using app_conference
+
+There is no configuration file. Conferences are created on-the-fly.
+
+Dialplan syntax: Conference(ConferenceName/Flags/Priority[/VADSTART/VADCONTINUE])
+
+    * ConferenceName: Whatever you want to name the conference
+    * Flags: please see Flags.txt for a comprehensive list of dialplan flags
+    * Priority: Currently ignored; was to be a "speaking priority" so a 
+	higher priority caller could "override" others.
+    * VADSTART: Optional: "probability" to use to detect start of speech.
+    * VADCONTINUE: Optional: "probability" to use to detect continuation 
+	of speech. 
+
+
+CLI Commands
+
+Please look at CLI.txt for a comprehensive list of CLI commands and parameters.
+
+
+Manager Events
+
+<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/apps/conference/README.videoswitch b/apps/conference/README.videoswitch
new file mode 100644
index 0000000..2612194
--- /dev/null
+++ b/apps/conference/README.videoswitch
@@ -0,0 +1,87 @@
+VideoSwitch
+-----------
+(c) Vipadia Limited 2005-2006
+Neil Stratford <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/apps/conference/TODO b/apps/conference/TODO
new file mode 100644
index 0000000..121e83e
--- /dev/null
+++ b/apps/conference/TODO
@@ -0,0 +1,4 @@
+Things we need to do for the next release
+- Enable speex based VAD for all members instead of just for telephone
+members (configurable)
+- Documentation!
diff --git a/apps/conference/app_conference.h b/apps/conference/app_conference.h
new file mode 100644
index 0000000..17aca58
--- /dev/null
+++ b/apps/conference/app_conference.h
@@ -0,0 +1,244 @@
+
+// $Id: app_conference.h 839 2007-01-17 22:32:03Z sbalea $
+
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <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 <speex/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/apps/conference/cli.c b/apps/conference/cli.c
new file mode 100644
index 0000000..3d88ddf
--- /dev/null
+++ b/apps/conference/cli.c
@@ -0,0 +1,1265 @@
+
+// $Id: cli.c 884 2007-06-27 14:56:21Z sbalea $
+
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <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_exit( int fd, int argc, char *argv[] )
+{
+	if ( argc < 3 )
+		return RESULT_SHOWUSAGE ;
+
+	const char *channel = argv[2];
+
+	struct ast_conf_member *member = find_member(channel, 1);
+	if(!member)
+	{
+		ast_cli(fd, "Member %s not found\n", channel);
+		return RESULT_FAILURE;
+	}
+	const char * name = member->conf_name;
+	int res = kick_channel( name, channel );
+	
+	if ( !res )
+	{
+		ast_cli(fd, "Cannot exit channel %s from conference %s\n", channel, name);
+		ast_mutex_unlock(&member->lock);
+		return RESULT_FAILURE;
+	}
+
+	ast_mutex_unlock( &member->lock ) ;
+	return RESULT_SUCCESS ;
+}
+
+static char conference_exit_usage[] =
+	"usage: conference exit <channel>\n"
+	"       exit channel from any conference where it in\n";
+
+static struct ast_cli_entry cli_exit = {
+	{ "conference", "exit", NULL },
+	conference_exit,
+	"exit channel from any conference where it in",
+	conference_exit_usage
+};
+
+int conference_mute( int fd, int argc, char *argv[] )
+{
+	if ( argc < 4 )
+		return RESULT_SHOWUSAGE ;
+
+	// get the conference name
+	const char* name = argv[2] ;
+
+	int member_id;
+	sscanf(argv[3], "%d", &member_id);
+
+	int res = mute_member( name, member_id );
+
+	if (res) ast_cli( fd, "User #: %d muted\n", member_id) ;
+
+	return RESULT_SUCCESS ;
+}
+
+static char conference_mute_usage[] =
+	"usage: conference mute <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 < 3 )
+		return RESULT_SHOWUSAGE ;
+
+	// get the conference name
+	const char* name = argv[2] ;
+
+	int member_id;
+	sscanf(argv[3], "%d", &member_id);
+
+	int res = unmute_member( name, member_id );
+
+	if (res) ast_cli( fd, "User #: %d unmuted\n", member_id) ;
+
+	return RESULT_SUCCESS ;
+}
+
+static char conference_unmute_usage[] =
+	"usage: conference unmute <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_exit );
+	ast_cli_register( &cli_mute );
+	ast_cli_register( &cli_mutechannel );
+	ast_cli_register( &cli_viewstream );
+	ast_cli_register( &cli_viewchannel );
+	ast_cli_register( &cli_unmute );
+	ast_cli_register( &cli_unmutechannel );
+	ast_cli_register( &cli_play_sound ) ;
+	ast_cli_register( &cli_stop_sounds ) ;
+	ast_cli_register( &cli_end );
+	ast_cli_register( &cli_lock );
+	ast_cli_register( &cli_lockchannel );
+	ast_cli_register( &cli_unlock );
+	ast_cli_register( &cli_set_default );
+	ast_cli_register( &cli_set_defaultchannel );
+	ast_cli_register( &cli_video_mute ) ;
+	ast_cli_register( &cli_video_unmute ) ;
+	ast_cli_register( &cli_video_mutechannel ) ;
+	ast_cli_register( &cli_video_unmutechannel ) ;
+	ast_cli_register( &cli_text );
+	ast_cli_register( &cli_textchannel );
+	ast_cli_register( &cli_textbroadcast );
+	ast_cli_register( &cli_drive );
+	ast_cli_register( &cli_drivechannel );
+	ast_manager_register( "ConferenceList", 0, manager_conference_list, "Conference List" );
+	ast_manager_register( "ConferenceEnd", EVENT_FLAG_CALL, manager_conference_end, "Terminate a conference" );
+
+}
+
+void unregister_conference_cli( void )
+{
+	ast_cli_unregister( &cli_restart );
+	ast_cli_unregister( &cli_debug ) ;
+	ast_cli_unregister( &cli_show_stats ) ;
+	ast_cli_unregister( &cli_list );
+	ast_cli_unregister( &cli_kick );
+	ast_cli_unregister( &cli_kickchannel );
+	ast_cli_unregister( &cli_exit );
+	ast_cli_unregister( &cli_mute );
+	ast_cli_unregister( &cli_mutechannel );
+	ast_cli_unregister( &cli_viewstream );
+	ast_cli_unregister( &cli_viewchannel );
+	ast_cli_unregister( &cli_unmute );
+	ast_cli_unregister( &cli_unmutechannel );
+	ast_cli_unregister( &cli_play_sound ) ;
+	ast_cli_unregister( &cli_stop_sounds ) ;
+	ast_cli_unregister( &cli_end );
+	ast_cli_unregister( &cli_lock );
+	ast_cli_unregister( &cli_lockchannel );
+	ast_cli_unregister( &cli_unlock );
+	ast_cli_unregister( &cli_set_default );
+	ast_cli_unregister( &cli_set_defaultchannel );
+	ast_cli_unregister( &cli_video_mute ) ;
+	ast_cli_unregister( &cli_video_unmute ) ;
+	ast_cli_unregister( &cli_video_mutechannel ) ;
+	ast_cli_unregister( &cli_video_unmutechannel ) ;
+	ast_cli_unregister( &cli_text );
+	ast_cli_unregister( &cli_textchannel );
+	ast_cli_unregister( &cli_textbroadcast );
+	ast_cli_unregister( &cli_drive );
+	ast_cli_unregister( &cli_drivechannel );
+	ast_manager_unregister( "ConferenceList" );
+	ast_manager_unregister( "ConferenceEnd" );
+}
diff --git a/apps/conference/cli.h b/apps/conference/cli.h
new file mode 100644
index 0000000..34b79d6
--- /dev/null
+++ b/apps/conference/cli.h
@@ -0,0 +1,99 @@
+
+// $Id: cli.h 880 2007-04-25 15:23:59Z jpgrayson $
+
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <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_exit( int fd, int argc, char *argv[] ) ;
+
+int conference_mute( int fd, int argc, char *argv[] ) ;
+int conference_unmute( int fd, int argc, char *argv[] ) ;
+int conference_mutechannel( int fd, int argc, char *argv[] ) ;
+int conference_unmutechannel( int fd, int argc, char *argv[] ) ;
+int conference_viewstream( int fd, int argc, char *argv[] ) ;
+int conference_viewchannel( int fd, int argc, char *argv[] ) ;
+
+int conference_play_sound( int fd, int argc, char *argv[] ) ;
+int conference_stop_sounds( int fd, int argc, char *argv[] ) ;
+
+int conference_play_video( int fd, int argc, char *argv[] ) ;
+int conference_stop_videos( int fd, int argc, char *argv[] ) ;
+
+int conference_end( int fd, int argc, char *argv[] ) ;
+
+int conference_lock( int fd, int argc, char *argv[] ) ;
+int conference_lockchannel( int fd, int argc, char *argv[] ) ;
+int conference_unlock( int fd, int argc, char *argv[] ) ;
+
+int conference_set_default(int fd, int argc, char *argv[] ) ;
+int conference_set_defaultchannel(int fd, int argc, char *argv[] ) ;
+
+int conference_video_mute(int fd, int argc, char *argv[] ) ;
+int conference_video_mutechannel(int fd, int argc, char *argv[] ) ;
+int conference_video_unmute(int fd, int argc, char *argv[] ) ;
+int conference_video_unmutechannel(int fd, int argc, char *argv[] ) ;
+
+int conference_text( int fd, int argc, char *argv[] ) ;
+int conference_textchannel( int fd, int argc, char *argv[] ) ;
+int conference_textbroadcast( int fd, int argc, char *argv[] ) ;
+
+int conference_drive( int fd, int argc, char *argv[] ) ;
+int conference_drivechannel(int fd, int argc, char *argv[] );
+
+int manager_conference_end(struct mansession *s, const struct message *m);
+
+void register_conference_cli( void ) ;
+void unregister_conference_cli( void ) ;
+
+
+#endif
diff --git a/apps/conference/common.h b/apps/conference/common.h
new file mode 100644
index 0000000..989398c
--- /dev/null
+++ b/apps/conference/common.h
@@ -0,0 +1,63 @@
+
+// $Id: common.h 880 2007-04-25 15:23:59Z jpgrayson $
+
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <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/apps/conference/conf_frame.h b/apps/conference/conf_frame.h
new file mode 100644
index 0000000..e73e57a
--- /dev/null
+++ b/apps/conference/conf_frame.h
@@ -0,0 +1,73 @@
+
+// $Id: conf_frame.h 880 2007-04-25 15:23:59Z jpgrayson $
+
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <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/apps/conference/conference.c b/apps/conference/conference.c
new file mode 100644
index 0000000..72c71d9
--- /dev/null
+++ b/apps/conference/conference.c
@@ -0,0 +1,2923 @@
+
+// $Id: conference.c 886 2007-08-06 14:33:34Z bcholew $
+
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <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
+
+			// notify others about leave
+			char * leave_snd = member->leave_snd;
+			if (conf->membercount && strcmp(leave_snd, "-")>1)
+			{
+				struct ast_conf_member *membertest = conf->memberlist;
+				while (membertest != NULL)
+				{
+					if (membertest != member)
+					{
+						// lock member for basic_play_sound
+						ast_mutex_lock(&membertest->lock);
+						
+						// basic_play_sound unlock member automatically. do not mute on enter message
+						if (!basic_play_sound (membertest, leave_snd, 0))
+						{
+							ast_log(LOG_ERROR, "playing conference[%d] leave message <%s> FAILED on <%s>\n", conf->membercount, leave_snd, membertest->channel_name) ;
+						}
+						else
+						{
+							ast_log(LOG_NOTICE, "playing conference[%d] leave message <%s> on <%s>\n", conf->membercount, leave_snd, membertest->channel_name);
+						}
+						membertest = membertest->next;
+					}
+				}
+			}
+			
+			// delete the member
+			delete_member( member ) ;
+
+			ast_log( AST_CONF_DEBUG, "removed member from conference, name => %s, remaining => %d\n",
+					conf->name, conf->membercount ) ;
+
+			//break ;
+		}
+		else
+		{
+			// if member is a moderator, we end the conference when they leave
+			if ( member_is_moderator )
+			{
+				ast_mutex_lock( &member_list->lock ) ;
+				member_list->kick_flag = 1;
+				ast_mutex_unlock( &member_list->lock ) ;
+			}
+
+			// save a pointer to the current member,
+			// and then point to the next member in the list
+			member_temp = member_list ;
+			member_list = member_list->next ;
+		}
+	}
+	ast_mutex_unlock( &conf->lock );
+
+	// return -1 on error, or the number of members
+	// remaining if the requested member was deleted
+	return count ;
+}
+
+int count_member( struct ast_conf_member* member, struct ast_conference* conf, short add_member )
+{
+	if ( member == NULL || conf == NULL )
+	{
+		ast_log( LOG_WARNING, "unable to count member\n" ) ;
+		return -1 ;
+	}
+
+	short delta = ( add_member == 1 ) ? 1 : -1 ;
+
+	// increment member count
+	conf->membercount += delta ;
+
+	return conf->membercount ;
+}
+
+//
+// queue incoming frame functions
+//
+
+
+
+
+//
+// get conference stats
+//
+
+//
+// returns: -1 => error, 0 => debugging off, 1 => debugging on
+// state: on => 1, off => 0, toggle => -1
+//
+int set_conference_debugging( const char* name, int state )
+{
+	if ( name == NULL )
+		return -1 ;
+
+	// acquire mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	struct ast_conference *conf = conflist ;
+	int new_state = -1 ;
+
+	// loop through conf list
+	while ( conf != NULL )
+	{
+		if ( strncasecmp( (const char*)&(conf->name), name, 80 ) == 0 )
+		{
+			// lock conference
+			// ast_mutex_lock( &(conf->lock) ) ;
+
+			// toggle or set the state
+			if ( state == -1 )
+				conf->debug_flag = ( conf->debug_flag == 0 ) ? 1 : 0 ;
+			else
+				conf->debug_flag = ( state == 0 ) ? 0 : 1 ;
+
+			new_state = conf->debug_flag ;
+
+			// unlock conference
+			// ast_mutex_unlock( &(conf->lock) ) ;
+
+			break ;
+		}
+
+		conf = conf->next ;
+	}
+
+	// release mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return new_state ;
+}
+
+
+int get_conference_count( void )
+{
+	return conference_count ;
+}
+
+int show_conference_stats ( int fd )
+{
+        // no conferences exist
+	if ( conflist == NULL )
+	{
+		ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized.\n") ;
+		return 0 ;
+	}
+
+	// acquire mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	struct ast_conference *conf = conflist ;
+
+	ast_cli( fd, "%-20.20s  %-40.40s\n", "Name", "Members") ;
+
+	// loop through conf list
+	while ( conf != NULL )
+	{
+		ast_cli( fd, "%-20.20s %3d\n", conf->name, conf->membercount ) ;
+		conf = conf->next ;
+	}
+
+	// release mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return 1 ;
+}
+
+int show_conference_list ( int fd, const char *name )
+{
+	struct ast_conf_member *member;
+
+        // no conferences exist
+	if ( conflist == NULL )
+	{
+		ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", name ) ;
+		return 0 ;
+	}
+
+	// acquire mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	struct ast_conference *conf = conflist ;
+
+	// loop through conf list
+	while ( conf != NULL )
+	{
+		if ( strncasecmp( (const char*)&(conf->name), name, 80 ) == 0 )
+		{
+			// acquire conference mutex
+			ast_mutex_lock(&conf->lock);
+
+			// do the biz
+			member = conf->memberlist ;
+			while ( member != NULL )
+			{
+				ast_cli( fd, "User #: %d  ", member->id ) ;
+				ast_cli( fd, "Channel: %s ", member->channel_name ) ;
+
+				ast_cli( fd, "Flags:");
+				if ( member->mute_video ) ast_cli( fd, "C");
+				if ( member->norecv_video ) ast_cli( fd, "c");
+				if ( member->mute_audio ) ast_cli( fd, "L");
+				if ( member->norecv_audio ) ast_cli( fd, "l");
+				if ( member->vad_flag ) ast_cli( fd, "V");
+				if ( member->denoise_flag ) ast_cli( fd, "D");
+				if ( member->agc_flag ) ast_cli( fd, "A");
+				if ( member->dtmf_switch ) ast_cli( fd, "X");
+				if ( member->dtmf_relay ) ast_cli( fd, "R");
+				if ( member->vad_switch ) ast_cli( fd, "S");
+				if ( member->ismoderator ) ast_cli( fd, "M");
+				if ( member->no_camera ) ast_cli( fd, "N");
+				if ( member->does_text ) ast_cli( fd, "t");
+				if ( member->via_telephone ) ast_cli( fd, "T");
+				ast_cli( fd, " " );
+
+				if ( member->id == conf->default_video_source_id )
+					ast_cli(fd, "Default ");
+				if ( member->id == conf->current_video_source_id )
+				{
+					ast_cli(fd, "Showing ");
+					if ( conf->video_locked )
+						ast_cli(fd, "Locked ");
+				}
+				if ( member->driven_member != NULL )
+				{
+					ast_cli(fd, "Driving:%s(%d) ", member->driven_member->channel_name, member->driven_member->id);
+				}
+
+				ast_cli( fd, "\n");
+				member = member->next;
+			}
+
+			// release conference mutex
+			ast_mutex_unlock(&conf->lock);
+
+			break ;
+		}
+
+		conf = conf->next ;
+	}
+
+	// release mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return 1 ;
+}
+
+/* Dump list of conference info */
+int manager_conference_list( struct mansession *s, const struct message *m )
+{
+	const char *id = astman_get_header(m,"ActionID");
+	const char *conffilter = astman_get_header(m,"Conference");
+	char idText[256] = "";
+	struct ast_conf_member *member;
+
+	astman_send_ack(s, m, "Conference list will follow");
+
+  // no conferences exist
+	if ( conflist == NULL )
+		ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", conffilter );;
+
+	if (!ast_strlen_zero(id)) {
+		snprintf(idText,256,"ActionID: %s\r\n",id);
+	}
+
+	// acquire mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	struct ast_conference *conf = conflist ;
+
+	// loop through conf list
+	while ( conf != NULL )
+	{
+		if ( strncasecmp( (const char*)&(conf->name), conffilter, 80 ) == 0 )
+		{
+			// do the biz
+			member = conf->memberlist ;
+			while (member != NULL)
+			  {
+					astman_append(s, "Event: ConferenceEntry\r\n"
+						"ConferenceName: %s\r\n"
+						"Member: %d\r\n"
+						"Channel: %s\r\n"
+						"CallerID: %s\r\n"
+						"CallerIDName: %s\r\n"
+						"Muted: %s\r\n"
+						"VideoMuted: %s\r\n"
+						"Default: %s\r\n"
+						"Current: %s\r\n"
+						"%s"
+						"\r\n",
+						conf->name,
+						member->id,
+						member->channel_name,
+						member->chan->cid.cid_num ? member->chan->cid.cid_num : "unknown",
+						member->chan->cid.cid_name ? member->chan->cid.cid_name : "unknown",
+						member->mute_audio ? "YES" : "NO",
+						member->mute_video ? "YES" : "NO",
+						member->id == conf->default_video_source_id ? "YES" : "NO",
+						member->id == conf->current_video_source_id ? "YES" : "NO",
+						idText);
+			    member = member->next;
+			  }
+			break ;
+		}
+
+		conf = conf->next ;
+	}
+
+	// release mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	astman_append(s,
+		"Event: ConferenceListComplete\r\n"
+		"%s"
+		"\r\n",idText);
+
+	return RESULT_SUCCESS;
+}
+
+int kick_member (  const char* confname, int user_id)
+{
+	struct ast_conf_member *member;
+	int res = 0;
+
+	// no conferences exist
+	if ( conflist == NULL )
+	{
+		ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ;
+		return 0 ;
+	}
+
+	// acquire mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	struct ast_conference *conf = conflist ;
+
+	// loop through conf list
+	while ( conf != NULL )
+	{
+		if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 )
+		{
+		        // do the biz
+			ast_mutex_lock( &conf->lock ) ;
+		        member = conf->memberlist ;
+			while (member != NULL)
+			  {
+			    if (member->id == user_id)
+			      {
+				      ast_mutex_lock( &member->lock ) ;
+				      member->kick_flag = 1;
+				      //ast_soft_hangup(member->chan);
+				      ast_mutex_unlock( &member->lock ) ;
+
+				      res = 1;
+			      }
+			    member = member->next;
+			  }
+			ast_mutex_unlock( &conf->lock ) ;
+			break ;
+		}
+
+		conf = conf->next ;
+	}
+
+	// release mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return res ;
+}
+
+int kick_channel ( const char *confname, const char *channel)
+{
+	struct ast_conf_member *member;
+	int res = 0;
+
+	// no conferences exist
+	if ( conflist == NULL )
+	{
+		ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ;
+		return 0 ;
+	}
+
+	if ( confname == NULL || channel == NULL || strlen(confname) == 0 || strlen(channel) == 0 )
+		return 0;
+
+	// acquire mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	struct ast_conference *conf = conflist ;
+
+	// loop through conf list
+	while ( conf != NULL )
+	{
+		if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 )
+		{
+			// do the biz
+			ast_mutex_lock( &conf->lock ) ;
+			member = conf->memberlist ;
+			while ( member != NULL )
+			{
+				if ( strncasecmp( member->channel_name, channel, 80 ) == 0 )
+				{
+					ast_mutex_lock( &member->lock ) ;
+					member->kick_flag = 1;
+					//ast_soft_hangup(member->chan);
+					ast_mutex_unlock( &member->lock ) ;
+
+					res = 1;
+				}
+				member = member->next;
+			}
+			ast_mutex_unlock( &conf->lock ) ;
+			break ;
+		}
+
+		conf = conf->next ;
+	}
+
+	// release mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return res ;
+}
+
+int kick_all ( void )
+{
+  struct ast_conf_member *member;
+  int res = 0;
+
+        // no conferences exist
+	if ( conflist == NULL )
+	{
+		ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized\n" ) ;
+		return 0 ;
+	}
+
+	// acquire mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	struct ast_conference *conf = conflist ;
+
+	// loop through conf list
+	while ( conf != NULL )
+	{
+		// do the biz
+		ast_mutex_lock( &conf->lock ) ;
+		member = conf->memberlist ;
+		while (member != NULL)
+		{
+			ast_mutex_lock( &member->lock ) ;
+			member->kick_flag = 1;
+			ast_mutex_unlock( &member->lock ) ;
+			member = member->next;
+		}
+		ast_mutex_unlock( &conf->lock ) ;
+		break ;
+
+	conf = conf->next ;
+	}
+
+	// release mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return res ;
+}
+
+int mute_member (  const char* confname, int user_id)
+{
+  struct ast_conf_member *member;
+  int res = 0;
+
+        // no conferences exist
+	if ( conflist == NULL )
+	{
+		ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ;
+		return 0 ;
+	}
+
+	// acquire mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	struct ast_conference *conf = conflist ;
+
+	// loop through conf list
+	while ( conf != NULL )
+	{
+		if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 )
+		{
+		        // do the biz
+			ast_mutex_lock( &conf->lock ) ;
+		        member = conf->memberlist ;
+			while (member != NULL)
+			  {
+			    if (member->id == user_id)
+			      {
+				      ast_mutex_lock( &member->lock ) ;
+				      member->mute_audio = 1;
+				      ast_mutex_unlock( &member->lock ) ;
+				      res = 1;
+			      }
+			    member = member->next;
+			  }
+			ast_mutex_unlock( &conf->lock ) ;
+			break ;
+		}
+
+		conf = conf->next ;
+	}
+
+	// release mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return res ;
+}
+
+int mute_channel (  const char* confname, const char* user_chan)
+{
+  struct ast_conf_member *member;
+  int res = 0;
+
+        // no conferences exist
+	if ( conflist == NULL )
+	{
+		ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ;
+		return 0 ;
+	}
+
+	// acquire mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	struct ast_conference *conf = conflist ;
+
+	// loop through conf list
+	while ( conf != NULL )
+	{
+		if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 )
+		{
+		        // do the biz
+			ast_mutex_lock( &conf->lock ) ;
+		        member = conf->memberlist ;
+			while (member != NULL)
+			  {
+				  if (strncasecmp( member->channel_name, user_chan, 80 ) == 0)
+			      {
+				      ast_mutex_lock( &member->lock ) ;
+				      member->mute_audio = 1;
+				      ast_mutex_unlock( &member->lock ) ;
+				      res = 1;
+			      }
+			    member = member->next;
+			  }
+			ast_mutex_unlock( &conf->lock ) ;
+			break ;
+		}
+
+		conf = conf->next ;
+	}
+
+	// release mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return res ;
+}
+
+int unmute_member (  const char* confname, int user_id)
+{
+  struct ast_conf_member *member;
+  int res = 0;
+
+        // no conferences exist
+	if ( conflist == NULL )
+	{
+		ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ;
+		return 0 ;
+	}
+
+	// acquire mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	struct ast_conference *conf = conflist ;
+
+	// loop through conf list
+	while ( conf != NULL )
+	{
+		if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 )
+		{
+		        // do the biz
+			ast_mutex_lock( &conf->lock ) ;
+		        member = conf->memberlist ;
+			while (member != NULL)
+			  {
+			    if (member->id == user_id)
+			      {
+				      ast_mutex_lock( &member->lock ) ;
+				      member->mute_audio = 0;
+				      ast_mutex_unlock( &member->lock ) ;
+				      res = 1;
+			      }
+			    member = member->next;
+			  }
+			ast_mutex_unlock( &conf->lock ) ;
+			break ;
+		}
+
+		conf = conf->next ;
+	}
+
+	// release mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return res ;
+}
+
+int unmute_channel (const char* confname, const char* user_chan)
+{
+  struct ast_conf_member *member;
+  int res = 0;
+
+        // no conferences exist
+	if ( conflist == NULL )
+	{
+		ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ;
+		return 0 ;
+	}
+
+	// acquire mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	struct ast_conference *conf = conflist ;
+
+	// loop through conf list
+	while ( conf != NULL )
+	{
+		if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 )
+		{
+		        // do the biz
+			ast_mutex_lock( &conf->lock ) ;
+		        member = conf->memberlist ;
+			while (member != NULL)
+			  {
+			   if (strncasecmp( member->channel_name, user_chan, 80 ) == 0)
+			      {
+				      ast_mutex_lock( &member->lock ) ;
+				      member->mute_audio = 0;
+				      ast_mutex_unlock( &member->lock ) ;
+				      res = 1;
+			      }
+			    member = member->next;
+			  }
+			ast_mutex_unlock( &conf->lock ) ;
+			break ;
+		}
+
+		conf = conf->next ;
+	}
+
+	// release mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return res ;
+}
+
+int viewstream_switch ( const char* confname, int user_id, int stream_id )
+{
+  struct ast_conf_member *member;
+  int res = 0;
+
+        // no conferences exist
+	if ( conflist == NULL )
+	{
+		ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ;
+		return 0 ;
+	}
+
+	// acquire mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	struct ast_conference *conf = conflist ;
+
+	// loop through conf list
+	while ( conf != NULL )
+	{
+		if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 )
+		{
+		        // do the biz
+			ast_mutex_lock( &conf->lock ) ;
+		        member = conf->memberlist ;
+			while (member != NULL)
+			{
+				if (member->id == user_id)
+				{
+					// switch the video
+					ast_mutex_lock( &member->lock ) ;
+
+					member->req_id = stream_id;
+					member->conference = 1;
+
+					ast_mutex_unlock( &member->lock ) ;
+					res = 1;
+				}
+				member = member->next;
+			}
+			ast_mutex_unlock( &conf->lock ) ;
+			break ;
+		}
+
+		conf = conf->next ;
+	}
+
+	// release mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return res ;
+}
+
+int viewchannel_switch ( const char* confname, const char* userchan, const char* streamchan )
+{
+  struct ast_conf_member *member;
+  int res = 0;
+  int stream_id = -1;
+
+        // no conferences exist
+	if ( conflist == NULL )
+	{
+		ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ;
+		return 0 ;
+	}
+
+	// acquire mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	struct ast_conference *conf = conflist ;
+
+	// loop through conf list
+	while ( conf != NULL )
+	{
+		if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 )
+		{
+		        // do the biz
+			ast_mutex_lock( &conf->lock ) ;
+		        member = conf->memberlist ;
+			while (member != NULL)
+			{
+				if (strncasecmp( member->channel_name, streamchan, 80 ) == 0)
+				{
+					stream_id = member->id;
+				}
+				member = member->next;
+			}
+			ast_mutex_unlock( &conf->lock ) ;
+			if (stream_id >= 0)
+			{
+				// do the biz
+				ast_mutex_lock( &conf->lock ) ;
+				member = conf->memberlist ;
+				while (member != NULL)
+				{
+					if (strncasecmp( member->channel_name, userchan, 80 ) == 0)
+					{
+						// switch the video
+						ast_mutex_lock( &member->lock ) ;
+
+						member->req_id = stream_id;
+						member->conference = 1;
+
+						ast_mutex_unlock( &member->lock ) ;
+						res = 1;
+					}
+					member = member->next;
+				}
+				ast_mutex_unlock( &conf->lock ) ;
+			}
+			break ;
+		}
+
+		conf = conf->next ;
+	}
+
+	// release mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return res ;
+}
+
+int get_conference_stats( ast_conference_stats* stats, int requested )
+{
+	// no conferences exist
+	if ( conflist == NULL )
+	{
+		ast_log( AST_CONF_DEBUG, "conflist has not yet been initialize\n" ) ;
+		return 0 ;
+	}
+
+	// acquire mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	// compare the number of requested to the number of available conferences
+	requested = ( get_conference_count() < requested ) ? get_conference_count() : requested ;
+
+	//
+	// loop through conf list
+	//
+
+	struct ast_conference* conf = conflist ;
+	int count = 0 ;
+
+	while ( count <= requested && conf != NULL )
+	{
+		// copy stats struct to array
+		stats[ count ] = conf->stats ;
+
+		conf = conf->next ;
+		++count ;
+	}
+
+	// release mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return count ;
+}
+
+int get_conference_stats_by_name( ast_conference_stats* stats, const char* name )
+{
+	// no conferences exist
+	if ( conflist == NULL )
+	{
+		ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", name ) ;
+		return 0 ;
+	}
+
+	// make sure stats is null
+	stats = NULL ;
+
+	// acquire mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	struct ast_conference *conf = conflist ;
+
+	// loop through conf list
+	while ( conf != NULL )
+	{
+		if ( strncasecmp( (const char*)&(conf->name), name, 80 ) == 0 )
+		{
+			// copy stats for found conference
+			*stats = conf->stats ;
+			break ;
+		}
+
+		conf = conf->next ;
+	}
+
+	// release mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return ( stats == NULL ) ? 0 : 1 ;
+}
+
+struct ast_conf_member *find_member (const char *chan, int lock)
+{
+	struct ast_conf_member *found = NULL;
+	struct ast_conf_member *member;
+	struct ast_conference *conf;
+
+	ast_mutex_lock( &conflist_lock ) ;
+
+	conf = conflist;
+
+	// loop through conf list
+	while ( conf != NULL && !found )
+	{
+		// lock conference
+		ast_mutex_lock( &conf->lock );
+
+		member = conf->memberlist ;
+
+		while (member != NULL)
+		{
+		    if(!strcmp(member->channel_name, chan)) {
+			found = member;
+			if(lock)
+			    ast_mutex_lock(&member->lock);
+			break;
+		    }
+		    member = member->next;
+		}
+
+		// unlock conference
+		ast_mutex_unlock( &conf->lock );
+
+		conf = conf->next ;
+	}
+
+	// release mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return found;
+}
+
+// All the VAD-based video switching magic happens here
+// This function should be called inside conference_exec
+// The conference mutex should be locked, we don't have to do it here
+void do_VAD_switching(struct ast_conference *conf)
+{
+	struct ast_conf_member *member;
+	struct timeval         current_time;
+	long                   longest_speaking;
+	struct ast_conf_member *longest_speaking_member;
+	int                    current_silent, current_no_camera, current_video_mute;
+	int                    default_no_camera, default_video_mute;
+
+	current_time = ast_tvnow();
+
+	// Scan the member list looking for the longest speaking member
+	// We also check if the currently speaking member has been silent for a while
+	// Also, we check for camera disabled or video muted members
+	// We say that a member is speaking after his speaking state has been on for
+	// at least AST_CONF_VIDEO_START_TIMEOUT ms
+	// We say that a member is silent after his speaking state has been off for
+	// at least AST_CONF_VIDEO_STOP_TIMEOUT ms
+	longest_speaking = 0;
+	longest_speaking_member = NULL;
+	current_silent = 0;
+	current_no_camera = 0;
+	current_video_mute = 0;
+	default_no_camera = 0;
+	default_video_mute = 0;
+	for ( member = conf->memberlist ;
+	      member != NULL ;
+	      member = member->next )
+	{
+		// Has the state changed since last time through this loop? Notify!
+		if ( member->speaking_state_notify )
+		{
+/*			fprintf(stderr, "Mihai: member %d, channel %s has changed state to %s\n",
+				member->id,
+				member->channel_name,
+				((member->speaking_state == 1 ) ? "speaking" : "silent")
+			       );			*/
+		}
+
+		// If a member connects via telephone, they don't have video
+		if ( member->via_telephone )
+			continue;
+
+		// We check for no VAD switching, video-muted or camera disabled
+		// If yes, this member will not be considered as a candidate for switching
+		// If this is the currently speaking member, then mark it so we force a switch
+		if ( !member->vad_switch )
+			continue;
+
+		if ( member->mute_video )
+		{
+			if ( member->id == conf->default_video_source_id )
+				default_video_mute = 1;
+			if ( member->id == conf->current_video_source_id )
+				current_video_mute = 1;
+			else
+				continue;
+		}
+
+		if ( member->no_camera )
+		{
+			if ( member->id == conf->default_video_source_id )
+				default_no_camera = 1;
+			if ( member->id == conf->current_video_source_id )
+				current_no_camera = 1;
+			else
+				continue;
+		}
+
+		// Check if current speaker has been silent for a while
+		if ( member->id == conf->current_video_source_id &&
+		     member->speaking_state == 0 &&
+		     ast_tvdiff_ms(current_time, member->last_state_change) > AST_CONF_VIDEO_STOP_TIMEOUT )
+		{
+			current_silent = 1;
+		}
+
+		// Find a candidate to switch to by looking for the longest speaking member
+		// We exclude the current video source from the search
+		if ( member->id != conf->current_video_source_id && member->speaking_state == 1 )
+		{
+			long tmp = ast_tvdiff_ms(current_time, member->last_state_change);
+			if ( tmp > AST_CONF_VIDEO_START_TIMEOUT && tmp > longest_speaking )
+			{
+				longest_speaking = tmp;
+				longest_speaking_member = member;
+			}
+		}
+	}
+
+	// We got our results, now let's make a decision
+	// If the currently speaking member has been marked as silent, then we take the longest
+	// speaking member.  If no member is speaking, we go to default
+	// As a policy we don't want to switch away from a member that is speaking
+	// however, we might need to refine this to avoid a situation when a member has a
+	// low noise threshold or its VAD is simply stuck
+	if ( current_silent || current_no_camera || current_video_mute || conf->current_video_source_id < 0 )
+	{
+		if ( longest_speaking_member != NULL )
+		{
+			do_video_switching(conf, longest_speaking_member->id, 0);
+		} else
+		{
+			// If there's nobody speaking and we have a default that can send video, switch to it
+			// If not, then switch to empty (-1)
+			if ( conf->default_video_source_id >= 0 &&
+			     !default_no_camera &&
+			     !default_video_mute
+			   )
+				do_video_switching(conf, conf->default_video_source_id, 0);
+			else
+				do_video_switching(conf, -1, 0);
+		}
+	}
+}
+
+int lock_conference(const char *conference, int member_id)
+{
+	struct ast_conference  *conf;
+	struct ast_conf_member *member;
+	int                   res;
+
+	if ( conference == NULL || member_id < 0 )
+		return -1 ;
+
+	// acquire mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	// Look for conference
+	res = 0;
+	for ( conf = conflist ; conf != NULL ; conf = conf->next )
+	{
+		if ( strcmp(conference, conf->name) == 0 )
+		{
+			// Search member list for our member
+			// acquire conference mutex
+			ast_mutex_lock( &conf->lock );
+
+			for ( member = conf->memberlist ; member != NULL ; member = member->next )
+			{
+				if ( member->id == member_id && !member->mute_video )
+				{
+					do_video_switching(conf, member_id, 0);
+					conf->video_locked = 1;
+					res = 1;
+
+					manager_event(EVENT_FLAG_CALL, "ConferenceLock", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name);
+					break;
+				}
+			}
+
+			// Release conference mutex
+			ast_mutex_unlock( &conf->lock );
+			break;
+		}
+	}
+
+	// release mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return res;
+}
+
+int lock_conference_channel(const char *conference, const char *channel)
+{
+	struct ast_conference  *conf;
+	struct ast_conf_member *member;
+	int                   res;
+
+	if ( conference == NULL || channel == NULL )
+		return -1 ;
+
+	// acquire mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	// Look for conference
+	res = 0;
+	for ( conf = conflist ; conf != NULL ; conf = conf->next )
+	{
+		if ( strcmp(conference, conf->name) == 0 )
+		{
+			// Search member list for our member
+			// acquire conference mutex
+			ast_mutex_lock( &conf->lock );
+
+			for ( member = conf->memberlist ; member != NULL ; member = member->next )
+			{
+				if ( strcmp(channel, member->channel_name) == 0 && !member->mute_video )
+				{
+					do_video_switching(conf, member->id, 0);
+					conf->video_locked = 1;
+					res = 1;
+
+					manager_event(EVENT_FLAG_CALL, "ConferenceLock", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name);
+					break;
+				}
+			}
+
+			// Release conference mutex
+			ast_mutex_unlock( &conf->lock );
+			break;
+		}
+	}
+
+	// release mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return res;
+}
+
+int unlock_conference(const char *conference)
+{
+	struct ast_conference  *conf;
+	int                   res;
+
+	if ( conference == NULL )
+		return -1;
+
+	// acquire conference list mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	// Look for conference
+	res = 0;
+	for ( conf = conflist ; conf != NULL ; conf = conf->next )
+	{
+		if ( strcmp(conference, conf->name) == 0 )
+		{
+			conf->video_locked = 0;
+			manager_event(EVENT_FLAG_CALL, "ConferenceUnlock", "ConferenceName: %s\r\n", conf->name);
+			do_video_switching(conf, conf->default_video_source_id, 0);
+			res = 1;
+
+			break;
+		}
+	}
+
+	// release conference list mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return res;
+}
+
+int set_default_id(const char *conference, int member_id)
+{
+	struct ast_conference  *conf;
+	struct ast_conf_member *member;
+	int                   res;
+
+	if ( conference == NULL )
+		return -1 ;
+
+	// acquire conference list mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	// Look for conference
+	res = 0;
+	for ( conf = conflist ; conf != NULL ; conf = conf->next )
+	{
+		if ( strcmp(conference, conf->name) == 0 )
+		{
+			if ( member_id < 0 )
+			{
+				conf->default_video_source_id = -1;
+				manager_event(EVENT_FLAG_CALL, "ConferenceDefault", "ConferenceName: %s\r\nChannel: empty\r\n", conf->name);
+				res = 1;
+				break;
+			} else
+			{
+				// Search member list for our member
+				// acquire conference mutex
+				ast_mutex_lock( &conf->lock );
+
+				for ( member = conf->memberlist ; member != NULL ; member = member->next )
+				{
+					// We do not allow video muted members or members that do not support
+					// VAD switching to become defaults
+					if ( member->id == member_id &&
+					     !member->mute_video &&
+					     member->vad_switch
+					   )
+					{
+						conf->default_video_source_id = member_id;
+						res = 1;
+
+						manager_event(EVENT_FLAG_CALL, "ConferenceDefault", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name);
+						break;
+					}
+				}
+
+				// Release conference mutex
+				ast_mutex_unlock( &conf->lock );
+				break;
+			}
+		}
+	}
+
+	// release conference list mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return res;
+
+}
+
+int set_default_channel(const char *conference, const char *channel)
+{
+	struct ast_conference  *conf;
+	struct ast_conf_member *member;
+	int                   res;
+
+	if ( conference == NULL || channel == NULL )
+		return -1 ;
+
+	// acquire conference list mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	// Look for conference
+	res = 0;
+	for ( conf = conflist ; conf != NULL ; conf = conf->next )
+	{
+		if ( strcmp(conference, conf->name) == 0 )
+		{
+			// Search member list for our member
+			// acquire conference mutex
+			ast_mutex_lock( &conf->lock );
+
+			for ( member = conf->memberlist ; member != NULL ; member = member->next )
+			{
+				// We do not allow video muted members or members that do not support
+				// VAD switching to become defaults
+				if ( strcmp(channel, member->channel_name) == 0 &&
+				     !member->mute_video &&
+				     member->vad_switch
+				   )
+				{
+					conf->default_video_source_id = member->id;
+					res = 1;
+
+					manager_event(EVENT_FLAG_CALL, "ConferenceDefault", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name);
+					break;
+				}
+			}
+
+			// Release conference mutex
+			ast_mutex_unlock( &conf->lock );
+			break;
+		}
+	}
+
+	// release conference list mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return res;
+}
+
+int video_mute_member(const char *conference, int member_id)
+{
+	struct ast_conference  *conf;
+	struct ast_conf_member *member;
+	int                    res;
+
+	if ( conference == NULL || member_id < 0 )
+		return -1;
+
+	res = 0;
+
+	// acquire conference list mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	for ( conf = conflist ; conf != NULL ; conf = conf->next )
+	{
+		if ( strcmp(conference, conf->name) == 0 )
+		{
+			// acquire conference mutex
+			ast_mutex_lock( &conf->lock );
+
+			for ( member = conf->memberlist ; member != NULL ; member = member->next )
+			{
+				if ( member->id == member_id )
+				{
+					// acquire member mutex
+					ast_mutex_lock( &member->lock );
+
+					member->mute_video = 1;
+
+					// release member mutex
+					ast_mutex_unlock( &member->lock );
+
+					manager_event(EVENT_FLAG_CALL, "ConferenceVideoMute", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name);
+
+					if ( member->id == conf->current_video_source_id )
+					{
+						do_video_switching(conf, conf->default_video_source_id, 0);
+					}
+
+					res = 1;
+					break;
+				}
+			}
+
+			// release conference mutex
+			ast_mutex_unlock( &conf->lock );
+
+			break;
+		}
+	}
+
+	// release conference list mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return res;
+}
+
+int video_unmute_member(const char *conference, int member_id)
+{
+	struct ast_conference  *conf;
+	struct ast_conf_member *member;
+	int                    res;
+
+	if ( conference == NULL || member_id < 0 )
+		return -1;
+
+	res = 0;
+
+	// acquire conference list mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	for ( conf = conflist ; conf != NULL ; conf = conf->next )
+	{
+		if ( strcmp(conference, conf->name) == 0 )
+		{
+			// acquire conference mutex
+			ast_mutex_lock( &conf->lock );
+
+			for ( member = conf->memberlist ; member != NULL ; member = member->next )
+			{
+				if ( member->id == member_id )
+				{
+					// acquire member mutex
+					ast_mutex_lock( &member->lock );
+
+					member->mute_video = 0;
+
+					// release member mutex
+					ast_mutex_unlock( &member->lock );
+
+					manager_event(EVENT_FLAG_CALL, "ConferenceVideoUnmute", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name);
+
+					res = 1;
+					break;
+				}
+			}
+
+			// release conference mutex
+			ast_mutex_unlock( &conf->lock );
+
+			break;
+		}
+	}
+
+	// release conference list mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return res;
+}
+
+int video_mute_channel(const char *conference, const char *channel)
+{
+	struct ast_conference  *conf;
+	struct ast_conf_member *member;
+	int                    res;
+
+	if ( conference == NULL || channel == NULL )
+		return -1;
+
+	res = 0;
+
+	// acquire conference list mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	for ( conf = conflist ; conf != NULL ; conf = conf->next )
+	{
+		if ( strcmp(conference, conf->name) == 0 )
+		{
+			// acquire conference mutex
+			ast_mutex_lock( &conf->lock );
+
+			for ( member = conf->memberlist ; member != NULL ; member = member->next )
+			{
+				if ( strcmp(channel, member->channel_name) == 0 )
+				{
+					// acquire member mutex
+					ast_mutex_lock( &member->lock );
+
+					member->mute_video = 1;
+
+					// release member mutex
+					ast_mutex_unlock( &member->lock );
+
+					manager_event(EVENT_FLAG_CALL, "ConferenceVideoMute", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name);
+
+					if ( member->id == conf->current_video_source_id )
+					{
+						do_video_switching(conf, conf->default_video_source_id, 0);
+					}
+
+					res = 1;
+					break;
+				}
+			}
+
+			// release conference mutex
+			ast_mutex_unlock( &conf->lock );
+
+			break;
+		}
+	}
+
+	// release conference list mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return res;
+}
+
+int video_unmute_channel(const char *conference, const char *channel)
+{
+	struct ast_conference  *conf;
+	struct ast_conf_member *member;
+	int                    res;
+
+	if ( conference == NULL || channel == NULL )
+		return -1;
+
+	res = 0;
+
+	// acquire conference list mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	for ( conf = conflist ; conf != NULL ; conf = conf->next )
+	{
+		if ( strcmp(conference, conf->name) == 0 )
+		{
+			// acquire conference mutex
+			ast_mutex_lock( &conf->lock );
+
+			for ( member = conf->memberlist ; member != NULL ; member = member->next )
+			{
+				if ( strcmp(channel, member->channel_name) == 0 )
+				{
+					// acquire member mutex
+					ast_mutex_lock( &member->lock );
+
+					member->mute_video = 0;
+
+					// release member mutex
+					ast_mutex_unlock( &member->lock );
+
+					manager_event(EVENT_FLAG_CALL, "ConferenceVideoUnmute", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name);
+
+					res = 1;
+					break;
+				}
+			}
+
+			// release conference mutex
+			ast_mutex_unlock( &conf->lock );
+
+			break;
+		}
+	}
+
+	// release conference list mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return res;
+}
+
+//
+// Text message functions
+//
+int send_text(const char *conference, int member_id, const char *text)
+{
+	struct ast_conference  *conf;
+	struct ast_conf_member *member;
+	int                    res;
+
+	if ( conference == NULL || member_id < 0 || text == NULL )
+		return -1;
+
+	res = 0;
+
+	// acquire conference list mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	for ( conf = conflist ; conf != NULL ; conf = conf->next )
+	{
+		if ( strcmp(conference, conf->name) == 0 )
+		{
+			// acquire conference mutex
+			ast_mutex_lock( &conf->lock );
+
+			for ( member = conf->memberlist ; member != NULL ; member = member->next )
+			{
+				if ( member->id == member_id )
+				{
+					res = send_text_message_to_member(member, text) == 0;
+					break;
+				}
+			}
+
+			// release conference mutex
+			ast_mutex_unlock( &conf->lock );
+
+			break;
+		}
+	}
+
+	// release conference list mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+	return res;
+}
+
+int send_text_channel(const char *conference, const char *channel, const char *text)
+{
+	struct ast_conference  *conf;
+	struct ast_conf_member *member;
+	int                    res;
+
+	if ( conference == NULL || channel == NULL || text == NULL )
+		return -1;
+
+	res = 0;
+
+	// acquire conference list mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	for ( conf = conflist ; conf != NULL ; conf = conf->next )
+	{
+		if ( strcmp(conference, conf->name) == 0 )
+		{
+			// acquire conference mutex
+			ast_mutex_lock( &conf->lock );
+
+			for ( member = conf->memberlist ; member != NULL ; member = member->next )
+			{
+				if ( strcmp(member->channel_name, channel) == 0 )
+				{
+					res = send_text_message_to_member(member, text) == 0;
+					break;
+				}
+			}
+
+			// release conference mutex
+			ast_mutex_unlock( &conf->lock );
+
+			break;
+		}
+	}
+
+	// release conference list mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return res;
+}
+
+int send_text_broadcast(const char *conference, const char *text)
+{
+	struct ast_conference  *conf;
+	struct ast_conf_member *member;
+	int                    res;
+
+	if ( conference == NULL || text == NULL )
+		return -1;
+
+	res = 0;
+
+	// acquire conference list mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	for ( conf = conflist ; conf != NULL ; conf = conf->next )
+	{
+		if ( strcmp(conference, conf->name) == 0 )
+		{
+			// acquire conference mutex
+			ast_mutex_lock( &conf->lock );
+
+			for ( member = conf->memberlist ; member != NULL ; member = member->next )
+			{
+				if ( send_text_message_to_member(member, text) == 0 )
+					res = res || 1;
+			}
+
+			// release conference mutex
+			ast_mutex_unlock( &conf->lock );
+
+			break;
+		}
+	}
+
+	// release conference list mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return res;
+}
+
+// Creates a text frame and sends it to a given member
+// Returns 0 on success, -1 on failure
+int send_text_message_to_member(struct ast_conf_member *member, const char *text)
+{
+	struct ast_frame *f;
+
+	if ( member == NULL || text == NULL ) return -1;
+
+	if ( member->does_text )
+	{
+		f = create_text_frame(text, 1);
+		if ( f == NULL || queue_outgoing_text_frame(member, f) != 0) return -1;
+		ast_frfree(f);
+	}
+
+	return 0;
+}
+
+// Associates two members
+// Drives VAD-based video switching of dst_member from audio from src_member
+// This can be used when a member participates in a video conference but
+// talks using a telephone (simulcast) connection
+int drive(const char *conference, int src_member_id, int dst_member_id)
+{
+	int res;
+	struct ast_conference *conf;
+	struct ast_conf_member *member;
+	struct ast_conf_member *src;
+	struct ast_conf_member *dst;
+
+	if ( conference == NULL || src_member_id < 0 )
+		return -1;
+
+	res = 0;
+
+	// acquire conference list mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	for ( conf = conflist ; conf != NULL ; conf = conf->next )
+	{
+		if ( strcmp(conference, conf->name) == 0 )
+		{
+			// acquire conference mutex
+			ast_mutex_lock( &conf->lock );
+
+			src = NULL;
+			dst = NULL;
+			for ( member = conf->memberlist ; member != NULL ; member = member->next )
+			{
+				if ( member->id == src_member_id )
+					src = member;
+				if ( member->id == dst_member_id )
+					dst = member;
+			}
+			if ( src != NULL )
+			{
+				// acquire member mutex
+				ast_mutex_lock(&src->lock);
+
+				if ( dst != NULL )
+				{
+					src->driven_member = dst;
+					// Make sure the driven member's speaker count is correct
+					if ( src->speaking_state == 1 )
+						increment_speaker_count(src->driven_member, 1);
+					res = 1;
+				} else
+				{
+					if ( dst_member_id < 0 )
+					{
+						// Make sure the driven member's speaker count is correct
+						if ( src->speaking_state == 1 )
+							decrement_speaker_count(src->driven_member, 1);
+						src->driven_member = NULL;
+						res = 1;
+					}
+				}
+
+				// release member mutex
+				ast_mutex_unlock(&src->lock);
+			}
+
+			// release conference mutex
+			ast_mutex_unlock( &conf->lock );
+
+			break;
+		}
+	}
+
+	// release conference list mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return res;
+}
+
+// Associates two channels
+// Drives VAD-based video switching of dst_channel from audio from src_channel
+// This can be used when a member participates in a video conference but
+// talks using a telephone (simulcast) connection
+int drive_channel(const char *conference, const char *src_channel, const char *dst_channel)
+{
+	int res;
+	struct ast_conference *conf;
+	struct ast_conf_member *member;
+	struct ast_conf_member *src;
+	struct ast_conf_member *dst;
+
+	if ( conference == NULL || src_channel == NULL )
+		return -1;
+
+	res = 0;
+
+	// acquire conference list mutex
+	ast_mutex_lock( &conflist_lock ) ;
+
+	for ( conf = conflist ; conf != NULL ; conf = conf->next )
+	{
+		if ( strcmp(conference, conf->name) == 0 )
+		{
+			// acquire conference mutex
+			ast_mutex_lock( &conf->lock );
+
+			src = NULL;
+			dst = NULL;
+			for ( member = conf->memberlist ; member != NULL ; member = member->next )
+			{
+				if ( strcmp(src_channel, member->channel_name) == 0 )
+					src = member;
+				if ( dst_channel != NULL && strcmp(dst_channel, member->channel_name) == 0 )
+					dst = member;
+			}
+			if ( src != NULL )
+			{
+				// acquire member mutex
+				ast_mutex_lock(&src->lock);
+
+				if ( dst != NULL )
+				{
+					src->driven_member = dst;
+					// Make sure the driven member's speaker count is correct
+					if ( src->speaking_state == 1 )
+						increment_speaker_count(src->driven_member, 1);
+					res = 1;
+				} else
+				{
+					if ( dst_channel == NULL )
+					{
+						// Make sure the driven member's speaker count is correct
+						if ( src->speaking_state == 1 )
+							decrement_speaker_count(src->driven_member, 1);
+						src->driven_member = NULL;
+						res = 1;
+					}
+				}
+
+				// release member mutex
+				ast_mutex_unlock(&src->lock);
+			}
+
+			// release conference mutex
+			ast_mutex_unlock( &conf->lock );
+
+			break;
+		}
+	}
+
+	// release conference list mutex
+	ast_mutex_unlock( &conflist_lock ) ;
+
+	return res;
+}
+
+// Switches video source
+// Sends a manager event as well as
+// a text message notifying members of a video switch
+// The notification is sent to the current member and to the new member
+// The function locks the conference mutex as required
+void do_video_switching(struct ast_conference *conf, int new_id, int lock)
+{
+	struct ast_conf_member *member;
+	struct ast_conf_member *new_member = NULL;
+
+	if ( conf == NULL ) return;
+
+	if ( lock )
+	{
+		// acquire conference mutex
+		ast_mutex_lock( &conf->lock );
+	}
+
+	//fprintf(stderr, "Mihai: video switch from %d to %d\n", conf->current_video_source_id, new_id);
+
+	// No need to do anything if the current member is the same as the new member
+	if ( new_id != conf->current_video_source_id )
+	{
+		for ( member = conf->memberlist ; member != NULL ; member = member->next )
+		{
+			if ( member->id == conf->current_video_source_id )
+			{
+				send_text_message_to_member(member, AST_CONF_CONTROL_STOP_VIDEO);
+			}
+			if ( member->id == new_id )
+			{
+				send_text_message_to_member(member, AST_CONF_CONTROL_START_VIDEO);
+				new_member = member;
+			}
+		}
+
+		conf->current_video_source_id = new_id;
+
+		if ( new_member != NULL )
+		{
+			manager_event(EVENT_FLAG_CALL,
+				"ConferenceVideoSwitch",
+				"ConferenceName: %s\r\nChannel: %s\r\n",
+				conf->name,
+				new_member->channel_name);
+		} else
+		{
+			manager_event(EVENT_FLAG_CALL,
+				"ConferenceVideoSwitch",
+				"ConferenceName: %s\r\nChannel: empty\r\n",
+				conf->name);
+		}
+	}
+
+	if ( lock )
+	{
+		// release conference mutex
+		ast_mutex_unlock( &conf->lock );
+	}
+}
+
+int basic_play_sound(struct ast_conf_member *member, const char *file, int mute)
+{
+	struct ast_conf_soundq *newsound;
+	struct ast_conf_soundq **q;
+
+	newsound = calloc(1, sizeof(struct ast_conf_soundq));
+	newsound->stream = ast_openstream(member->chan, file, NULL);
+	if( !newsound->stream ) 
+	{
+		free(newsound);
+		ast_mutex_unlock(&member->lock);
+		return 0;
+	}
+	member->chan->stream = NULL;
+
+	newsound->muted = mute;
+	ast_copy_string(newsound->name, file, sizeof(newsound->name));
+
+	// append sound to the end of the list.
+	for ( q=&member->soundq; *q; q = &((*q)->next) ) ;
+	*q = newsound;
+
+	ast_mutex_unlock(&member->lock);
+
+	return 1 ;
+}
+
+int play_sound_channel(int fd, const char *channel, const char *file, int mute)
+{
+	struct ast_conf_member *member;
+
+	member = find_member(channel, 1);
+	if( !member )
+	{
+		ast_cli(fd, "Member %s not found\n", channel);
+		return 0;
+	}
+
+	if (! basic_play_sound(member, file, mute))
+	{
+		ast_cli(fd, "Sound %s not found\n", file);
+		return 0;
+	}
+
+	ast_cli(fd, "Playing sound %s to member %s %s\n",
+		      file, channel, mute ? "with mute" : "");
+
+	return 1 ;
+}
+
+int stop_sound_channel(int fd, const char *channel)
+{
+	struct ast_conf_member *member;
+	struct ast_conf_soundq *sound;
+	struct ast_conf_soundq *next;
+
+	member = find_member(channel, 1);
+	if ( !member )
+	{
+		ast_cli(fd, "Member %s not found\n", channel);
+		return 0;
+	}
+
+	// clear all sounds
+	sound = member->soundq;
+	member->soundq = NULL;
+
+	while ( sound )
+	{
+		next = sound->next;
+		ast_closestream(sound->stream);
+		free(sound);
+		sound = next;
+	}
+
+	// reset write format, since we're done playing the sound
+	if ( ast_set_write_format( member->chan, member->write_format ) < 0 )
+	{
+		ast_log( LOG_ERROR, "unable to set write format to %d\n",
+		    member->write_format ) ;
+	}
+
+	ast_mutex_unlock(&member->lock);
+
+	ast_cli( fd, "Stopped sounds to member %s\n", channel);
+	return 1;
+}
diff --git a/apps/conference/conference.h b/apps/conference/conference.h
new file mode 100644
index 0000000..0f7e248
--- /dev/null
+++ b/apps/conference/conference.h
@@ -0,0 +1,194 @@
+
+// $Id: conference.h 884 2007-06-27 14:56:21Z sbalea $
+
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <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 basic_play_sound(struct ast_conf_member *member, const char *file, int mute);
+int play_sound_channel(int fd, const char *channel, const char *file, int mute);
+int stop_sound_channel(int fd, const char *channel);
+
+int set_conference_debugging( const char* name, int state ) ;
+
+#endif
diff --git a/apps/conference/frame.c b/apps/conference/frame.c
new file mode 100644
index 0000000..24e04bd
--- /dev/null
+++ b/apps/conference/frame.c
@@ -0,0 +1,679 @@
+
+// $Id: frame.c 751 2006-12-11 22:08:45Z sbalea $
+
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <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/apps/conference/frame.h b/apps/conference/frame.h
new file mode 100644
index 0000000..2b77805
--- /dev/null
+++ b/apps/conference/frame.h
@@ -0,0 +1,75 @@
+
+// $Id: frame.h 746 2006-12-11 20:12:12Z sbalea $
+
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <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/apps/conference/member.c b/apps/conference/member.c
new file mode 100644
index 0000000..d7f98af
--- /dev/null
+++ b/apps/conference/member.c
@@ -0,0 +1,3332 @@
+
+// $Id: member.c 885 2007-06-27 15:41:18Z sbalea $
+
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <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 <stdio.h>
+#include "asterisk/channel.h"
+#include "asterisk/app.h"
+#include "asterisk/autoconfig.h"
+#include "member.h"
+
+
+// process an incoming frame.  Returns 0 normally, 1 if hangup was received.
+static int process_incoming(struct ast_conf_member *member, struct ast_conference *conf, struct ast_frame *f)
+{
+	int silent_frame = 0;
+	struct ast_conf_member *src_member ;
+
+	// In Asterisk 1.4 AST_FRAME_DTMF is equivalent to AST_FRAME_DTMF_END
+	if (f->frametype == AST_FRAME_DTMF)
+	{
+		if (member->dtmf_switch)
+		{
+			ast_mutex_lock( &member->lock ) ;
+			switch (f->subclass) {
+			case '0' :member->req_id=0;
+				break;
+			case '1' :member->req_id=1;
+				break;
+			case '2' :member->req_id=2;
+				break;
+			case '3' :member->req_id=3;
+				break;
+			case '4' :member->req_id=4;
+				break;
+			case '5' :member->req_id=5;
+				break;
+			case '6' :member->req_id=6;
+				break;
+			case '7' :member->req_id=7;
+				break;
+			case '8' :member->req_id=8;
+				break;
+			case '9' :member->req_id=9;
+				break;
+			case '*' :
+				if (member->mute_video == 0 && member->mute_audio == 0)
+				{
+					member->mute_video = 1;
+					member->mute_audio = 1;
+				}
+				else if (member->mute_video == 1 && member->mute_audio == 1)
+				{
+					member->mute_video = 0;
+					member->mute_audio = 0;
+				}
+				break;
+			}
+			member->conference = 1; // switch me
+			ast_mutex_unlock( &member->lock ) ;
+		}
+		if (member->dtmf_relay)
+		{
+			// output to manager...
+			manager_event(
+				EVENT_FLAG_CALL,
+				"ConferenceDTMF",
+				"ConferenceName: %s\r\n"
+				"Channel: %s\r\n"
+				"CallerID: %s\r\n"
+				"CallerIDName: %s\r\n"
+				"Key: %c\r\n",
+				conf->name,
+				member->channel_name,
+				member->chan->cid.cid_num ? member->chan->cid.cid_num : "unknown",
+				member->chan->cid.cid_name ? member->chan->cid.cid_name : "unknown",
+				f->subclass
+				) ;
+
+		}
+		if (!member->dtmf_switch && !member->dtmf_relay)
+		{
+			// relay this to the listening channels
+			queue_incoming_dtmf_frame( member, f );
+		}
+	} else if (f->frametype == AST_FRAME_DTMF_BEGIN)
+	{
+		if (!member->dtmf_switch && !member->dtmf_relay)
+		{
+			// relay this to the listening channels
+			queue_incoming_dtmf_frame( member, f );
+		}
+	}
+
+	ast_mutex_lock( &member->lock ) ;
+	// Handle a local or remote conference
+	if (member->conference)
+	{
+		int req_id = member->req_id;
+		ast_mutex_unlock( &member->lock );
+		// this will return NULL or a locked member
+		src_member = check_active_video(req_id,conf);
+		// Stream a picture to the recipient if no active video
+		if (!src_member)
+		{
+			// Mihai: we don't want to send video here, we cannot negotiate codec
+			// and we don't know what codec the conference is using
+			//if (member->norecv_video == 0)
+			//{
+			//	if(!ast_streamfile(member->chan,"novideo",member->chan->language))
+			//	{
+			//		ast_waitstream(member->chan,"");
+			//	}
+			//}
+		}
+		else
+		{
+			// Send a FIR to the new sender
+			ast_indicate(src_member->chan,AST_CONTROL_VIDUPDATE);
+			// we will have locked in check_active_video()
+			ast_mutex_unlock( &src_member->lock);
+		}
+		ast_mutex_lock( &member->lock );
+		member->conference = 0;
+	}
+	ast_mutex_unlock( &member->lock );
+
+
+	if ((f->frametype == AST_FRAME_VOICE && member->mute_audio == 1) || (f->frametype == AST_FRAME_VIDEO && member->mute_video == 1))
+	{
+		// this is a listen-only user, ignore the frame
+		//ast_log( AST_CONF_DEBUG, "Listen only user frame");
+		ast_frfree( f ) ;
+		f = NULL ;
+	}
+	else if ( f->frametype == AST_FRAME_VOICE )
+	{	//ast_log( AST_CONF_DEBUG, "Got voice frame");
+		// reset silence detection flag
+		silent_frame = 0 ;
+
+		// accounting: count the incoming frame
+		member->frames_in++ ;
+
+#if ( SILDET == 2 )
+		//
+		// make sure we have a valid dsp and frame type
+		//
+		if (
+			member->dsp != NULL
+			&& f->subclass == AST_FORMAT_SLINEAR
+			&& f->datalen == AST_CONF_FRAME_DATA_SIZE
+			)
+		{
+			// send the frame to the preprocessor
+			int spx_ret;
+			spx_ret = speex_preprocess( member->dsp, f->data, NULL );
+#ifdef DEBUG_USE_TIMELOG
+			TIMELOG(spx_ret, 3, "speex_preprocess");
+#endif
+			if ( spx_ret == 0 )
+			{
+				//
+				// we ignore the preprocessor's outcome if we've seen voice frames
+				// in within the last AST_CONF_SKIP_SPEEX_PREPROCESS frames
+				//
+				if ( member->ignore_speex_count > 0 )
+				{
+					// ast_log( AST_CONF_DEBUG, "ignore_speex_count => %d\n", ignore_speex_count ) ;
+
+					// skip speex_preprocess(), and decrement counter
+					--member->ignore_speex_count ;
+				}
+				else
+				{
+					// set silent_frame flag
+					silent_frame = 1 ;
+				}
+			}
+			else
+			{
+				// voice detected, reset skip count
+				member->ignore_speex_count = AST_CONF_SKIP_SPEEX_PREPROCESS ;
+			}
+		}
+#endif
+		if ( !silent_frame )
+			queue_incoming_frame( member, f );
+
+		// free the original frame
+		ast_frfree( f ) ;
+		f = NULL ;
+
+	}
+	else if (f->frametype == AST_FRAME_VIDEO)
+	{
+		queue_incoming_video_frame( member, f );
+
+		// free the original frame
+		ast_frfree( f ) ;
+		f = NULL ;
+
+	}
+	else if (
+		f->frametype == AST_FRAME_CONTROL
+		&& f->subclass == AST_CONTROL_HANGUP
+		)
+	{
+		// hangup received
+
+		// free the frame
+		ast_frfree( f ) ;
+		f = NULL ;
+
+		// break out of the while ( 42 == 42 )
+		return 1;
+	}
+	else if (
+		f->frametype == AST_FRAME_CONTROL
+		&& f->subclass == AST_CONTROL_VIDUPDATE
+		)
+	{
+		// say we have switched to cause a FIR to
+		// be sent to the sender
+		ast_mutex_lock( &member->lock ) ;
+		member->conference = 1;
+		ast_mutex_unlock( &member->lock ) ;
+
+		// free the original frame
+		ast_frfree( f ) ;
+		f = NULL ;
+	}
+	else if ( f->frametype == AST_FRAME_TEXT  && member->does_text )
+	{
+		if ( strncmp(f->data, AST_CONF_CONTROL_CAMERA_DISABLED, strlen(AST_CONF_CONTROL_CAMERA_DISABLED)) == 0 )
+		{
+			ast_mutex_lock(&member->lock);
+			manager_event(EVENT_FLAG_CALL,
+			              "ConferenceCameraDisabled",
+			              "ConferenceName: %s\r\nChannel: %s\r\n",
+			              conf->name,
+			              member->channel_name);
+			member->no_camera = 1;
+			ast_mutex_unlock(&member->lock);
+		} else if ( strncmp(f->data, AST_CONF_CONTROL_CAMERA_ENABLED, strlen(AST_CONF_CONTROL_CAMERA_ENABLED)) == 0 )
+		{
+			ast_mutex_lock(&member->lock);
+			manager_event(EVENT_FLAG_CALL,
+			              "ConferenceCameraEnabled",
+			              "ConferenceName: %s\r\nChannel: %s\r\n",
+			              conf->name,
+			              member->channel_name);
+			member->no_camera = 0;
+			ast_mutex_unlock(&member->lock);
+		} else if ( strncmp(f->data, AST_CONF_CONTROL_STOP_VIDEO_TRANSMIT, strlen(AST_CONF_CONTROL_STOP_VIDEO_TRANSMIT)) == 0 )
+		{
+			ast_mutex_lock(&member->lock);
+			manager_event(EVENT_FLAG_CALL,
+			              "ConferenceStopVideoTransmit",
+			              "ConferenceName: %s\r\nChannel: %s\r\n",
+			              conf->name,
+			              member->channel_name);
+			member->norecv_video = 1;
+			ast_mutex_unlock(&member->lock);
+		} else if ( strncmp(f->data, AST_CONF_CONTROL_START_VIDEO_TRANSMIT, strlen(AST_CONF_CONTROL_START_VIDEO_TRANSMIT)) == 0 )
+		{
+			ast_mutex_lock(&member->lock);
+			manager_event(EVENT_FLAG_CALL,
+			              "ConferenceStartVideoTransmit",
+			              "ConferenceName: %s\r\nChannel: %s\r\n",
+			              conf->name,
+			              member->channel_name);
+			member->norecv_video = 0;
+			ast_mutex_unlock(&member->lock);
+		}
+		ast_frfree(f);
+		f = NULL;
+	} else
+	{
+		// undesirables
+		ast_frfree( f ) ;
+		f = NULL ;
+	}
+
+	return 0;
+}
+
+// get the next frame from the soundq;  must be called with member locked.
+static struct ast_frame *get_next_soundframe(struct ast_conf_member *member, struct ast_frame
+    *exampleframe) {
+    struct ast_frame *f;
+
+again:
+    f=ast_readframe(member->soundq->stream);
+
+    if(!f) { // we're done with this sound; remove it from the queue, and try again
+	struct ast_conf_soundq *toboot = member->soundq;
+
+	ast_closestream(toboot->stream);
+	member->soundq = toboot->next;
+
+	//ast_log( LOG_WARNING, "finished playing a sound, next = %x\n", member->soundq);
+	// notify applications via mgr interface that this sound has been played
+	manager_event(
+		EVENT_FLAG_CALL,
+		"ConferenceSoundComplete",
+		"Channel: %s\r\n"
+		"Sound: %s\r\n",
+		member->channel_name,
+		toboot->name
+	);
+
+	free(toboot);
+	if(member->soundq) goto again;
+
+	// if we get here, we've gotten to the end of the queue; reset write format
+	if ( ast_set_write_format( member->chan, member->write_format ) < 0 )
+	{
+		ast_log( LOG_ERROR, "unable to set write format to %d\n",
+		    member->write_format ) ;
+	}
+    } else {
+	// copy delivery from exampleframe
+	f->delivery = exampleframe->delivery;
+    }
+
+    return f;
+}
+
+
+// process outgoing frames for the channel, playing either normal conference audio,
+// or requested sounds
+static int process_outgoing(struct ast_conf_member *member)
+{
+	conf_frame* cf ; // frame read from the output queue
+	struct ast_frame *f;
+	struct ast_frame *realframe = NULL;
+
+	for(;;)
+	{
+		// acquire member mutex and grab a frame.
+		ast_mutex_lock( &member->lock ) ;
+		cf = get_outgoing_frame( member ) ;
+
+                // if there's no frames exit the loop.
+		if ( !cf )
+		{
+			ast_mutex_unlock( &member->lock ) ;
+			break;
+		}
+
+
+		f = cf->fr;
+
+		// if we're playing sounds, we can just replace the frame with the
+		// next sound frame, and send it instead
+		if ( member->soundq )
+		{
+			realframe = f;
+			f = get_next_soundframe(member, f);
+			if ( !f )
+			{
+				// if we didn't get anything, just revert to "normal"
+				f = realframe;
+				realframe = NULL;
+			} else
+			{
+				// We have a sound frame now, but we need to make sure it's the same
+				// format as our channel write format
+				int wf = member->chan->writeformat & AST_FORMAT_AUDIO_MASK;
+				if ( f->frametype == AST_FRAME_VOICE && !(wf & f->subclass) )
+				{
+					// We need to change our channel's write format
+					ast_set_write_format(member->chan, f->subclass);
+				}
+			}
+		}
+
+		ast_mutex_unlock(&member->lock);
+
+
+#ifdef DEBUG_FRAME_TIMESTAMPS
+		// !!! TESTING !!!
+		int delivery_diff = usecdiff( &f->delivery, &member->lastsent_timeval ) ;
+		if ( delivery_diff != AST_CONF_FRAME_INTERVAL )
+		{
+			ast_log( AST_CONF_DEBUG, "unanticipated delivery time, delivery_diff => %d, delivery.tv_usec => %ld\n",
+				 delivery_diff, f->delivery.tv_usec ) ;
+		}
+
+		// !!! TESTING !!!
+		if (
+			f->delivery.tv_sec < member->lastsent_timeval.tv_sec
+			|| (
+				f->delivery.tv_sec == member->lastsent_timeval.tv_sec
+				&& f->delivery.tv_usec <= member->lastsent_timeval.tv_usec
+				)
+			)
+		{
+			ast_log( LOG_WARNING, "queued frame timestamped in the past, %ld.%ld <= %ld.%ld\n",
+				 f->delivery.tv_sec, f->delivery.tv_usec,
+				 member->lastsent_timeval.tv_sec, member->lastsent_timeval.tv_usec ) ;
+		}
+		member->lastsent_timeval = f->delivery ;
+#endif
+
+#ifdef DEBUG_USE_TIMELOG
+		TIMELOG( ast_write( member->chan, f ), 10, "member: ast_write");
+#else
+
+		// send the voice frame
+		if ( ast_write( member->chan, f ) == 0 )
+		{
+			struct timeval tv = ast_tvnow();
+			ast_log( AST_CONF_DEBUG, "SENT VOICE FRAME, channel => %s, frames_out => %ld, s => %ld, ms => %ld\n",
+				 member->channel_name, member->frames_out, tv.tv_sec, tv.tv_usec ) ;
+		}
+		else
+		{
+			// log 'dropped' outgoing frame
+			ast_log( LOG_ERROR, "unable to write voice frame to channel, channel => %s\n", member->channel_name ) ;
+
+			// accounting: count dropped outgoing frames
+			member->frames_out_dropped++ ;
+		}
+#endif
+		// clean up frame
+		delete_conf_frame( cf ) ;
+
+	}
+
+	// Do the same for video, suck it dry
+	for(;;)
+	{
+		// grab a frame.
+		cf = get_outgoing_video_frame( member ) ;
+
+                // if there's no frames exit the loop.
+		if(!cf){
+			break;
+		}
+
+		f = cf->fr;
+
+		// send the video frame
+		if ( ast_write_video( member->chan, f ) == 1 )
+		{
+			struct timeval tv = ast_tvnow();
+			ast_log( AST_CONF_DEBUG, "SENT VIDEO FRAME, channel => %s, frames_out => %ld, s => %ld, ms => %ld\n",
+				 member->channel_name, member->frames_out, tv.tv_sec, tv.tv_usec ) ;
+		}
+		else
+		{
+			// log 'dropped' outgoing frame
+			ast_log( AST_CONF_DEBUG, "unable to write video frame to channel, channel => %s\n", member->channel_name ) ;
+
+			// accounting: count dropped outgoing frames
+			member->video_frames_out_dropped++ ;
+		}
+
+		// clean up frame
+		delete_conf_frame( cf ) ;
+
+	}
+
+        // Do the same for dtmf, suck it dry
+	for(;;)
+	{
+		// acquire member mutex and grab a frame.
+		cf = get_outgoing_dtmf_frame( member ) ;
+
+		// if there's no frames exit the loop.
+		if(!cf) break;
+
+		// send the dtmf frame
+		if ( ast_write( member->chan, cf->fr ) == 0 )
+		{
+			struct timeval tv = ast_tvnow();
+			ast_log( AST_CONF_DEBUG, "SENT DTMF FRAME, channel => %s, frames_out => %ld, s => %ld, ms => %ld\n",
+				 member->channel_name, member->frames_out, tv.tv_sec, tv.tv_usec ) ;
+
+		}
+		else
+		{
+			// log 'dropped' outgoing frame
+			ast_log( AST_CONF_DEBUG, "unable to write dtmf frame to channel, channel => %s\n", member->channel_name ) ;
+
+			// accounting: count dropped outgoing frames
+			member->dtmf_frames_out_dropped++ ;
+		}
+
+		// clean up frame
+		delete_conf_frame( cf ) ;
+	}
+
+        // Do the same for text, hell, why not?
+	for(;;)
+	{
+		// acquire member mutex and grab a frame.
+		cf = get_outgoing_text_frame( member ) ;
+
+		// if there's no frames exit the loop.
+		if(!cf) break;
+
+		// send the text frame
+		if ( ast_write( member->chan, cf->fr ) == 0 )
+		{
+			struct timeval tv = ast_tvnow();
+			ast_log( AST_CONF_DEBUG, "SENT TEXT FRAME, channel => %s, frames_out => %ld, s => %ld, ms => %ld\n",
+				 member->channel_name, member->frames_out, tv.tv_sec, tv.tv_usec ) ;
+
+		}
+		else
+		{
+			// log 'dropped' outgoing frame
+			ast_log( AST_CONF_DEBUG, "unable to write text frame to channel, channel => %s\n", member->channel_name ) ;
+
+			// accounting: count dropped outgoing frames
+			member->text_frames_out_dropped++ ;
+		}
+
+		// clean up frame
+		delete_conf_frame( cf ) ;
+	}
+
+
+	return 0;
+}
+
+static int member_checkkick( struct ast_conf_member *member )
+{
+	int kick;
+	ast_mutex_lock( &member->lock ) ;
+	kick = member->kick_flag;
+	ast_mutex_unlock( &member->lock ) ;
+	return kick;
+}
+
+//
+// main member thread function
+//
+
+int member_exec( struct ast_channel* chan, void* data )
+{
+//	struct timeval start, end ;
+//	start = ast_tvnow();
+
+	struct ast_conference *conf ;
+	struct ast_conf_member *member ;
+
+	struct ast_frame *f ; // frame received from ast_read()
+
+	int left = 0 ;
+	int res;
+
+	ast_log( AST_CONF_DEBUG, "Begin processing member thread, channel => %s\n", chan->name ) ;
+
+	//
+	// If the call has not yet been answered, answer the call
+	// Note: asterisk apps seem to check _state, but it seems like it's safe
+	// to just call ast_answer.  It will just do nothing if it is up.
+	// it will also return -1 if the channel is a zombie, or has hung up.
+	//
+
+	res = ast_answer( chan ) ;
+	if ( res )
+	{
+		ast_log( LOG_ERROR, "unable to answer call\n" ) ;
+		return -1 ;
+	}
+
+	//
+	// create a new member for the conference
+ 	//
+
+//	ast_log( AST_CONF_DEBUG, "creating new member, id => %s, flags => %s, p => %s\n",
+//		id, flags, priority ) ;
+
+	member = create_member( chan, (const char*)( data ) ) ; // flags, atoi( priority ) ) ;
+
+	// unable to create member, return an error
+	if ( member == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to create member\n" ) ;
+		return -1 ;
+	}
+
+	//
+	// setup asterisk read/write formats
+	//
+#if 0
+	ast_log( AST_CONF_DEBUG, "CHANNEL INFO, CHANNEL => %s, DNID => %s, CALLER_ID => %s, ANI => %s\n",
+		chan->name, chan->dnid, chan->callerid, chan->ani ) ;
+
+	ast_log( AST_CONF_DEBUG, "CHANNEL CODECS, CHANNEL => %s, NATIVE => %d, READ => %d, WRITE => %d\n",
+		chan->name, chan->nativeformats, member->read_format, member->write_format ) ;
+#endif
+	if ( ast_set_read_format( chan, member->read_format ) < 0 )
+	{
+		ast_log( LOG_ERROR, "unable to set read format to signed linear\n" ) ;
+		delete_member( member ) ;
+		return -1 ;
+	}
+
+	if ( ast_set_write_format( chan, member->write_format ) < 0 ) // AST_FORMAT_SLINEAR, chan->nativeformats
+	{
+		ast_log( LOG_ERROR, "unable to set write format to signed linear\n" ) ;
+		delete_member( member ) ;
+		return -1 ;
+	}
+
+	//
+	// setup a conference for the new member
+	//
+
+	conf = start_conference( member ) ;
+
+	if ( conf == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to setup member conference\n" ) ;
+		delete_member( member) ;
+		return -1 ;
+	}
+
+
+	manager_event(
+		EVENT_FLAG_CALL,
+		"ConferenceJoin",
+		"ConferenceName: %s\r\n"
+		"Member: %d\r\n"
+		"Channel: %s\r\n"
+		"CallerID: %s\r\n"
+		"CallerIDName: %s\r\n"
+		"Count: %d\r\n",
+		conf->name,
+		member->id,
+		member->channel_name,
+		member->chan->cid.cid_num ? member->chan->cid.cid_num : "unknown",
+		member->chan->cid.cid_name ? member->chan->cid.cid_name: "unknown",
+		conf->membercount
+	) ;
+
+	// Store the CID information
+	if ( member->chan->cid.cid_num )
+	{
+		if ( (member->callerid = malloc(strlen(member->chan->cid.cid_num)+1)) )
+			memcpy(member->callerid,member->chan->cid.cid_num, strlen(member->chan->cid.cid_num)+1);
+	} else
+		member->callerid = NULL;
+
+	if ( member->chan->cid.cid_name )
+	{
+		if ( (member->callername = malloc(strlen(member->chan->cid.cid_name)+1)) )
+			memcpy(member->callername, member->chan->cid.cid_name, strlen(member->chan->cid.cid_name)+1);
+	} else
+		member->callername = NULL;
+
+
+	//
+	// process loop for new member ( this runs in it's own thread )
+	//
+
+	ast_log( AST_CONF_DEBUG, "begin member event loop, channel => %s\n", chan->name ) ;
+
+	// timer timestamps
+	struct timeval base, curr ;
+	base = ast_tvnow();
+
+	// tell conference_exec we're ready for frames
+	member->ready_for_outgoing = 1 ;
+
+	// notify others about enter
+	char * enter_snd = member->enter_snd;
+	if (strcmp(enter_snd, "-")!=0)
+	{
+		// lock conference
+		ast_mutex_lock( &conf->lock );
+		
+		/*if (conf->membercount < 2)
+		  {
+		  enter_snd = "conf-onlyperson";
+		  }*/
+
+		struct ast_conf_member *membertest = conf->memberlist;
+		while (membertest != NULL)
+		{
+			// lock member for basic_play_sound
+			ast_mutex_lock(&membertest->lock);
+
+			// basic_play_sound unlock member automatically. do not mute on enter message
+			if (!basic_play_sound ( membertest, enter_snd, 0 ))
+			{
+				ast_log(LOG_ERROR, "playing conference[%d] entry message <%s> FAILED on <%s>\n", conf->membercount, enter_snd, membertest->channel_name);
+			}
+			else
+			{
+				ast_log(LOG_NOTICE, "playing conference[%d] entry message <%s> on <%s>\n", conf->membercount, enter_snd, membertest->channel_name);
+			}
+			
+			membertest = membertest->next;
+		}
+
+		// unlock conference
+		ast_mutex_unlock( &conf->lock );
+	}
+
+	while ( 42 == 42 )
+	{
+		// make sure we have a channel to process
+		if ( chan == NULL )
+		{
+			ast_log( LOG_NOTICE, "member channel has closed\n" ) ;
+			break ;
+		}
+
+		//-----------------//
+		// INCOMING FRAMES //
+		//-----------------//
+
+		// wait for an event on this channel
+		left = ast_waitfor( chan, AST_CONF_WAITFOR_LATENCY ) ;
+
+		//ast_log( AST_CONF_DEBUG, "received event on channel, name => %s, left => %d\n", chan->name, left ) ;
+
+		if ( left < 0 )
+		{
+			// an error occured
+			ast_log(
+				LOG_NOTICE,
+				"an error occured waiting for a frame, channel => %s, error => %d\n",
+				chan->name, left
+			) ;
+			break; // out of the 42==42
+		}
+		else if ( left == 0 )
+		{
+			// no frame has arrived yet
+			// ast_log( LOG_NOTICE, "no frame available from channel, channel => %s\n", chan->name ) ;
+		}
+		else if ( left > 0 )
+		{
+			// a frame has come in before the latency timeout
+			// was reached, so we process the frame
+
+			f = ast_read( chan ) ;
+
+			if ( f == NULL )
+			{
+				if (conf->debug_flag)
+				{
+					ast_log( LOG_NOTICE, "unable to read from channel, channel => %s\n", chan->name ) ;
+				// They probably want to hangup...
+				}
+				break ;
+			}
+
+			// actually process the frame: break if we got hangup.
+			if(process_incoming(member, conf, f)) break;
+
+		}
+
+		if (member_checkkick(member)) break;
+
+		//-----------------//
+		// OUTGOING FRAMES //
+		//-----------------//
+
+		// update the current timestamps
+		curr = ast_tvnow();
+
+		process_outgoing(member);
+		// back to process incoming frames
+		continue ;
+	}
+
+	ast_log( AST_CONF_DEBUG, "end member event loop, time_entered => %ld\n", member->time_entered.tv_sec ) ;
+
+	//
+	// clean up
+	//
+
+#ifdef DEBUG_OUTPUT_PCM
+	// !!! TESTING !!!
+	if ( incoming_fh != NULL )
+		fclose( incoming_fh ) ;
+#endif
+
+	// If we're driving another member, make sure its speaker count is correct
+	if ( member != NULL ) member->remove_flag = 1 ;
+
+//	end = ast_tvnow();
+//	int expected_frames = ( int )( floor( (double)( msecdiff( &end, &start ) / AST_CONF_FRAME_INTERVAL ) ) ) ;
+//	ast_log( AST_CONF_DEBUG, "expected_frames => %d\n", expected_frames ) ;
+
+	return 0 ;
+}
+
+
+
+struct ast_conf_member *check_active_video( int id, struct ast_conference *conf )
+{
+     struct ast_conf_member *member;
+
+     // acquire the conference lock
+     ast_mutex_lock( &conf->lock ) ;
+
+     member = conf->memberlist;
+     while (member)
+     {
+	     if (member->id == id)
+	     {
+		     // lock this member
+		     ast_mutex_lock( &member->lock ) ;
+	      	     ast_mutex_unlock( &conf->lock ) ;
+		     return member;
+	     }
+	     member = member->next;
+     }
+     ast_mutex_unlock( &conf->lock ) ;
+     return NULL;
+}
+
+//
+// manange member functions
+//
+
+struct ast_conf_member* create_member( struct ast_channel *chan, const char* data )
+{
+	char *parse;
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(conf_name);
+		AST_APP_ARG(flags);
+		AST_APP_ARG(enter_snd);
+		AST_APP_ARG(leave_snd);
+		AST_APP_ARG(priority);
+		AST_APP_ARG(vad_prob_start);
+		AST_APP_ARG(vad_prob_continue);
+	);
+
+	//
+	// check input
+	//
+
+	if ( chan == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to create member with null channel\n" ) ;
+		return NULL ;
+	}
+
+	if ( chan->name == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to create member with null channel name\n" ) ;
+		return NULL ;
+	}
+
+        if (ast_strlen_zero(data)) {
+                ast_log(LOG_ERROR, "Conference requires an argument (conf_name[|flags[|enter_snd[|leave_snd[|priority[vad_prob_start[|vad_prob_continue]]]]]]))\n");
+                return NULL;
+        }
+
+	//
+	// allocate memory for new conference member
+	//
+
+	struct ast_conf_member *member = calloc( 1,  sizeof( struct ast_conf_member ) ) ;
+
+	if ( member == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to malloc ast_conf_member\n" ) ;
+		return NULL ;
+	}
+
+	// initialize mutex
+	ast_mutex_init( &member->lock ) ;
+
+	//
+	// initialize member with passed data values
+	//
+	
+	parse = ast_strdupa(data);
+
+	AST_STANDARD_APP_ARGS(args, parse);
+
+	// parse the id
+	if (ast_strlen_zero(args.conf_name))
+	{
+		ast_log( LOG_ERROR, "unable to parse member id\n" ) ;
+		free( member ) ;
+		return NULL ;
+	}
+	else
+		member->conf_name = ast_strdup(args.conf_name);
+
+	// parse the flags
+	if (!ast_strlen_zero(args.flags))
+		member->flags = ast_strdup(args.flags);
+	else
+		member->flags = ast_strdup("");
+	
+	// parse enter sound
+	if (!ast_strlen_zero(args.enter_snd))
+		member->enter_snd = ast_strdup(args.enter_snd);
+	else
+		member->enter_snd = ast_strdup("enter");
+
+	// parse leave sound
+	if (!ast_strlen_zero(args.leave_snd))
+		member->leave_snd = ast_strdup(args.leave_snd);
+	else
+		member->leave_snd = ast_strdup("leave");
+
+	if (!ast_strlen_zero(args.priority))
+	// parse the priority
+		member->priority = atoi(args.priority);
+	else
+		member->priority = 0;
+
+	if (!ast_strlen_zero(args.vad_prob_start))
+		member->vad_prob_start = atof(args.vad_prob_start);
+	else
+		member->vad_prob_start = AST_CONF_PROB_START;
+
+	if (!ast_strlen_zero(args.vad_prob_continue))
+		member->vad_prob_continue = atof(args.vad_prob_continue);
+	else
+		member->vad_prob_continue = AST_CONF_PROB_CONTINUE;
+
+	// debugging
+	ast_log(
+		AST_CONF_DEBUG,
+		"parsed data params, id => %s, flags => %s, priority => %d, vad_prob_start => %f, vad_prob_continue => %f\n",
+		member->conf_name, member->flags, member->priority, member->vad_prob_start, member->vad_prob_continue
+	) ;
+
+	//
+	// initialize member with default values
+	//
+
+	// keep pointer to member's channel
+	member->chan = chan ;
+
+	// copy the channel name
+	member->channel_name = malloc( strlen( chan->name ) + 1 ) ;
+	strcpy( member->channel_name, chan->name ) ;
+
+	// ( default can be overridden by passed flags )
+	member->mute_audio = 0;
+	member->mute_video = 0;
+	member->norecv_audio = 0;
+	member->norecv_video = 0;
+	member->no_camera = 0;
+
+	// moderator?
+	member->ismoderator = 0;
+
+	// ready flag
+	member->ready_for_outgoing = 0 ;
+
+	// incoming frame queue
+	member->inFrames = NULL ;
+	member->inFramesTail = NULL ;
+	member->inFramesCount = 0 ;
+
+	member->inVideoFrames = NULL ;
+	member->inVideoFramesTail = NULL ;
+	member->inVideoFramesCount = 0 ;
+
+	member->inDTMFFrames = NULL ;
+	member->inDTMFFramesTail = NULL ;
+	member->inDTMFFramesCount = 0 ;
+
+	member->inTextFrames = NULL ;
+	member->inTextFramesTail = NULL ;
+	member->inTextFramesCount = 0 ;
+
+	member->conference = 1; // we have switched req_id
+	member->dtmf_switch = 0; // no dtmf switch by default
+	member->dtmf_relay = 0; // no dtmf relay by default
+
+	// start of day video ids
+	member->req_id = -1;
+	member->id = -1;
+
+	member->first_frame_received = 0; // cause a FIR after NAT delay
+
+	// last frame caching
+	member->inFramesRepeatLast = 0 ;
+	member->inFramesLast = NULL ;
+	member->okayToCacheLast = 0 ;
+
+	// outgoing frame queue
+	member->outFrames = NULL ;
+	member->outFramesTail = NULL ;
+	member->outFramesCount = 0 ;
+
+	member->outVideoFrames = NULL ;
+	member->outVideoFramesTail = NULL ;
+	member->outVideoFramesCount = 0 ;
+
+	member->outDTMFFrames = NULL ;
+	member->outDTMFFramesTail = NULL ;
+	member->outDTMFFramesCount = 0 ;
+
+	member->outTextFrames = NULL ;
+	member->outTextFramesTail = NULL ;
+	member->outTextFramesCount = 0 ;
+
+	// ( not currently used )
+	// member->samplesperframe = AST_CONF_BLOCK_SAMPLES ;
+
+	// used for determining need to mix frames
+	// and for management interface notification
+	// and for VAD based video switching
+	member->speaking_state_notify = 0 ;
+	member->speaking_state = 0 ;
+	member->local_speaking_state = 0;
+	member->speaker_count = 0;
+	member->driven_member = NULL;
+
+	// linked-list pointer
+	member->next = NULL ;
+
+	// account data
+	member->frames_in = 0 ;
+	member->frames_in_dropped = 0 ;
+	member->frames_out = 0 ;
+	member->frames_out_dropped = 0 ;
+	member->video_frames_in = 0 ;
+	member->video_frames_in_dropped = 0 ;
+	member->video_frames_out = 0 ;
+	member->video_frames_out_dropped = 0 ;
+	member->dtmf_frames_in = 0 ;
+	member->dtmf_frames_in_dropped = 0 ;
+	member->dtmf_frames_out = 0 ;
+	member->dtmf_frames_out_dropped = 0 ;
+	member->text_frames_in = 0 ;
+	member->text_frames_in_dropped = 0 ;
+	member->text_frames_out = 0 ;
+	member->text_frames_out_dropped = 0 ;
+
+	// for counting sequentially dropped frames
+	member->sequential_drops = 0 ;
+	member->since_dropped = 0 ;
+
+	// flags
+	member->remove_flag = 0 ;
+	member->kick_flag = 0;
+
+	// record start time
+	// init dropped frame timestamps
+	// init state change timestamp
+	member->time_entered =
+		member->last_in_dropped =
+		member->last_out_dropped =
+		member->last_state_change = ast_tvnow();
+
+	//
+	// parse passed flags
+	//
+
+	// silence detection flags w/ defaults
+	member->vad_flag = 0 ;
+	member->denoise_flag = 0 ;
+	member->agc_flag = 0 ;
+
+	// is this member using the telephone?
+	member->via_telephone = 0 ;
+
+	// temp pointer to flags string
+	char* flags = member->flags ;
+
+	int i;
+
+	for ( i = 0 ; i < strlen( flags ) ; ++i )
+	{
+
+		if (flags[i] >= (int)'0' && flags[i] <= (int)'9')
+		{
+			if (member->req_id < 0)
+			{
+				member->req_id = flags[i] - (int)'0';
+			}
+			else
+			{
+				int newid = flags[i] - (int)'0';
+				// need to boot anyone with this id already
+				// will happen in add_member
+				member->id = newid;
+			}
+		}
+		else
+		{
+			// allowed flags are C, c, L, l, V, D, A, C, X, R, T, t, M, S
+			// mute/no_recv options
+			switch ( flags[i] )
+			{
+			case 'C':
+				member->mute_video = 1;
+				break ;
+			case 'c':
+				member->norecv_video = 1;
+				break ;
+			case 'L':
+				member->mute_audio = 1;
+				break ;
+			case 'l':
+				member->norecv_audio = 1;
+				break;
+
+				// speex preprocessing options
+			case 'V':
+				member->vad_flag = 1 ;
+				break ;
+			case 'D':
+				member->denoise_flag = 1 ;
+				break ;
+			case 'A':
+				member->agc_flag = 1 ;
+				break ;
+
+				// dtmf/moderator/video switching options
+			case 'X':
+				member->dtmf_switch = 1;
+				break;
+			case 'R':
+				member->dtmf_relay = 1;
+				break;
+			case 'S':
+				member->vad_switch = 1;
+				break;
+			case 'M':
+				member->ismoderator = 1;
+				break;
+			case 'N':
+				member->no_camera = 1;
+				break;
+			case 't':
+				member->does_text = 1;
+				break;
+
+				//Telephone connection
+			case 'T':
+				member->via_telephone = 1;
+				break;
+
+			default:
+				ast_log( LOG_WARNING, "received invalid flag, chan => %s, flag => %c\n",
+					 chan->name, flags[i] );
+				break ;
+			}
+		}
+	}
+
+	// set the dsp to null so silence detection is disabled by default
+	member->dsp = NULL ;
+
+#if ( SILDET == 2 )
+	//
+	// configure silence detection and preprocessing
+	// if the user is coming in via the telephone,
+	// and is not listen-only
+	//
+	if (
+		member->via_telephone == 1
+		&& member->type != 'L'
+	)
+	{
+		// create a speex preprocessor
+		member->dsp = speex_preprocess_state_init( AST_CONF_BLOCK_SAMPLES, AST_CONF_SAMPLE_RATE ) ;
+
+		if ( member->dsp == NULL )
+		{
+			ast_log( LOG_WARNING, "unable to initialize member dsp, channel => %s\n", chan->name ) ;
+		}
+		else
+		{
+			ast_log( LOG_NOTICE, "member dsp initialized, channel => %s, v => %d, d => %d, a => %d\n",
+				chan->name, member->vad_flag, member->denoise_flag, member->agc_flag ) ;
+
+			// set speex preprocessor options
+			speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_VAD, &(member->vad_flag) ) ;
+			speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_DENOISE, &(member->denoise_flag) ) ;
+			speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_AGC, &(member->agc_flag) ) ;
+
+			speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_PROB_START, &member->vad_prob_start ) ;
+			speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_PROB_CONTINUE, &member->vad_prob_continue ) ;
+
+			speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_GET_PROB_START, &member->vad_prob_start ) ;
+			speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_GET_PROB_CONTINUE, &member->vad_prob_continue ) ;
+
+			ast_log( AST_CONF_DEBUG, "speech_prob_start => %f, speech_prob_continue => %f\n",
+				member->vad_prob_start, member->vad_prob_continue ) ;
+		}
+	}
+#endif
+
+	//
+	// set connection type
+	//
+
+	if ( member->via_telephone == 1 )
+	{
+		member->connection_type = 'T' ;
+	}
+	else if ( strncmp( member->channel_name, "SIP", 3 ) == 0 )
+	{
+		member->connection_type = 'S' ;
+	}
+	else // default to iax
+	{
+		member->connection_type = 'X' ;
+	}
+
+	//
+	// read, write, and translation options
+	//
+
+	// set member's audio formats, taking dsp preprocessing into account
+	// ( chan->nativeformats, AST_FORMAT_SLINEAR, AST_FORMAT_ULAW, AST_FORMAT_GSM )
+	member->read_format = ( member->dsp == NULL ) ? chan->nativeformats : AST_FORMAT_SLINEAR ;
+
+	member->write_format = chan->nativeformats;
+
+	// 1.2 or 1.3+
+#ifdef AST_FORMAT_AUDIO_MASK
+
+	member->read_format &= AST_FORMAT_AUDIO_MASK;
+	member->write_format &= AST_FORMAT_AUDIO_MASK;
+#endif
+
+	// translation paths ( ast_translator_build_path() returns null if formats match )
+	member->to_slinear = ast_translator_build_path( AST_FORMAT_SLINEAR, member->read_format ) ;
+	member->from_slinear = ast_translator_build_path( member->write_format, AST_FORMAT_SLINEAR ) ;
+
+	ast_log( AST_CONF_DEBUG, "AST_FORMAT_SLINEAR => %d\n", AST_FORMAT_SLINEAR ) ;
+
+	// index for converted_frames array
+	switch ( member->write_format )
+	{
+		case AST_FORMAT_SLINEAR:
+			member->write_format_index = AC_SLINEAR_INDEX ;
+			break ;
+
+		case AST_FORMAT_ULAW:
+			member->write_format_index = AC_ULAW_INDEX ;
+			break ;
+
+	        case AST_FORMAT_ALAW:
+			member->write_format_index = AC_ALAW_INDEX ;
+			break ;
+
+		case AST_FORMAT_GSM:
+			member->write_format_index = AC_GSM_INDEX ;
+			break ;
+
+		case AST_FORMAT_SPEEX:
+			member->write_format_index = AC_SPEEX_INDEX;
+			break;
+
+#ifdef AC_USE_G729A
+		case AST_FORMAT_G729A:
+			member->write_format_index = AC_G729A_INDEX;
+			break;
+#endif
+
+		default:
+			member->write_format_index = 0 ;
+	}
+
+	// index for converted_frames array
+	switch ( member->read_format )
+	{
+		case AST_FORMAT_SLINEAR:
+			member->read_format_index = AC_SLINEAR_INDEX ;
+			break ;
+
+		case AST_FORMAT_ULAW:
+			member->read_format_index = AC_ULAW_INDEX ;
+			break ;
+
+		case AST_FORMAT_ALAW:
+			member->read_format_index = AC_ALAW_INDEX ;
+			break ;
+
+		case AST_FORMAT_GSM:
+			member->read_format_index = AC_GSM_INDEX ;
+			break ;
+
+		case AST_FORMAT_SPEEX:
+			member->read_format_index = AC_SPEEX_INDEX;
+			break;
+
+#ifdef AC_USE_G729A
+		case AST_FORMAT_G729A:
+			member->read_format_index = AC_G729A_INDEX;
+			break;
+#endif
+
+		default:
+			member->read_format_index = 0 ;
+	}
+
+	// smoother defaults.
+	member->smooth_multiple =1;
+	member->smooth_size_in = -1;
+	member->smooth_size_out = -1;
+	member->inSmoother= NULL;
+	member->outPacker= NULL;
+
+	switch (member->read_format){
+		/* these assumptions may be incorrect */
+		case AST_FORMAT_ULAW:
+		case AST_FORMAT_ALAW:
+			member->smooth_size_in  = 160; //bytes
+			member->smooth_size_out = 160; //samples
+			break;
+		case AST_FORMAT_GSM:
+			/*
+			member->smooth_size_in  = 33; //bytes
+			member->smooth_size_out = 160;//samples
+			*/
+			break;
+		case AST_FORMAT_SPEEX:
+		case AST_FORMAT_G729A:
+			/* this assumptions are wrong
+			member->smooth_multiple = 2 ;  // for testing, force to dual frame
+			member->smooth_size_in  = 39;  // bytes
+			member->smooth_size_out = 160; // samples
+			*/
+			break;
+		case AST_FORMAT_SLINEAR:
+			member->smooth_size_in  = 320; //bytes
+			member->smooth_size_out = 160; //samples
+			break;
+		default:
+			member->inSmoother = NULL; //don't use smoother for this type.
+			//ast_log( AST_CONF_DEBUG, "smoother is NULL for member->read_format => %d\n", member->read_format);
+	}
+
+	if (member->smooth_size_in > 0){
+		member->inSmoother = ast_smoother_new(member->smooth_size_in);
+		ast_log( AST_CONF_DEBUG, "created smoother(%d) for %d\n", member->smooth_size_in , member->read_format);
+	}
+
+	//
+	// finish up
+	//
+
+	ast_log( AST_CONF_DEBUG, "created member, type => %c, priority => %d, readformat => %d\n",
+		member->type, member->priority, chan->readformat ) ;
+
+	return member ;
+}
+
+struct ast_conf_member* delete_member( struct ast_conf_member* member )
+{
+	// !!! NO RETURN TEST !!!
+	// do { sleep(1) ; } while (1) ;
+
+	// !!! CRASH TEST !!!
+	// *((int *)0) = 0;
+
+	if ( member == NULL )
+	{
+		ast_log( LOG_WARNING, "unable to the delete null member\n" ) ;
+		return NULL ;
+	}
+
+	ast_mutex_lock(&member->lock);
+
+	// If member is driving another member, make sure its speaker count is correct
+	if ( member->driven_member != NULL && member->speaking_state == 1 )
+		decrement_speaker_count(member->driven_member, 1);
+
+	//
+	// clean up member flags
+	//
+
+	if ( member->flags != NULL )
+	{
+		// !!! DEBUGING !!!
+		ast_log( AST_CONF_DEBUG, "freeing member flags, name => %s\n",
+			member->channel_name ) ;
+		free( member->flags ) ;
+	}
+
+	//
+	// delete the members frames
+	//
+
+	conf_frame* cf ;
+
+	// !!! DEBUGING !!!
+	ast_log( AST_CONF_DEBUG, "deleting member input frames, name => %s\n",
+		member->channel_name ) ;
+
+	// incoming frames
+	cf = member->inFrames ;
+
+	while ( cf != NULL )
+	{
+		cf = delete_conf_frame( cf ) ;
+	}
+
+	if (member->inSmoother != NULL)
+		ast_smoother_free(member->inSmoother);
+
+	cf = member->inVideoFrames ;
+
+	while ( cf != NULL )
+	{
+		cf = delete_conf_frame( cf ) ;
+	}
+
+	// !!! DEBUGING !!!
+	ast_log( AST_CONF_DEBUG, "deleting member output frames, name => %s\n",
+		member->channel_name ) ;
+
+	// outgoing frames
+	cf = member->outFrames ;
+
+	while ( cf != NULL )
+	{
+		cf = delete_conf_frame( cf ) ;
+	}
+
+	cf = member->outVideoFrames ;
+
+	while ( cf != NULL )
+	{
+		cf = delete_conf_frame( cf ) ;
+	}
+
+#if ( SILDET == 2 )
+	if ( member->dsp != NULL )
+	{
+		// !!! DEBUGING !!!
+		ast_log( AST_CONF_DEBUG, "destroying member preprocessor, name => %s\n",
+			member->channel_name ) ;
+		speex_preprocess_state_destroy( member->dsp ) ;
+	}
+#endif
+
+	// !!! DEBUGING !!!
+	ast_log( AST_CONF_DEBUG, "freeing member translator paths, name => %s\n",
+		member->channel_name ) ;
+
+	// free the mixing translators
+	ast_translator_free_path( member->to_slinear ) ;
+	ast_translator_free_path( member->from_slinear ) ;
+
+	// get a pointer to the next
+	// member so we can return it
+	struct ast_conf_member* nm = member->next ;
+
+	ast_mutex_unlock(&member->lock);
+
+	// !!! DEBUGING !!!
+	ast_log( AST_CONF_DEBUG, "freeing member channel name, name => %s\n",
+		member->channel_name ) ;
+
+	// free the member's copy for the channel name
+	free( member->channel_name ) ;
+
+	// free the member's copy of the conference name
+	free(member->conf_name);
+
+	// !!! DEBUGING !!!
+	ast_log( AST_CONF_DEBUG, "freeing member\n" ) ;
+
+	// free the member's memory
+	free(member->callerid);
+	free(member->callername);
+
+	// free enter/leave sounds
+	free(member->enter_snd);
+	free(member->leave_snd);
+
+	free( member ) ;
+	member = NULL ;
+
+	return nm ;
+}
+
+//
+// incoming frame functions
+//
+
+conf_frame* get_incoming_video_frame( struct ast_conf_member *member )
+{
+  	if ( member == NULL )
+	{
+		ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ;
+		return NULL ;
+	}
+
+	ast_mutex_lock(&member->lock);
+
+	if ( member->inVideoFramesCount == 0 )
+	{
+		ast_mutex_unlock(&member->lock);
+		return NULL ;
+	}
+
+	//
+	// return the next frame in the queue
+	//
+
+	conf_frame* cfr = NULL ;
+
+	// get first frame in line
+	cfr = member->inVideoFramesTail ;
+
+	// if it's the only frame, reset the queue,
+	// else, move the second frame to the front
+	if ( member->inVideoFramesTail == member->inVideoFrames )
+	{
+		member->inVideoFramesTail = NULL ;
+		member->inVideoFrames = NULL ;
+	}
+	else
+	{
+		// move the pointer to the next frame
+		member->inVideoFramesTail = member->inVideoFramesTail->prev ;
+
+		// reset it's 'next' pointer
+		if ( member->inVideoFramesTail != NULL )
+			member->inVideoFramesTail->next = NULL ;
+	}
+
+	// separate the conf frame from the list
+	cfr->next = NULL ;
+	cfr->prev = NULL ;
+
+	// decrement frame count
+	member->inVideoFramesCount-- ;
+
+	ast_mutex_unlock(&member->lock);
+	return cfr ;
+
+}
+conf_frame* get_incoming_dtmf_frame( struct ast_conf_member *member )
+{
+  	if ( member == NULL )
+	{
+		ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ;
+		return NULL ;
+	}
+
+	ast_mutex_lock(&member->lock);
+
+	if ( member->inDTMFFramesCount == 0 )
+	{
+		ast_mutex_unlock(&member->lock);
+		return NULL ;
+	}
+
+	//
+	// return the next frame in the queue
+	//
+
+	conf_frame* cfr = NULL ;
+
+	// get first frame in line
+	cfr = member->inDTMFFramesTail ;
+
+	// if it's the only frame, reset the queue,
+	// else, move the second frame to the front
+	if ( member->inDTMFFramesTail == member->inDTMFFrames )
+	{
+		member->inDTMFFramesTail = NULL ;
+		member->inDTMFFrames = NULL ;
+	}
+	else
+	{
+		// move the pointer to the next frame
+		member->inDTMFFramesTail = member->inDTMFFramesTail->prev ;
+
+		// reset it's 'next' pointer
+		if ( member->inDTMFFramesTail != NULL )
+			member->inDTMFFramesTail->next = NULL ;
+	}
+
+	// separate the conf frame from the list
+	cfr->next = NULL ;
+	cfr->prev = NULL ;
+
+	// decriment frame count
+	member->inDTMFFramesCount-- ;
+
+	ast_mutex_unlock(&member->lock);
+	return cfr ;
+
+}
+
+
+conf_frame* get_incoming_frame( struct ast_conf_member *member )
+{
+	conf_frame *cf_result;
+	//
+	// sanity checks
+	//
+
+	if ( member == NULL )
+	{
+		ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ;
+		return NULL ;
+	}
+
+	ast_mutex_lock(&member->lock);
+
+ 	//
+ 	// repeat last frame a couple times to smooth transition
+ 	//
+
+#ifdef AST_CONF_CACHE_LAST_FRAME
+	if ( member->inFramesCount == 0 )
+	{
+		// nothing to do if there's no cached frame
+		if ( member->inFramesLast == NULL ) {
+			ast_mutex_unlock(&member->lock);
+			return NULL ;
+		}
+
+		// turn off 'okay to cache' flag
+		member->okayToCacheLast = 0 ;
+
+		if ( member->inFramesRepeatLast >= AST_CONF_CACHE_LAST_FRAME )
+		{
+			// already used this frame AST_CONF_CACHE_LAST_FRAME times
+
+			// reset repeat count
+			member->inFramesRepeatLast = 0 ;
+
+			// clear the cached frame
+			delete_conf_frame( member->inFramesLast ) ;
+			member->inFramesLast = NULL ;
+
+			// return null
+			ast_mutex_unlock(&member->lock);
+			return NULL ;
+		}
+		else
+		{
+			ast_log( AST_CONF_DEBUG, "repeating cached frame, channel => %s, inFramesRepeatLast => %d\n",
+				member->channel_name, member->inFramesRepeatLast ) ;
+
+			// increment counter
+			member->inFramesRepeatLast++ ;
+
+			// return a copy of the cached frame
+			cf_result = copy_conf_frame( member->inFramesLast ) ;
+			ast_mutex_unlock(&member->lock);
+			return cf_result;
+		}
+	}
+	else if ( member->okayToCacheLast == 0 && member->inFramesCount >= 3 )
+	{
+		ast_log( AST_CONF_DEBUG, "enabling cached frame, channel => %s, incoming => %d, outgoing => %d\n",
+			member->channel_name, member->inFramesCount, member->outFramesCount ) ;
+
+		// turn on 'okay to cache' flag
+		member->okayToCacheLast = 1 ;
+	}
+#else
+	if ( member->inFramesCount == 0 ) {
+		ast_mutex_unlock(&member->lock);
+		return NULL ;
+	}
+#endif // AST_CONF_CACHE_LAST_FRAME
+
+	//
+	// return the next frame in the queue
+	//
+
+	conf_frame* cfr = NULL ;
+
+	// get first frame in line
+	cfr = member->inFramesTail ;
+
+	// if it's the only frame, reset the queue,
+	// else, move the second frame to the front
+	if ( member->inFramesTail == member->inFrames )
+	{
+		member->inFramesTail = NULL ;
+		member->inFrames = NULL ;
+	}
+	else
+	{
+		// move the pointer to the next frame
+		member->inFramesTail = member->inFramesTail->prev ;
+
+		// reset it's 'next' pointer
+		if ( member->inFramesTail != NULL )
+			member->inFramesTail->next = NULL ;
+	}
+
+	// separate the conf frame from the list
+	cfr->next = NULL ;
+	cfr->prev = NULL ;
+
+	// decriment frame count
+	member->inFramesCount-- ;
+
+#ifdef AST_CONF_CACHE_LAST_FRAME
+	// copy frame if queue is now empty
+	if (
+		member->inFramesCount == 0
+		&& member->okayToCacheLast == 1
+	)
+	{
+		// reset repeat count
+		member->inFramesRepeatLast = 0 ;
+
+		// clear cached frame
+		if ( member->inFramesLast != NULL )
+		{
+			delete_conf_frame( member->inFramesLast ) ;
+			member->inFramesLast = NULL ;
+		}
+
+		// cache new frame
+		member->inFramesLast = copy_conf_frame( cfr ) ;
+	}
+#endif // AST_CONF_CACHE_LAST_FRAME
+
+	ast_mutex_unlock(&member->lock);
+	return cfr ;
+}
+
+int queue_incoming_video_frame( struct ast_conf_member* member, const struct ast_frame* fr )
+{
+	// check on frame
+	if ( fr == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to queue null frame\n" ) ;
+		return -1 ;
+	}
+
+	// check on member
+	if ( member == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ;
+		return -1 ;
+	}
+
+	// lock the member
+	ast_mutex_lock(&member->lock);
+
+	if (!member->first_frame_received)
+	{
+		// nat=yes will be correct now
+		member->first_frame_received = 1;
+		member->conference = 1;
+	}
+
+	// We have to drop if the queue is full!
+	if ( member->inVideoFramesCount >= AST_CONF_MAX_VIDEO_QUEUE )
+	{
+		ast_log(
+			AST_CONF_DEBUG,
+			"unable to queue incoming VIDEO frame, channel => %s, incoming => %d, outgoing => %d\n",
+			member->channel_name, member->inVideoFramesCount, member->outVideoFramesCount
+		) ;
+		ast_mutex_unlock(&member->lock);
+		return -1 ;
+	}
+
+	//
+	// create new conf frame from passed data frame
+	//
+
+	// ( member->inFrames may be null at this point )
+	conf_frame* cfr = create_conf_frame( member, member->inVideoFrames, fr ) ;
+
+	if ( cfr == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to malloc conf_frame\n" ) ;
+		ast_mutex_unlock(&member->lock);
+		return -1 ;
+	}
+
+	// copy frame data pointer to conf frame
+	// cfr->fr = fr ;
+
+	//
+	// add new frame to speaking members incoming frame queue
+	// ( i.e. save this frame data, so we can distribute it in conference_exec later )
+	//
+
+	if ( member->inVideoFrames == NULL )
+	{
+		// this is the first frame in the buffer
+		member->inVideoFramesTail = cfr ;
+		member->inVideoFrames = cfr ;
+	}
+	else
+	{
+		// put the new frame at the head of the list
+		member->inVideoFrames = cfr ;
+	}
+
+	// increment member frame count
+	member->inVideoFramesCount++ ;
+
+	ast_mutex_unlock(&member->lock);
+
+        // Everything has gone okay!
+	return 0;
+}
+
+int queue_incoming_dtmf_frame( struct ast_conf_member* member, const struct ast_frame* fr )
+{
+  //ast_log( AST_CONF_DEBUG, "queue incoming video frame\n");
+
+	// check on frame
+	if ( fr == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to queue null frame\n" ) ;
+		return -1 ;
+	}
+
+	// check on member
+	if ( member == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ;
+		return -1 ;
+	}
+
+	ast_mutex_lock(&member->lock);
+
+	// We have to drop if the queue is full!
+	if ( member->inDTMFFramesCount >= AST_CONF_MAX_DTMF_QUEUE )
+	{
+		ast_log(
+			AST_CONF_DEBUG,
+			"unable to queue incoming DTMF frame, channel => %s, incoming => %d, outgoing => %d\n",
+			member->channel_name, member->inDTMFFramesCount, member->outDTMFFramesCount
+		) ;
+		ast_mutex_unlock(&member->lock);
+		return -1 ;
+	}
+
+	//
+	// create new conf frame from passed data frame
+	//
+
+	// ( member->inFrames may be null at this point )
+	conf_frame* cfr = create_conf_frame( member, member->inDTMFFrames, fr ) ;
+
+	if ( cfr == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to malloc conf_frame\n" ) ;
+		ast_mutex_unlock(&member->lock);
+		return -1 ;
+	}
+
+	// copy frame data pointer to conf frame
+	// cfr->fr = fr ;
+
+	//
+	// add new frame to speaking members incoming frame queue
+	// ( i.e. save this frame data, so we can distribute it in conference_exec later )
+	//
+
+	if ( member->inDTMFFrames == NULL )
+	{
+		// this is the first frame in the buffer
+		member->inDTMFFramesTail = cfr ;
+		member->inDTMFFrames = cfr ;
+	}
+	else
+	{
+		// put the new frame at the head of the list
+		member->inDTMFFrames = cfr ;
+	}
+
+	// increment member frame count
+	member->inDTMFFramesCount++ ;
+
+	ast_mutex_unlock(&member->lock);
+
+	// Everything has gone okay!
+	return 0;
+}
+
+int queue_incoming_frame( struct ast_conf_member* member, struct ast_frame* fr )
+{
+	//
+	// sanity checks
+	//
+
+	// check on frame
+	if ( fr == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to queue null frame\n" ) ;
+		return -1 ;
+	}
+
+	// check on member
+	if ( member == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ;
+		return -1 ;
+	}
+
+	ast_mutex_lock(&member->lock);
+
+	if ( member->inFramesCount > member->inFramesNeeded )
+	{
+		if ( member->inFramesCount > AST_CONF_QUEUE_DROP_THRESHOLD )
+		{
+			struct timeval curr = ast_tvnow();
+
+			// time since last dropped frame
+			long diff = ast_tvdiff_ms(curr, member->last_in_dropped);
+
+			// number of milliseconds which must pass between frame drops
+			// ( 15 frames => -100ms, 10 frames => 400ms, 5 frames => 900ms, 0 frames => 1400ms, etc. )
+			long time_limit = 1000 - ( ( member->inFramesCount - AST_CONF_QUEUE_DROP_THRESHOLD ) * 100 ) ;
+
+			if ( diff >= time_limit )
+			{
+				// count sequential drops
+				member->sequential_drops++ ;
+
+				ast_log(
+					AST_CONF_DEBUG,
+					"dropping frame from input buffer, channel => %s, incoming => %d, outgoing => %d\n",
+					member->channel_name, member->inFramesCount, member->outFramesCount
+				) ;
+
+				// accounting: count dropped incoming frames
+				member->frames_in_dropped++ ;
+
+				// reset frames since dropped
+				member->since_dropped = 0 ;
+
+				// delete the frame
+				delete_conf_frame( get_incoming_frame( member ) ) ;
+
+				member->last_in_dropped = ast_tvnow();
+			}
+			else
+			{
+/*
+				ast_log(
+					AST_CONF_DEBUG,
+					"input buffer larger than drop threshold, channel => %s, incoming => %d, outgoing => %d\n",
+					member->channel_name, member->inFramesCount, member->outFramesCount
+				) ;
+*/
+			}
+		}
+	}
+
+	//
+	// if we have to drop frames, we'll drop new frames
+	// because it's easier ( and doesn't matter much anyway ).
+	//
+
+	if ( member->inFramesCount >= AST_CONF_MAX_QUEUE )
+	{
+		// count sequential drops
+		member->sequential_drops++ ;
+
+		ast_log(
+			AST_CONF_DEBUG,
+			"unable to queue incoming frame, channel => %s, incoming => %d, outgoing => %d\n",
+			member->channel_name, member->inFramesCount, member->outFramesCount
+		) ;
+
+		// accounting: count dropped incoming frames
+		member->frames_in_dropped++ ;
+
+		// reset frames since dropped
+		member->since_dropped = 0 ;
+
+		ast_mutex_unlock(&member->lock);
+		return -1 ;
+	}
+
+	// reset sequential drops
+	member->sequential_drops = 0 ;
+
+	// increment frames since dropped
+	member->since_dropped++ ;
+
+	//
+	// create new conf frame from passed data frame
+	//
+
+	// ( member->inFrames may be null at this point )
+	if (member->inSmoother == NULL ){
+		conf_frame* cfr = create_conf_frame( member, member->inFrames, fr ) ;
+		if ( cfr == NULL )
+		{
+			ast_log( LOG_ERROR, "unable to malloc conf_frame\n" ) ;
+			ast_mutex_unlock(&member->lock);
+			return -1 ;
+		}
+
+		//
+		// add new frame to speaking members incoming frame queue
+		// ( i.e. save this frame data, so we can distribute it in conference_exec later )
+		//
+
+		if ( member->inFrames == NULL ) {
+			member->inFramesTail = cfr ;
+		}
+		member->inFrames = cfr ;
+		member->inFramesCount++ ;
+	} else {
+		//feed frame(fr) into the smoother
+
+		// smoother tmp frame
+		struct ast_frame *sfr;
+		int multiple = 1;
+		int i=0;
+
+#if 0
+		if ( (member->smooth_size_in > 0 ) && (member->smooth_size_in * member->smooth_multiple != fr->datalen) )
+		{
+			ast_log( AST_CONF_DEBUG, "resetting smooth_size_in. old size=> %d, multiple =>%d, datalen=> %d\n", member->smooth_size_in, member->smooth_multiple, fr->datalen );
+			if ( fr->datalen % member->smooth_multiple != 0) {
+				// if datalen not divisible by smooth_multiple, assume we're just getting normal encoding.
+			//	ast_log(AST_CONF_DEBUG,"smooth_multiple does not divide datalen. changing smooth size from %d to %d, multiple => 1\n", member->smooth_size_in, fr->datalen);
+				member->smooth_size_in = fr->datalen;
+				member->smooth_multiple = 1;
+			} else {
+				// assume a fixed multiple, so divide into datalen.
+				int newsmooth = fr->datalen / member->smooth_multiple ;
+			//	ast_log(AST_CONF_DEBUG,"datalen is divisible by smooth_multiple, changing smooth size from %d to %d\n", member->smooth_size_in, newsmooth);
+				member->smooth_size_in = newsmooth;
+			}
+
+			//free input smoother.
+			if (member->inSmoother != NULL)
+				ast_smoother_free(member->inSmoother);
+
+			//make new input smoother.
+			member->inSmoother = ast_smoother_new(member->smooth_size_in);
+		}
+#endif
+
+		ast_smoother_feed( member->inSmoother, fr );
+ast_log (AST_CONF_DEBUG, "SMOOTH:Feeding frame into inSmoother, timestamp => %ld.%ld\n", fr->delivery.tv_sec, fr->delivery.tv_usec);
+
+		if ( multiple > 1 )
+			fr->samples /= multiple;
+
+		// read smoothed version of frames, add to queue
+		while( ( sfr = ast_smoother_read( member->inSmoother ) ) ){
+
+			++i;
+ast_log( AST_CONF_DEBUG , "\treading new frame [%d] from smoother, inFramesCount[%d], \n\tsfr->frametype -> %d , sfr->subclass -> %d , sfr->datalen => %d sfr->samples => %d\n", i , member->inFramesCount , sfr->frametype, sfr->subclass, sfr->datalen, sfr->samples);
+ast_log (AST_CONF_DEBUG, "SMOOTH:Reading frame from inSmoother, i=>%d, timestamp => %ld.%ld\n",i, sfr->delivery.tv_sec, sfr->delivery.tv_usec);
+			conf_frame* cfr = create_conf_frame( member, member->inFrames, sfr ) ;
+			if ( cfr == NULL )
+			{
+				ast_log( LOG_ERROR, "unable to malloc conf_frame\n" ) ;
+				ast_mutex_unlock(&member->lock);
+				return -1 ;
+			}
+
+			//
+			// add new frame to speaking members incoming frame queue
+			// ( i.e. save this frame data, so we can distribute it in conference_exec later )
+			//
+
+			if ( member->inFrames == NULL ) {
+				member->inFramesTail = cfr ;
+			}
+			member->inFrames = cfr ;
+			member->inFramesCount++ ;
+		}
+	}
+	ast_mutex_unlock(&member->lock);
+	return 0 ;
+}
+
+//
+// outgoing frame functions
+//
+
+conf_frame* get_outgoing_frame( struct ast_conf_member *member )
+{
+	if ( member == NULL )
+	{
+		ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ;
+		return NULL ;
+	}
+
+	conf_frame* cfr ;
+
+	// ast_log( AST_CONF_DEBUG, "getting member frames, count => %d\n", member->outFramesCount ) ;
+
+	ast_mutex_lock(&member->lock);
+
+	if ( member->outFramesCount > AST_CONF_MIN_QUEUE )
+	{
+		cfr = member->outFramesTail ;
+
+		// if it's the only frame, reset the queu,
+		// else, move the second frame to the front
+		if ( member->outFramesTail == member->outFrames )
+		{
+			member->outFrames = NULL ;
+			member->outFramesTail = NULL ;
+		}
+		else
+		{
+			// move the pointer to the next frame
+			member->outFramesTail = member->outFramesTail->prev ;
+
+			// reset it's 'next' pointer
+			if ( member->outFramesTail != NULL )
+				member->outFramesTail->next = NULL ;
+		}
+
+		// separate the conf frame from the list
+		cfr->next = NULL ;
+		cfr->prev = NULL ;
+
+		// decriment frame count
+		member->outFramesCount-- ;
+		ast_mutex_unlock(&member->lock);
+		return cfr ;
+	}
+	ast_mutex_unlock(&member->lock);
+	return NULL ;
+}
+
+int __queue_outgoing_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery )
+{
+	// accounting: count the number of outgoing frames for this member
+	member->frames_out++ ;
+
+	//
+	// we have to drop frames, so we'll drop new frames
+	// because it's easier ( and doesn't matter much anyway ).
+	//
+	if ( member->outFramesCount >= AST_CONF_MAX_QUEUE )
+	{
+		ast_log(
+			AST_CONF_DEBUG,
+			"unable to queue outgoing frame, channel => %s, incoming => %d, outgoing => %d\n",
+			member->channel_name, member->inFramesCount, member->outFramesCount
+		) ;
+
+		// accounting: count dropped outgoing frames
+		member->frames_out_dropped++ ;
+		return -1 ;
+	}
+
+	//
+	// create new conf frame from passed data frame
+	//
+
+	conf_frame* cfr = create_conf_frame( member, member->outFrames, fr ) ;
+
+	if ( cfr == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to create new conf frame\n" ) ;
+
+		// accounting: count dropped outgoing frames
+		member->frames_out_dropped++ ;
+		return -1 ;
+	}
+
+	// set delivery timestamp
+	cfr->fr->delivery = delivery ;
+
+	//
+	// add new frame to speaking members incoming frame queue
+	// ( i.e. save this frame data, so we can distribute it in conference_exec later )
+	//
+
+	if ( member->outFrames == NULL ) {
+		member->outFramesTail = cfr ;
+	}
+	member->outFrames = cfr ;
+	member->outFramesCount++ ;
+
+	// return success
+	return 0 ;
+}
+
+int queue_outgoing_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery )
+{
+	// check on frame
+	if ( fr == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to queue null frame\n" ) ;
+		return -1 ;
+	}
+
+	// check on member
+	if ( member == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ;
+		return -1 ;
+	}
+
+	if ( ( member->outPacker == NULL ) && ( member->smooth_multiple > 1 ) && ( member->smooth_size_out > 0 ) ){
+		//ast_log (AST_CONF_DEBUG, "creating outPacker with size => %d \n\t( multiple => %d ) * ( size => %d )\n", member->smooth_multiple * member-> smooth_size_out, member->smooth_multiple , member->smooth_size_out);
+		member->outPacker = ast_packer_new( member->smooth_multiple * member->smooth_size_out);
+	}
+
+	if (member->outPacker == NULL ){
+		return __queue_outgoing_frame( member, fr, delivery ) ;
+	}
+	else
+	{
+		struct ast_frame *sfr;
+		int exitval = 0;
+//ast_log (AST_CONF_DEBUG, "sending fr into outPacker, datalen=>%d, samples=>%d\n",fr->datalen, fr->samples);
+		ast_packer_feed( member->outPacker , fr );
+		while( (sfr = ast_packer_read( member->outPacker ) ) )
+		{
+//ast_log (AST_CONF_DEBUG, "read sfr from outPacker, datalen=>%d, samples=>%d\n",sfr->datalen, sfr->samples);
+			if ( __queue_outgoing_frame( member, sfr, delivery ) == -1 ) {
+				exitval = -1;
+			}
+		}
+
+		return exitval;
+	}
+}
+
+//
+// outgoing frame functions
+//
+
+conf_frame* get_outgoing_video_frame( struct ast_conf_member *member )
+{
+	if ( member == NULL )
+	{
+		ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ;
+		return NULL ;
+	}
+
+	conf_frame* cfr ;
+
+	ast_mutex_lock(&member->lock);
+
+	// ast_log( AST_CONF_DEBUG, "getting member frames, count => %d\n", member->outFramesCount ) ;
+
+	if ( member->outVideoFramesCount > AST_CONF_MIN_QUEUE )
+	{
+		cfr = member->outVideoFramesTail ;
+
+		// if it's the only frame, reset the queu,
+		// else, move the second frame to the front
+		if ( member->outVideoFramesTail == member->outVideoFrames )
+		{
+			member->outVideoFrames = NULL ;
+			member->outVideoFramesTail = NULL ;
+		}
+		else
+		{
+			// move the pointer to the next frame
+			member->outVideoFramesTail = member->outVideoFramesTail->prev ;
+
+			// reset it's 'next' pointer
+			if ( member->outVideoFramesTail != NULL )
+				member->outVideoFramesTail->next = NULL ;
+		}
+
+		// separate the conf frame from the list
+		cfr->next = NULL ;
+		cfr->prev = NULL ;
+
+		// decriment frame count
+		member->outVideoFramesCount-- ;
+		ast_mutex_unlock(&member->lock);
+		return cfr ;
+	}
+
+	ast_mutex_unlock(&member->lock);
+	return NULL ;
+}
+
+
+
+int queue_outgoing_video_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery )
+{
+	// check on frame
+	if ( fr == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to queue null frame\n" ) ;
+		return -1 ;
+	}
+
+	// check on member
+	if ( member == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ;
+		return -1 ;
+	}
+
+	ast_mutex_lock(&member->lock);
+
+	// accounting: count the number of outgoing frames for this member
+	member->video_frames_out++ ;
+
+	//
+	// we have to drop frames, so we'll drop new frames
+	// because it's easier ( and doesn't matter much anyway ).
+	//
+	if ( member->outVideoFramesCount >= AST_CONF_MAX_VIDEO_QUEUE)
+	{
+		ast_log(
+			AST_CONF_DEBUG,
+			"unable to queue outgoing VIDEO frame, channel => %s, incoming => %d, outgoing => %d\n",
+			member->channel_name, member->inVideoFramesCount, member->outVideoFramesCount
+		) ;
+
+		// accounting: count dropped outgoing frames
+		member->video_frames_out_dropped++ ;
+		ast_mutex_unlock(&member->lock);
+		return -1 ;
+	}
+
+	//
+	// create new conf frame from passed data frame
+	//
+
+	conf_frame* cfr = create_conf_frame( member, member->outVideoFrames, fr ) ;
+
+	if ( cfr == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to create new conf frame\n" ) ;
+
+		// accounting: count dropped outgoing frames
+		member->video_frames_out_dropped++ ;
+                ast_mutex_unlock(&member->lock);
+		return -1 ;
+	}
+
+	// set delivery timestamp
+#ifdef VIDEO_SETTIMESTAMP
+	cfr->fr->delivery = delivery ;
+#else
+	cfr->fr->delivery.tv_sec = 0;
+	cfr->fr->delivery.tv_usec = 0;
+#endif
+	//ast_log (LOG_WARNING,"%d\n",cfr->fr->seqno);
+
+#ifdef RTP_SEQNO_ZERO
+	cfr->fr->seqno = 0;
+#endif
+
+	if ( member->outVideoFrames == NULL )
+	{
+		// this is the first frame in the buffer
+		member->outVideoFramesTail = cfr ;
+		member->outVideoFrames = cfr ;
+	}
+	else
+	{
+		// put the new frame at the head of the list
+		member->outVideoFrames = cfr ;
+	}
+
+	// increment member frame count
+	member->outVideoFramesCount++ ;
+
+	ast_mutex_unlock(&member->lock);
+
+	// return success
+	return 0 ;
+}
+
+conf_frame* get_outgoing_dtmf_frame( struct ast_conf_member *member )
+{
+	if ( member == NULL )
+	{
+		ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ;
+		return NULL ;
+	}
+
+	conf_frame* cfr ;
+
+	// ast_log( AST_CONF_DEBUG, "getting member frames, count => %d\n", member->outFramesCount ) ;
+
+	ast_mutex_lock(&member->lock);
+
+	if ( member->outDTMFFramesCount > AST_CONF_MIN_QUEUE )
+	{
+		cfr = member->outDTMFFramesTail ;
+
+		// if it's the only frame, reset the queu,
+		// else, move the second frame to the front
+		if ( member->outDTMFFramesTail == member->outDTMFFrames )
+		{
+			member->outDTMFFrames = NULL ;
+			member->outDTMFFramesTail = NULL ;
+		}
+		else
+		{
+			// move the pointer to the next frame
+			member->outDTMFFramesTail = member->outDTMFFramesTail->prev ;
+
+			// reset it's 'next' pointer
+			if ( member->outDTMFFramesTail != NULL )
+				member->outDTMFFramesTail->next = NULL ;
+		}
+
+		// separate the conf frame from the list
+		cfr->next = NULL ;
+		cfr->prev = NULL ;
+
+		// decriment frame count
+		member->outDTMFFramesCount-- ;
+		ast_mutex_unlock(&member->lock);
+		return cfr ;
+	}
+	ast_mutex_unlock(&member->lock);
+	return NULL ;
+}
+
+conf_frame* get_outgoing_text_frame( struct ast_conf_member *member )
+{
+	if ( member == NULL )
+	{
+		ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ;
+		return NULL ;
+	}
+
+	conf_frame* cfr ;
+
+	// ast_log( AST_CONF_DEBUG, "getting member frames, count => %d\n", member->outFramesCount ) ;
+
+	ast_mutex_lock(&member->lock);
+
+	if ( member->outTextFramesCount > AST_CONF_MIN_QUEUE )
+	{
+		cfr = member->outTextFramesTail ;
+
+		// if it's the only frame, reset the queu,
+		// else, move the second frame to the front
+		if ( member->outTextFramesTail == member->outTextFrames )
+		{
+			member->outTextFrames = NULL ;
+			member->outTextFramesTail = NULL ;
+		}
+		else
+		{
+			// move the pointer to the next frame
+			member->outTextFramesTail = member->outTextFramesTail->prev ;
+
+			// reset it's 'next' pointer
+			if ( member->outTextFramesTail != NULL )
+				member->outTextFramesTail->next = NULL ;
+		}
+
+		// separate the conf frame from the list
+		cfr->next = NULL ;
+		cfr->prev = NULL ;
+
+		// decriment frame count
+		member->outTextFramesCount-- ;
+		ast_mutex_unlock(&member->lock);
+		return cfr ;
+	}
+	ast_mutex_unlock(&member->lock);
+	return NULL ;
+}
+
+
+int queue_outgoing_dtmf_frame( struct ast_conf_member* member, const struct ast_frame* fr )
+{
+	// check on frame
+	if ( fr == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to queue null frame\n" ) ;
+		return -1 ;
+	}
+
+	// check on member
+	if ( member == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ;
+		return -1 ;
+	}
+
+	ast_mutex_lock(&member->lock);
+
+	// accounting: count the number of outgoing frames for this member
+	member->dtmf_frames_out++ ;
+
+	//
+	// we have to drop frames, so we'll drop new frames
+	// because it's easier ( and doesn't matter much anyway ).
+	//
+	if ( member->outDTMFFramesCount >= AST_CONF_MAX_DTMF_QUEUE)
+	{
+		ast_log(
+			AST_CONF_DEBUG,
+			"unable to queue outgoing DTMF frame, channel => %s, incoming => %d, outgoing => %d\n",
+			member->channel_name, member->inDTMFFramesCount, member->outDTMFFramesCount
+		) ;
+
+		// accounting: count dropped outgoing frames
+		member->dtmf_frames_out_dropped++ ;
+		ast_mutex_unlock(&member->lock);
+		return -1 ;
+	}
+
+	//
+	// create new conf frame from passed data frame
+	//
+
+	conf_frame* cfr = create_conf_frame( member, member->outDTMFFrames, fr ) ;
+
+	if ( cfr == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to create new conf frame\n" ) ;
+
+		// accounting: count dropped outgoing frames
+		member->dtmf_frames_out_dropped++ ;
+		ast_mutex_unlock(&member->lock);
+		return -1 ;
+	}
+
+#ifdef RTP_SEQNO_ZERO
+	cfr->fr->seqno = 0;
+#endif
+
+	if ( member->outDTMFFrames == NULL )
+	{
+		// this is the first frame in the buffer
+		member->outDTMFFramesTail = cfr ;
+		member->outDTMFFrames = cfr ;
+	}
+	else
+	{
+		// put the new frame at the head of the list
+		member->outDTMFFrames = cfr ;
+	}
+
+	// increment member frame count
+	member->outDTMFFramesCount++ ;
+
+	ast_mutex_unlock(&member->lock);
+	// return success
+	return 0 ;
+}
+
+int queue_outgoing_text_frame( struct ast_conf_member* member, const struct ast_frame* fr)
+{
+	// check on frame
+	if ( fr == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to queue null frame\n" ) ;
+		return -1 ;
+	}
+
+	// check on member
+	if ( member == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ;
+		return -1 ;
+	}
+
+	ast_mutex_lock(&member->lock);
+
+	// accounting: count the number of outgoing frames for this member
+	member->text_frames_out++ ;
+
+	//
+	// we have to drop frames, so we'll drop new frames
+	// because it's easier ( and doesn't matter much anyway ).
+	//
+	if ( member->outTextFramesCount >= AST_CONF_MAX_TEXT_QUEUE)
+	{
+		ast_log(
+			AST_CONF_DEBUG,
+			"unable to queue outgoing text frame, channel => %s, incoming => %d, outgoing => %d\n",
+			member->channel_name, member->inTextFramesCount, member->outTextFramesCount
+		) ;
+
+		// accounting: count dropped outgoing frames
+		member->text_frames_out_dropped++ ;
+		ast_mutex_unlock(&member->lock);
+		return -1 ;
+	}
+
+	//
+	// create new conf frame from passed data frame
+	//
+
+	conf_frame* cfr = create_conf_frame( member, member->outTextFrames, fr ) ;
+
+	if ( cfr == NULL )
+	{
+		ast_log( LOG_ERROR, "unable to create new conf frame\n" ) ;
+
+		// accounting: count dropped outgoing frames
+		member->text_frames_out_dropped++ ;
+		ast_mutex_unlock(&member->lock);
+		return -1 ;
+	}
+
+#ifdef RTP_SEQNO_ZERO
+	cfr->fr->seqno = 0;
+#endif
+
+	if ( member->outTextFrames == NULL )
+	{
+		// this is the first frame in the buffer
+		member->outTextFramesTail = cfr ;
+		member->outTextFrames = cfr ;
+	}
+	else
+	{
+		// put the new frame at the head of the list
+		member->outTextFrames = cfr ;
+	}
+
+	// increment member frame count
+	member->outTextFramesCount++ ;
+
+	ast_mutex_unlock(&member->lock);
+	// return success
+	return 0 ;
+}
+
+
+//
+// manager functions
+//
+
+void send_state_change_notifications( struct ast_conf_member* member )
+{
+	// ast_log( AST_CONF_DEBUG, "sending state change notification\n" ) ;
+
+	// loop through list of members, sending state changes
+	while ( member != NULL )
+	{
+		// has the state changed since last time through this loop?
+		if ( member->speaking_state_notify )
+		{
+			manager_event(
+				EVENT_FLAG_CALL,
+				"ConferenceState",
+				"Channel: %s\r\n"
+				"State: %s\r\n",
+				member->channel_name,
+				( ( member->speaking_state == 1 ) ? "speaking" : "silent" )
+			) ;
+
+			ast_log( AST_CONF_DEBUG, "member state changed, channel => %s, state => %d, incoming => %d, outgoing => %d\n",
+				member->channel_name, member->speaking_state, member->inFramesCount, member->outFramesCount ) ;
+
+			member->speaking_state_notify = 0;
+		}
+
+		// move the pointer to the next member
+		member = member->next ;
+	}
+
+	return ;
+}
+
+//
+// ast_packer, adapted from ast_smoother
+// pack multiple frames together into one packet on the wire.
+//
+
+#define PACKER_SIZE  8000
+#define PACKER_QUEUE 10 // store at most 10 complete packets in the queue
+
+struct ast_packer {
+	int framesize; // number of frames per packet on the wire.
+	int size;
+	int packet_index;
+	int format;
+	int readdata;
+	int optimizablestream;
+	int flags;
+	float samplesperbyte;
+	struct ast_frame f;
+	struct timeval delivery;
+	char data[PACKER_SIZE];
+	char framedata[PACKER_SIZE + AST_FRIENDLY_OFFSET];
+	int samples;
+	int sample_queue[PACKER_QUEUE];
+	int len_queue[PACKER_QUEUE];
+	struct ast_frame *opt;
+	int len;
+};
+
+void ast_packer_reset(struct ast_packer *s, int framesize)
+{
+	memset(s, 0, sizeof(struct ast_packer));
+	s->framesize = framesize;
+	s->packet_index=0;
+	s->len=0;
+}
+
+struct ast_packer *ast_packer_new(int framesize)
+{
+	struct ast_packer *s;
+	if (framesize < 1)
+		return NULL;
+	s = malloc(sizeof(struct ast_packer));
+	if (s)
+		ast_packer_reset(s, framesize);
+	return s;
+}
+
+int ast_packer_get_flags(struct ast_packer *s)
+{
+	return s->flags;
+}
+
+void ast_packer_set_flags(struct ast_packer *s, int flags)
+{
+	s->flags = flags;
+}
+
+int ast_packer_feed(struct ast_packer *s, const struct ast_frame *f)
+{
+	if (f->frametype != AST_FRAME_VOICE) {
+		ast_log(LOG_WARNING, "Huh?  Can't pack a non-voice frame!\n");
+		return -1;
+	}
+	if (!s->format) {
+		s->format = f->subclass;
+		s->samples=0;
+	} else if (s->format != f->subclass) {
+		ast_log(LOG_WARNING, "Packer was working on %d format frames, now trying to feed %d?\n", s->format, f->subclass);
+		return -1;
+	}
+	if (s->len + f->datalen > PACKER_SIZE) {
+		ast_log(LOG_WARNING, "Out of packer space\n");
+		return -1;
+	}
+	if (s->packet_index >= PACKER_QUEUE ){
+		ast_log(LOG_WARNING, "Out of packer queue space\n");
+		return -1;
+	}
+
+	memcpy(s->data + s->len, f->data, f->datalen);
+	/* If either side is empty, reset the delivery time */
+	if (!s->len || (!f->delivery.tv_sec && !f->delivery.tv_usec) ||
+			(!s->delivery.tv_sec && !s->delivery.tv_usec))
+		s->delivery = f->delivery;
+	s->len += f->datalen;
+//packer stuff
+	s->len_queue[s->packet_index]    += f->datalen;
+	s->sample_queue[s->packet_index] += f->samples;
+	s->samples += f->samples;
+
+	if (s->samples > s->framesize )
+		++s->packet_index;
+
+	return 0;
+}
+
+struct ast_frame *ast_packer_read(struct ast_packer *s)
+{
+	struct ast_frame *opt;
+	int len;
+	/* IF we have an optimization frame, send it */
+	if (s->opt) {
+		opt = s->opt;
+		s->opt = NULL;
+		return opt;
+	}
+
+	/* Make sure we have enough data */
+	if (s->samples < s->framesize ){
+			return NULL;
+	}
+	len = s->len_queue[0];
+	if (len > s->len)
+		len = s->len;
+	/* Make frame */
+	s->f.frametype = AST_FRAME_VOICE;
+	s->f.subclass = s->format;
+	s->f.data = s->framedata + AST_FRIENDLY_OFFSET;
+	s->f.offset = AST_FRIENDLY_OFFSET;
+	s->f.datalen = len;
+	s->f.samples = s->sample_queue[0];
+	s->f.delivery = s->delivery;
+	/* Fill Data */
+	memcpy(s->f.data, s->data, len);
+	s->len -= len;
+	/* Move remaining data to the front if applicable */
+	if (s->len) {
+		/* In principle this should all be fine because if we are sending
+		   G.729 VAD, the next timestamp will take over anyawy */
+		memmove(s->data, s->data + len, s->len);
+		if (s->delivery.tv_sec || s->delivery.tv_usec) {
+			/* If we have delivery time, increment it, otherwise, leave it at 0 */
+			s->delivery.tv_sec +=  s->sample_queue[0] / 8000.0;
+			s->delivery.tv_usec += (((int)(s->sample_queue[0])) % 8000) * 125;
+			if (s->delivery.tv_usec > 1000000) {
+				s->delivery.tv_usec -= 1000000;
+				s->delivery.tv_sec += 1;
+			}
+		}
+	}
+	int j;
+	s->samples -= s->sample_queue[0];
+	if( s->packet_index > 0 ){
+		for (j=0; j<s->packet_index -1 ; j++){
+			s->len_queue[j]=s->len_queue[j+1];
+			s->sample_queue[j]=s->sample_queue[j+1];
+		}
+		s->len_queue[s->packet_index]=0;
+		s->sample_queue[s->packet_index]=0;
+		s->packet_index--;
+	} else {
+		s->len_queue[0]=0;
+		s->sample_queue[0]=0;
+	}
+
+
+	/* Return frame */
+	return &s->f;
+}
+
+void ast_packer_free(struct ast_packer *s)
+{
+	free(s);
+}
+
+int queue_frame_for_listener(
+	struct ast_conference* conf,
+	struct ast_conf_member* member,
+	conf_frame* frame
+)
+{
+	//
+	// check inputs
+	//
+
+	if ( conf == NULL )
+	{
+		ast_log( LOG_WARNING, "unable to queue listener frame with null conference\n" ) ;
+		return -1 ;
+	}
+
+	if ( member == NULL )
+	{
+		ast_log( LOG_WARNING, "unable to queue listener frame with null member\n" ) ;
+		return -1 ;
+	}
+
+	//
+	// loop over spoken frames looking for member's appropriate match
+	//
+
+	short found_flag = 0 ;
+	struct ast_frame* qf ;
+
+	for ( ; frame != NULL ; frame = frame->next )
+	{
+		// we're looking for a null or matching member
+		if ( frame->member != NULL && frame->member != member )
+			continue ;
+
+		if ( frame->fr == NULL )
+		{
+			ast_log( LOG_WARNING, "unknown error queueing frame for listener, frame->fr == NULL\n" ) ;
+			continue ;
+		}
+
+		// first, try for a pre-converted frame
+		qf = frame->converted[ member->write_format_index ] ;
+
+		// convert ( and store ) the frame
+		if ( qf == NULL )
+		{
+			// make a copy of the slinear version of the frame
+			qf = ast_frdup( frame->fr ) ;
+
+			if ( qf == NULL )
+			{
+				ast_log( LOG_WARNING, "unable to duplicate frame\n" ) ;
+				continue ;
+			}
+
+			// convert using the conference's translation path
+			qf = convert_frame_from_slinear( conf->from_slinear_paths[ member->write_format_index ], qf ) ;
+
+			// store the converted frame
+			// ( the frame will be free'd next time through the loop )
+			frame->converted[ member->write_format_index ] = qf ;
+		}
+
+		if ( qf != NULL )
+		{
+			// duplicate the frame before queue'ing it
+			// ( since this member doesn't own this _shared_ frame )
+			// qf = ast_frdup( qf ) ;
+
+
+
+			if ( queue_outgoing_frame( member, qf, conf->delivery_time ) != 0 )
+			{
+				// free the new frame if it couldn't be queue'd
+				// XXX NEILS - WOULD BE FREED IN CLEANUPast_frfree( qf ) ;
+				//qf = NULL ;
+			}
+		}
+		else
+		{
+			ast_log( LOG_WARNING, "unable to translate outgoing listener frame, channel => %s\n", member->channel_name ) ;
+		}
+
+		// set found flag
+		found_flag = 1 ;
+
+		// break from for loop
+		break ;
+	}
+
+	// queue a silent frame
+	if ( found_flag == 0 )
+		queue_silent_frame( conf, member ) ;
+
+	return 0 ;
+}
+
+
+int queue_frame_for_speaker(
+	struct ast_conference* conf,
+	struct ast_conf_member* member,
+	conf_frame* frame
+)
+{
+	//
+	// check inputs
+	//
+
+	if ( conf == NULL )
+	{
+		ast_log( LOG_WARNING, "unable to queue speaker frame with null conference\n" ) ;
+		return -1 ;
+	}
+
+	if ( member == NULL )
+	{
+		ast_log( LOG_WARNING, "unable to queue speaker frame with null member\n" ) ;
+		return -1 ;
+	}
+
+	//
+	// loop over spoken frames looking for member's appropriate match
+	//
+
+	short found_flag = 0 ;
+	struct ast_frame* qf ;
+
+	for ( ; frame != NULL ; frame = frame->next )
+	{
+		if ( frame->member != member )
+		{
+			continue ;
+		}
+
+		if ( frame->fr == NULL )
+		{
+			ast_log( LOG_WARNING, "unable to queue speaker frame with null data\n" ) ;
+			continue ;
+		}
+
+		//
+		// convert and queue frame
+		//
+
+		// short-cut pointer to the ast_frame
+		qf = frame->fr ;
+
+		if ( qf->subclass == member->write_format )
+		{
+			// frame is already in correct format, so just queue it
+
+			queue_outgoing_frame( member, qf, conf->delivery_time ) ;
+		}
+		else
+		{
+			//
+			// convert frame to member's write format
+			// ( calling ast_frdup() to make sure the translator's copy sticks around )
+			//
+			qf = convert_frame_from_slinear( member->from_slinear, ast_frdup( qf ) ) ;
+
+			if ( qf != NULL )
+			{
+				// queue frame
+				queue_outgoing_frame( member, qf, conf->delivery_time ) ;
+
+				// free frame ( the translator's copy )
+				ast_frfree( qf ) ;
+			}
+			else
+			{
+				ast_log( LOG_WARNING, "unable to translate outgoing speaker frame, channel => %s\n", member->channel_name ) ;
+			}
+		}
+
+		// set found flag
+		found_flag = 1 ;
+
+		// we found the frame, skip to the next member
+		break ;
+	}
+
+	// queue a silent frame
+	if ( found_flag == 0 )
+		queue_silent_frame( conf, member ) ;
+
+	return 0 ;
+}
+
+
+int queue_silent_frame(
+	struct ast_conference* conf,
+	struct ast_conf_member* member
+)
+{
+  int c;
+#ifdef APP_CONFERENCE_DEBUG
+	//
+	// check inputs
+	//
+
+	if ( conf == NULL )
+	{
+		ast_log( AST_CONF_DEBUG, "unable to queue silent frame for null conference\n" ) ;
+		return -1 ;
+	}
+
+	if ( member == NULL )
+	{
+		ast_log( AST_CONF_DEBUG, "unable to queue silent frame for null member\n" ) ;
+		return -1 ;
+	}
+#endif // APP_CONFERENCE_DEBUG
+
+	//
+	// initialize static variables
+	//
+
+	static conf_frame* silent_frame = NULL ;
+	static struct ast_frame* qf = NULL ;
+
+	if ( silent_frame == NULL )
+	{
+		if ( ( silent_frame = get_silent_frame() ) == NULL )
+		{
+			ast_log( LOG_WARNING, "unable to initialize static silent frame\n" ) ;
+			return -1 ;
+		}
+	}
+
+
+	// get the appropriate silent frame
+	qf = silent_frame->converted[ member->write_format_index ] ;
+
+	if ( qf == NULL )
+	{
+		//
+		// we need to do this to avoid echo on the speaker's line.
+		// translators seem to be single-purpose, i.e. they
+		// can't be used simultaneously for multiple audio streams
+		//
+
+		struct ast_trans_pvt* trans = ast_translator_build_path( member->write_format, AST_FORMAT_SLINEAR ) ;
+
+		if ( trans != NULL )
+		{
+			// attempt ( five times ) to get a silent frame
+			// to make sure we provice the translator with enough data
+			for ( c = 0 ; c < 5 ; ++c )
+			{
+				// translate the frame
+				qf = ast_translate( trans, silent_frame->fr, 0 ) ;
+
+				// break if we get a frame
+				if ( qf != NULL ) break ;
+			}
+
+			if ( qf != NULL )
+			{
+				// isolate the frame so we can keep it around after trans is free'd
+				qf = ast_frisolate( qf ) ;
+
+				// cache the new, isolated frame
+				silent_frame->converted[ member->write_format_index ] = qf ;
+			}
+
+			ast_translator_free_path( trans ) ;
+		}
+	}
+
+	//
+	// queue the frame, if it's not null,
+	// otherwise there was an error
+	//
+	if ( qf != NULL )
+	{
+		queue_outgoing_frame( member, qf, conf->delivery_time ) ;
+	}
+	else
+	{
+		ast_log( LOG_ERROR, "unable to translate outgoing silent frame, channel => %s\n", member->channel_name ) ;
+	}
+
+	return 0 ;
+}
+
+
+
+void member_process_outgoing_frames(struct ast_conference* conf,
+				  struct ast_conf_member *member,
+				  struct conf_frame *send_frames)
+{
+	ast_mutex_lock(&member->lock);
+
+	// skip members that are not ready
+	if ( member->ready_for_outgoing == 0 )
+	{
+		ast_mutex_unlock(&member->lock);
+		return ;
+	}
+
+	// skip no receive audio clients
+	if ( member->norecv_audio )
+	{
+		ast_mutex_unlock(&member->lock);
+		return;
+	}
+
+	if ( member->local_speaking_state == 0 )
+	{
+		// queue listener frame
+		queue_frame_for_listener( conf, member, send_frames ) ;
+	}
+	else
+	{
+		// queue speaker frame
+		queue_frame_for_speaker( conf, member, send_frames ) ;
+	}
+	ast_mutex_unlock(&member->lock);
+}
+
+// Functions that will increase and decrease speaker_count in a secure way, locking the member mutex if required
+// Will also set speaking_state flag.
+// Returns the previous speaking state
+int increment_speaker_count(struct ast_conf_member *member, int lock)
+{
+	int old_state;
+
+	if ( lock )
+		ast_mutex_lock(&member->lock);
+
+	old_state = member->speaking_state;
+	member->speaker_count++;
+	member->speaking_state = 1;
+
+	ast_log(AST_CONF_DEBUG, "Increment speaker count: id=%d, count=%d\n", member->id, member->speaker_count);
+
+	// If this is a state change, update the timestamp
+	if ( old_state == 0 )
+	{
+		member->speaking_state_notify = 1;
+		member->last_state_change = ast_tvnow();
+	}
+
+	if ( lock )
+		ast_mutex_unlock(&member->lock);
+
+	return old_state;
+}
+
+int decrement_speaker_count(struct ast_conf_member *member, int lock)
+{
+	int old_state;
+
+	if ( lock )
+		ast_mutex_lock(&member->lock);
+
+	old_state = member->speaking_state;
+	if ( member->speaker_count > 0 )
+		member->speaker_count--;
+	if ( member->speaker_count == 0 )
+		member->speaking_state = 0;
+
+	ast_log(AST_CONF_DEBUG, "Decrement speaker count: id=%d, count=%d\n", member->id, member->speaker_count);
+
+	// If this is a state change, update the timestamp
+	if ( old_state == 1 && member->speaking_state == 0 )
+	{
+		member->speaking_state_notify = 1;
+		member->last_state_change = ast_tvnow();
+	}
+
+	if ( lock )
+		ast_mutex_unlock(&member->lock);
+
+	return old_state;
+}
+
+void member_process_spoken_frames(struct ast_conference* conf,
+				 struct ast_conf_member *member,
+				 struct conf_frame **spoken_frames,
+				 long time_diff,
+				 int *listener_count,
+				 int *speaker_count
+	)
+{
+	struct conf_frame *cfr;
+
+	// acquire member mutex
+	TIMELOG(ast_mutex_lock( &member->lock ),1,"conf thread member lock") ;
+
+	// check for dead members
+	if ( member->remove_flag == 1 )
+	{
+		// If this member is the default video source for the conference, then change the default to -1
+		if ( member->id == conf->default_video_source_id )
+			conf->default_video_source_id = -1;
+
+		if (conf->debug_flag)
+		{
+			ast_log( LOG_NOTICE, "found member slated for removal, channel => %s\n", member->channel_name ) ;
+		}
+		remove_member( member, conf ) ;
+		return;
+	}
+
+	// tell member the number of frames we're going to need ( used to help dropping algorithm )
+	member->inFramesNeeded = ( time_diff / AST_CONF_FRAME_INTERVAL ) - 1 ;
+
+	// !!! TESTING !!!
+	if (
+		conf->debug_flag == 1
+		&& member->inFramesNeeded > 0
+		)
+	{
+		ast_log( AST_CONF_DEBUG, "channel => %s, inFramesNeeded => %d, inFramesCount => %d\n",
+			 member->channel_name, member->inFramesNeeded, member->inFramesCount ) ;
+	}
+
+	// non-listener member should have frames,
+	// unless silence detection dropped them
+	cfr = get_incoming_frame( member ) ;
+
+	// handle retrieved frames
+	if ( cfr == NULL || cfr->fr == NULL )
+	{
+		// Decrement speaker count for us and for driven members
+		// This happens only for the first missed frame, since we want to
+		// decrement only on state transitions
+		if ( member->local_speaking_state == 1 )
+		{
+			decrement_speaker_count(member, 0);
+			member->local_speaking_state = 0;
+			// If we're driving another member, decrement its speaker count as well
+			if ( member->driven_member != NULL )
+				decrement_speaker_count(member->driven_member, 1);
+		}
+
+		// count the listeners
+		(*listener_count)++ ;
+	}
+	else
+	{
+		// append the frame to the list of spoken frames
+		if ( *spoken_frames != NULL )
+		{
+			// add new frame to end of list
+			cfr->next = *spoken_frames ;
+			(*spoken_frames)->prev = cfr ;
+		}
+
+		// point the list at the new frame
+		*spoken_frames = cfr ;
+
+		// Increment speaker count for us and for driven members
+		// This happens only on the first received frame, since we want to
+		// increment only on state transitions
+		if ( member->local_speaking_state == 0 )
+		{
+			increment_speaker_count(member, 0);
+			member->local_speaking_state = 1;
+
+			// If we're driving another member, increment its speaker count as well
+			if ( member->driven_member != NULL )
+				increment_speaker_count(member->driven_member, 1);
+		}
+
+		// count the speakers
+		(*speaker_count)++ ;
+	}
+
+	// release member mutex
+	ast_mutex_unlock( &member->lock ) ;
+
+	return;
+}
diff --git a/apps/conference/member.h b/apps/conference/member.h
new file mode 100644
index 0000000..f4cae3f
--- /dev/null
+++ b/apps/conference/member.h
@@ -0,0 +1,315 @@
+
+// $Id: member.h 872 2007-03-05 23:43:10Z sbalea $
+
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <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_MEMBER_H
+#define _APP_CONF_MEMBER_H
+
+//
+// includes
+//
+
+#include "app_conference.h"
+#include "common.h"
+
+//
+// struct declarations
+//
+
+struct ast_conf_soundq
+{
+	char name[256];
+	struct ast_filestream *stream; // the stream
+	int muted; // should incoming audio be muted while we play?
+	struct ast_conf_soundq *next;
+};
+
+struct ast_conf_member
+{
+	ast_mutex_t lock ; // member data mutex
+
+	struct ast_channel* chan ; // member's channel
+	char* channel_name ; // member's channel name
+
+	// values passed to create_member () via *data
+	int priority ;	// highest priority gets the channel
+	char* flags ;	// raw member-type flags
+	char type ;		// L = ListenOnly, M = Moderator, S = Standard (Listen/Talk)
+	char* conf_name ;		// name of the conference that own this member
+
+	char *callerid;
+	char *callername;
+
+	// voice flags
+	int vad_flag;
+	int denoise_flag;
+	int agc_flag;
+	int via_telephone;
+
+	// video conference params
+	int id;
+	int initial_id;
+	int req_id;
+
+	// muting options - this member will not be heard/seen
+	int mute_audio;
+	int mute_video;
+
+	// this member will not hear/see
+	int norecv_audio;
+	int norecv_video;
+
+	// this member does not have a camera
+	int no_camera;
+
+	// is this person a moderator?
+	int ismoderator;
+
+	// determine by flags and channel name
+	char connection_type ; // T = telephone, X = iaxclient, S = sip
+
+	// vad voice probability thresholds
+	float vad_prob_start ;
+	float vad_prob_continue ;
+
+	// ready flag
+	short ready_for_outgoing ;
+
+	// input frame queue
+	conf_frame* inFrames ;
+	conf_frame* inFramesTail ;
+	unsigned int inFramesCount ;
+	conf_frame* inVideoFrames ;
+	conf_frame* inVideoFramesTail ;
+	unsigned int inVideoFramesCount ;
+	conf_frame* inDTMFFrames ;
+	conf_frame* inDTMFFramesTail ;
+	unsigned int inDTMFFramesCount ;
+	conf_frame* inTextFrames ;
+	conf_frame* inTextFramesTail ;
+	unsigned int inTextFramesCount ;
+
+
+	// input/output smoother
+	struct ast_smoother *inSmoother;
+	struct ast_packer *outPacker;
+	int smooth_size_in;
+	int smooth_size_out;
+	int smooth_multiple;
+
+	// frames needed by conference_exec
+	unsigned int inFramesNeeded ;
+	unsigned int inVideoFramesNeeded ;
+
+	// used when caching last frame
+	conf_frame* inFramesLast ;
+	unsigned int inFramesRepeatLast ;
+	unsigned short okayToCacheLast ;
+
+	// LL output frame queue
+	conf_frame* outFrames ;
+	conf_frame* outFramesTail ;
+	unsigned int outFramesCount ;
+	conf_frame* outVideoFrames ;
+	conf_frame* outVideoFramesTail ;
+	unsigned int outVideoFramesCount ;
+	conf_frame* outDTMFFrames ;
+	conf_frame* outDTMFFramesTail ;
+	unsigned int outDTMFFramesCount ;
+	conf_frame* outTextFrames ;
+	conf_frame* outTextFramesTail ;
+	unsigned int outTextFramesCount ;
+
+	// LL video switched flag
+	short conference;
+
+	// switch video by VAD?
+	short vad_switch;
+	// switch by dtmf?
+	short dtmf_switch;
+	// relay dtmf to manager?
+	short dtmf_relay;
+	// initial nat delay flag
+	short first_frame_received;
+	// does text messages?
+	short does_text;
+
+
+	// time we last dropped a frame
+	struct timeval last_in_dropped ;
+	struct timeval last_out_dropped ;
+
+	// ( not currently used )
+	// int samplesperframe ;
+
+	// used for determining need to mix frames
+	// and for management interface notification
+	// and for VAD based video switching
+	short speaking_state_notify ;
+	short speaking_state ; // This flag will be true if this member or any of its drivers is speaking
+	short local_speaking_state; // This flag will be true only if this member is speaking
+	struct timeval last_state_change;
+	int speaker_count; // Number of drivers (including this member) that are speaking
+
+	// pointer to next member in single-linked list
+	struct ast_conf_member* next ;
+
+	// accounting values
+	unsigned long frames_in ;
+	unsigned long frames_in_dropped ;
+	unsigned long frames_out ;
+	unsigned long frames_out_dropped ;
+
+	unsigned long video_frames_in ;
+	unsigned long video_frames_in_dropped ;
+	unsigned long video_frames_out ;
+	unsigned long video_frames_out_dropped ;
+
+	unsigned long dtmf_frames_in ;
+	unsigned long dtmf_frames_in_dropped ;
+	unsigned long dtmf_frames_out ;
+	unsigned long dtmf_frames_out_dropped ;
+
+	unsigned long text_frames_in ;
+	unsigned long text_frames_in_dropped ;
+	unsigned long text_frames_out ;
+	unsigned long text_frames_out_dropped ;
+
+	// for counting sequentially dropped frames
+	unsigned int sequential_drops ;
+	unsigned long since_dropped ;
+
+	// start time
+	struct timeval time_entered ;
+	struct timeval lastsent_timeval ;
+
+	// flag indicating we should remove this member
+	short remove_flag ;
+	short kick_flag ;
+
+#if ( SILDET == 2 )
+	// pointer to speex preprocessor dsp
+	SpeexPreprocessState *dsp ;
+        // number of frames to ignore speex_preprocess()
+	int ignore_speex_count;
+#else
+	// placeholder when preprocessing is not enabled
+	void* dsp ;
+#endif
+
+	// audio format this member is using
+	int write_format ;
+	int read_format ;
+
+	int write_format_index ;
+	int read_format_index ;
+
+	// member frame translators
+	struct ast_trans_pvt* to_slinear ;
+	struct ast_trans_pvt* from_slinear ;
+
+	// For playing sounds
+	struct ast_conf_soundq *soundq;
+	struct ast_conf_soundq *videoq;
+	
+	// Enter/leave sounds
+	char * enter_snd;
+	char * leave_snd;
+
+	// Pointer to another member that will be driven from this member's audio
+	struct ast_conf_member *driven_member;
+} ;
+
+struct conf_member
+{
+	struct ast_conf_member* realmember ;
+	struct conf_member* next ;
+} ;
+
+//
+// function declarations
+//
+
+int member_exec( struct ast_channel* chan, void* data ) ;
+
+struct ast_conf_member* check_active_video( int id, struct ast_conference *conf );
+
+struct ast_conf_member* create_member( struct ast_channel* chan, const char* data ) ;
+struct ast_conf_member* delete_member( struct ast_conf_member* member ) ;
+
+// incoming queue
+int queue_incoming_frame( struct ast_conf_member* member, struct ast_frame* fr ) ;
+int queue_incoming_video_frame( struct ast_conf_member* member, const struct ast_frame* fr ) ;
+int queue_incoming_dtmf_frame( struct ast_conf_member* member, const struct ast_frame* fr ) ;
+conf_frame* get_incoming_frame( struct ast_conf_member* member ) ;
+conf_frame* get_incoming_video_frame( struct ast_conf_member* member ) ;
+conf_frame* get_incoming_dtmf_frame( struct ast_conf_member* member ) ;
+
+// outgoing queue
+int queue_outgoing_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery ) ;
+int __queue_outgoing_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery ) ;
+conf_frame* get_outgoing_frame( struct ast_conf_member* member ) ;
+
+int queue_outgoing_video_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery ) ;
+conf_frame* get_outgoing_video_frame( struct ast_conf_member* member ) ;
+int queue_outgoing_dtmf_frame( struct ast_conf_member* member, const struct ast_frame* fr ) ;
+int queue_outgoing_text_frame( struct ast_conf_member* member, const struct ast_frame* fr ) ;
+conf_frame* get_outgoing_dtmf_frame( struct ast_conf_member* member ) ;
+conf_frame* get_outgoing_text_frame( struct ast_conf_member* member ) ;
+
+void send_state_change_notifications( struct ast_conf_member* member ) ;
+
+int increment_speaker_count(struct ast_conf_member *member, int lock);
+int decrement_speaker_count(struct ast_conf_member *member, int lock);
+
+void member_process_spoken_frames(struct ast_conference* conf,
+				  struct ast_conf_member *member,
+				  struct conf_frame **spoken_frames,
+				  long time_diff,
+				 int *listener_count,
+				 int *speaker_count);
+
+void member_process_outgoing_frames(struct ast_conference* conf,
+				    struct ast_conf_member *member,
+				    struct conf_frame *send_frames);
+
+//
+// packer functions
+//
+
+struct ast_packer;
+
+extern struct ast_packer *ast_packer_new(int bytes);
+extern void ast_packer_set_flags(struct ast_packer *packer, int flags);
+extern int ast_packer_get_flags(struct ast_packer *packer);
+extern void ast_packer_free(struct ast_packer *s);
+extern void ast_packer_reset(struct ast_packer *s, int bytes);
+extern int ast_packer_feed(struct ast_packer *s, const struct ast_frame *f);
+extern struct ast_frame *ast_packer_read(struct ast_packer *s);
+#endif
-- 
1.5.3.4