tomh / rpms / asterisk

Forked from rpms/asterisk 6 years ago
Clone
Blob Blame History Raw
From b5c3052fe56ef2dccde4b3aeb11ee1dfc237d2b1 Mon Sep 17 00:00:00 2001
From: Jeffrey C. Ollie <jeff@ocjtech.us>
Date: Mon, 18 Feb 2008 08:58:05 -0600
Subject: [PATCH] Latest updates for app_conference.

---
 apps/Makefile                      |   14 +
 apps/app_conference.c              |  113 ++
 apps/conference/CLI.txt            |   95 +
 apps/conference/Flags.txt          |   34 +
 apps/conference/LICENSE            |  341 ++++
 apps/conference/README             |  125 ++
 apps/conference/README.videoswitch |   85 +
 apps/conference/TODO               |    4 +
 apps/conference/app_conference.h   |  246 +++
 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       | 3021 ++++++++++++++++++++++++++++++++
 apps/conference/conference.h       |  190 ++
 apps/conference/frame.c            |  666 +++++++
 apps/conference/frame.h            |   75 +
 apps/conference/member.c           | 3375 ++++++++++++++++++++++++++++++++++++
 apps/conference/member.h           |  336 ++++
 19 files changed, 10220 insertions(+), 0 deletions(-)
 create mode 100644 apps/app_conference.c
 create mode 100644 apps/conference/CLI.txt
 create mode 100644 apps/conference/Flags.txt
 create mode 100644 apps/conference/LICENSE
 create mode 100644 apps/conference/README
 create mode 100644 apps/conference/README.videoswitch
 create mode 100644 apps/conference/TODO
 create mode 100644 apps/conference/app_conference.h
 create mode 100644 apps/conference/cli.c
 create mode 100644 apps/conference/cli.h
 create mode 100644 apps/conference/common.h
 create mode 100644 apps/conference/conf_frame.h
 create mode 100644 apps/conference/conference.c
 create mode 100644 apps/conference/conference.h
 create mode 100644 apps/conference/frame.c
 create mode 100644 apps/conference/frame.h
 create mode 100644 apps/conference/member.c
 create mode 100644 apps/conference/member.h

diff --git a/apps/Makefile b/apps/Makefile
index 4006c9a..cf33505 100644
--- a/apps/Makefile
+++ b/apps/Makefile
@@ -44,4 +44,18 @@ endif
 
 all: _all
 
+app_conference.o: ASTCFLAGS+=-DSILDET=2 -Iconference
+
+conference/conference.o: ASTCFLAGS+=-DSILDET=2 -Iconference
+
+conference/member.o: ASTCFLAGS+=-DSILDET=2 -Iconference
+
+conference/frame.o: ASTCFLAGS+=-DSILDET=2 -Iconference
+
+conference/cli.o: ASTCFLAGS+=-DSILDET=2 -Iconference
+
+app_conference.so: app_conference.o conference/conference.o conference/member.o conference/frame.o conference/cli.o
+	$(ECHO_PREFIX) echo "   [LD] $^ -> $@"
+	$(CMD_PREFIX) $(CXX) $(PTHREAD_CFLAGS) $(ASTLDFLAGS) $(SOLINK) -o $@ $^ -lspeex
+
 include $(ASTTOPDIR)/Makefile.moddir_rules
diff --git a/apps/app_conference.c b/apps/app_conference.c
new file mode 100644
index 0000000..824d5dd
--- /dev/null
+++ b/apps/app_conference.c
@@ -0,0 +1,113 @@
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <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..d54f9d1
--- /dev/null
+++ b/apps/conference/CLI.txt
@@ -0,0 +1,95 @@
+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..deef45d
--- /dev/null
+++ b/apps/conference/Flags.txt
@@ -0,0 +1,34 @@
+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'.
+'z' : member can "linger". When the member is currently transmitting video and becomes silent and nobody else is speaking, we stay on it.
+'o' : enable special behavior when in 1 and 2 member situation (one on one video). The conference observes the 'o' status of the last
+      member to join it
+'F' : force switch mode: if the member is talking, force a switch to it even when there is no video
+
+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..5a29d2c
--- /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..ccc0978
--- /dev/null
+++ b/apps/conference/README.videoswitch
@@ -0,0 +1,85 @@
+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..03ed638
--- /dev/null
+++ b/apps/conference/app_conference.h
@@ -0,0 +1,246 @@
+
+// $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
+
+// Amount of time we wait for a video frame until we decide that
+// the member has stopped broadcasting video
+#define AST_CONF_VIDEO_STOP_BROADCAST_TIMEOUT 200
+
+//
+// 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..84b14a9
--- /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..c7c63ac
--- /dev/null
+++ b/apps/conference/conference.c
@@ -0,0 +1,3021 @@
+
+// $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 ;
+
+// Forward funtcion declarations
+static void do_VAD_switching(struct ast_conference *conf);
+static void do_video_switching(struct ast_conference *conf, int new_id, int lock);
+static struct ast_conference* find_conf(const char* name);
+static struct ast_conference* create_conf(char* name, struct ast_conf_member* member);
+static void remove_conf(struct ast_conference* conf);
+static void add_member(struct ast_conf_member* member, struct ast_conference* conf);
+static int get_new_id(struct ast_conference *conf);
+static int update_member_broadcasting(struct ast_conference *conf, struct ast_conf_member *member, struct conf_frame *cfr, struct timeval time);
+
+//
+// main conference function
+//
+
+static 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 //
+		//-------//
+
+		curr = ast_tvnow();
+
+		// Chat mode handling
+		// If there's only one member, then video gets reflected back to it
+		// If there are two members, then each sees the other's video
+		if ( conf->does_chat_mode &&
+		     conf->membercount > 0 &&
+		     conf->membercount <= 2
+		   )
+		{
+			struct ast_conf_member *m1, *m2;
+
+			m1 = conf->memberlist;
+			m2 = conf->memberlist->next;
+
+			if ( !conf->chat_mode_on )
+				conf->chat_mode_on = 1;
+
+			start_video(m1);
+			if ( m2 != NULL )
+				start_video(m2);
+
+			if ( conf->membercount == 1 )
+			{
+				cfr = get_incoming_video_frame(m1);
+				update_member_broadcasting(conf, m1, cfr, curr);
+				while ( cfr )
+				{
+					queue_outgoing_video_frame(m1, cfr->fr, conf->delivery_time);
+					delete_conf_frame(cfr);
+					cfr = get_incoming_video_frame(m1);
+				}
+			} else if ( conf->membercount == 2 )
+			{
+				cfr = get_incoming_video_frame(m1);
+				update_member_broadcasting(conf, m1, cfr, curr);
+				while ( cfr )
+				{
+					queue_outgoing_video_frame(m2, cfr->fr, conf->delivery_time);
+					delete_conf_frame(cfr);
+					cfr = get_incoming_video_frame(m1);
+				}
+
+				cfr = get_incoming_video_frame(m2);
+				update_member_broadcasting(conf, m2, cfr, curr);
+				while ( cfr )
+				{
+					queue_outgoing_video_frame(m1, cfr->fr, conf->delivery_time);
+					delete_conf_frame(cfr);
+					cfr = get_incoming_video_frame(m2);
+				}
+			}
+		} else
+		{
+			// Generic conference handling (chat mode disabled or more than 2 members)
+			// If we were previously in chat mode, turn it off and stop video from members
+			if ( conf->chat_mode_on )
+			{
+				// Send STOPVIDEO commands to everybody except the current source, if any
+				conf->chat_mode_on = 0;
+				for (member = conf->memberlist; member != NULL; member = member->next)
+				{
+					if ( member->id != conf->current_video_source_id )
+						stop_video(member);
+				}
+			}
+
+			// 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
+			    )
+			{
+				cfr = get_incoming_video_frame(video_source_member);
+				update_member_broadcasting(conf, video_source_member, cfr, curr);
+				while ( cfr )
+				{
+					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);
+					cfr = get_incoming_video_frame(video_source_member);
+				}
+			}
+		}
+
+                //------//
+		// 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* join_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
+static 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
+static 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;
+
+	conf->chat_mode_on = 0;
+	conf->does_chat_mode = 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
+static 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 ;
+}
+
+static 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
+static void add_member( struct ast_conf_member *member, struct ast_conference *conf )
+{
+        int newid, last_id;
+        struct ast_conf_member *othermember;
+
+	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;
+		}
+	}
+
+	// update conference stats
+	conf->membercount++;
+
+	// The conference sets chat mode according to the latest member chat flag
+	conf->does_chat_mode = member->does_chat_mode;
+
+	// check if we're supposed to do chat_mode, and if so, start video on the client
+	if ( conf->does_chat_mode && conf->membercount <= 2 )
+	{
+		start_video(member);
+		conf->chat_mode_on = 1;
+	}
+
+	if ( member->mute_video )
+		stop_video(member);
+
+	// 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
+
+	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 = --conf->membercount;
+
+			// 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;
+			}
+
+			// If the member is broadcasting, we notify that it is no longer the case
+			if ( member->video_broadcast_active )
+			{
+				manager_event(EVENT_FLAG_CALL,
+					"ConferenceVideoBroadcastOff",
+					"ConferenceName: %s\r\nChannel: %s\r\n",
+					conf->name,
+					member->channel_name
+					);
+			}
+
+			// 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 ;
+}
+
+//
+// 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);
+
+			ast_cli(fd, "Chat mode is %s\n", conf->chat_mode_on ? "ON" : "OFF");
+
+			// 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");
+				if ( member->vad_linger ) ast_cli( fd, "z");
+				if ( member->does_chat_mode ) ast_cli( fd, "o" );
+				if ( member->force_vad_switch ) ast_cli( fd, "F" );
+				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
+static void do_VAD_switching(struct ast_conference *conf)
+{
+	struct ast_conf_member *member;
+	struct timeval current_time = ast_tvnow();
+	long longest_speaking = 0;
+	struct ast_conf_member *longest_speaking_member = NULL;
+	int current_silent = 0;
+	int current_linger = 0;
+	int current_no_video = 0;
+	int current_force_switch = 0;
+	int default_no_video = 0;
+	int default_force_switch = 0;
+
+	// 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
+	for ( member = conf->memberlist ;
+	      member != NULL ;
+	      member = member->next )
+	{
+		// 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;
+
+		// Extract the linger and force switch flags of the current video source
+		if ( member->id == conf->current_video_source_id )
+		{
+			current_linger = member->vad_linger;
+			current_force_switch = member->force_vad_switch;
+		}
+
+		if ( member->id == conf->default_video_source_id )
+			default_force_switch = member->force_vad_switch;
+
+		if ( member->no_camera || member->mute_video )
+		{
+			if ( member->id == conf->default_video_source_id )
+				default_no_video = 1;
+
+			if ( member->id == conf->current_video_source_id )
+				current_no_video = 1;
+			else if ( !member->force_vad_switch )
+				continue;
+		}
+
+		// Check if current speaker has been silent for a while
+		if ( member->id == conf->current_video_source_id &&
+		     !member->speaking_state &&
+		     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, but the current member has the vad_linger
+	// flag set, we stay put, otherwise we go to default.  If there's no default, we blank.
+	// 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 (
+	     (conf->current_video_source_id < 0) ||
+	     (current_silent && !current_linger) ||
+	     (current_silent && longest_speaking_member != NULL ) ||
+	     (current_no_video && !current_force_switch)
+	   )
+	{
+		int new_id;
+
+		if ( longest_speaking_member )
+			// Somebody is talking, switch to that member
+			new_id = longest_speaking_member->id;
+		else if ( conf->default_video_source_id &&
+		          (!default_no_video || default_force_switch)
+		        )
+			// No talking, but we have a default that can send video
+			new_id = conf->default_video_source_id;
+		else
+			// No default, switch to empty (-1)
+			new_id = -1;
+
+		do_video_switching(conf, new_id, 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
+static void do_video_switching(struct ast_conference *conf, int new_id, int lock)
+{
+	if ( conf == NULL ) return;
+
+	if ( lock )
+		ast_mutex_lock( &conf->lock );
+
+	// No need to do anything if the current member is the same as the new member
+	if ( new_id != conf->current_video_source_id )
+	{
+		// During chat mode, we don't actually switch members
+		// however, we keep track of who's supposed to be current speaker
+		// so we can switch to it once we get out of chat mode.
+		// We also send VideoSwitch events so anybody monitoring the AMI
+		// can keep track of this
+		struct ast_conf_member *member;
+		struct ast_conf_member *new_member = NULL;
+
+		for ( member = conf->memberlist ; member != NULL ; member = member->next )
+		{
+			if ( member->id == conf->current_video_source_id )
+			{
+				if ( !conf->chat_mode_on )
+					stop_video(member);
+			}
+			if ( member->id == new_id )
+			{
+				if ( !conf->chat_mode_on )
+					start_video(member);
+				new_member = member;
+			}
+		}
+
+		manager_event(EVENT_FLAG_CALL,
+			"ConferenceVideoSwitch",
+			"ConferenceName: %s\r\nChannel: %s\r\n",
+			conf->name,
+			new_member == NULL ? "empty" : new_member->channel_name
+			);
+
+		conf->current_video_source_id = new_id;
+	}
+
+	if ( lock )
+		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;
+}
+
+static int update_member_broadcasting(struct ast_conference *conf, struct ast_conf_member *member, struct conf_frame *cfr, struct timeval now)
+{
+	if ( conf == NULL || member == NULL )
+		return 0;
+
+	if ( cfr == NULL &&
+	     member->video_broadcast_active &&
+	     (ast_tvdiff_ms(now, member->last_video_frame_time)) > AST_CONF_VIDEO_STOP_BROADCAST_TIMEOUT
+	   )
+	{
+		member->video_broadcast_active = 0;
+		manager_event(EVENT_FLAG_CALL,
+				"ConferenceVideoBroadcastOff",
+				"ConferenceName: %s\r\nChannel: %s\r\n",
+				conf->name,
+				member->channel_name
+				);
+	} else if ( cfr != NULL )
+	{
+		member->last_video_frame_time = now;
+		if ( !member->video_broadcast_active )
+		{
+			member->video_broadcast_active = 1;
+			manager_event(EVENT_FLAG_CALL,
+				"ConferenceVideoBroadcastOn",
+				"ConferenceName: %s\r\nChannel: %s\r\n",
+				conf->name,
+				member->channel_name
+				);
+		}
+	}
+
+	return member->video_broadcast_active;
+}
diff --git a/apps/conference/conference.h b/apps/conference/conference.h
new file mode 100644
index 0000000..1b2b4bd
--- /dev/null
+++ b/apps/conference/conference.h
@@ -0,0 +1,190 @@
+
+// $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 ;
+
+	// the conference does chat mode: special treatment for situations with 1 and 2 members
+	short does_chat_mode;
+
+	// chat mode is on;
+	short chat_mode_on;
+
+	// 1 => on, 0 => off
+	short debug_flag ;
+} ;
+
+
+#include "member.h"
+
+//
+// function declarations
+//
+
+struct ast_conference* join_conference( struct ast_conf_member* member ) ;
+
+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 remove_member( struct ast_conf_member* member, struct ast_conference* conf ) ;
+
+int send_text_message_to_member(struct ast_conf_member *member, const char *text);
+
+// 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..9e23672
--- /dev/null
+++ b/apps/conference/frame.c
@@ -0,0 +1,666 @@
+
+// $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..535cb82
--- /dev/null
+++ b/apps/conference/member.c
@@ -0,0 +1,3375 @@
+
+// $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 = join_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->last_state_change = (struct timeval){0, 0};
+	member->speaker_count = 0;
+	member->driven_member = NULL;
+
+	member->video_broadcast_active = 0;
+	member->last_video_frame_time = (struct timeval){0, 0};
+
+	member->video_started = 0;
+
+	// 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, z, o, F
+			// 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 'F':
+				member->force_vad_switch = 1;
+				break;
+			case 'M':
+				member->ismoderator = 1;
+				break;
+			case 'N':
+				member->no_camera = 1;
+				break;
+			case 't':
+				member->does_text = 1;
+				break;
+			case 'z':
+				member->vad_linger = 1;
+				break;
+			case 'o':
+				member->does_chat_mode = 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;
+}
+
+int is_video_eligible(struct ast_conf_member *member)
+{
+	if ( member == NULL )
+		return 0;
+
+	return !member->no_camera && !member->mute_video && !member->via_telephone;
+}
+
+// Member start and stop video methods
+void start_video(struct ast_conf_member *member)
+{
+	if ( member == NULL || member->video_started || !is_video_eligible(member))
+		return;
+
+	send_text_message_to_member(member, AST_CONF_CONTROL_START_VIDEO);
+	member->video_started = 1;
+}
+
+void stop_video(struct ast_conf_member *member)
+{
+	if ( member == NULL || !member->video_started )
+		return;
+
+	send_text_message_to_member(member, AST_CONF_CONTROL_STOP_VIDEO);
+	member->video_started = 0;
+
+}
diff --git a/apps/conference/member.h b/apps/conference/member.h
new file mode 100644
index 0000000..52786d9
--- /dev/null
+++ b/apps/conference/member.h
@@ -0,0 +1,336 @@
+
+// $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;
+	// do a VAD switch even if video is not enabled?
+	short force_vad_switch;
+	// if member is current speaker, video will stay on it when it becomes silent
+	short vad_linger;
+	// 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;
+	// conference does chat mode (1 on 1 video when two members in conference)
+	short does_chat_mode;
+
+
+	// 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
+
+	// Stuff used to determine video broadcast state
+	// This member's video is sent out to at least one member of the conference
+	short video_broadcast_active;
+	// Time when we last sent out a video frame from this member
+	struct timeval last_video_frame_time;
+
+	// Is the member supposed to be transmitting video?
+	short video_started;
+
+	// 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);
+
+int is_video_eligible(struct ast_conf_member *member);
+
+// Member start and stop video methods
+void start_video(struct ast_conf_member *member);
+void stop_video(struct ast_conf_member *member);
+
+//
+// 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.4.1