From a6e3ccb8909e4b3fb8d9a1decd501c1e531b0b83 Mon Sep 17 00:00:00 2001
From: Jeffrey C. Ollie <jeff@ocjtech.us>
Date: Sun, 18 Nov 2007 22:23:17 -0600
Subject: [PATCH] Add app_conference rev 911
---
apps/Makefile | 14 +
apps/app_conference.c | 113 ++
apps/conference/CLI.txt | 96 +
apps/conference/Flags.txt | 31 +
apps/conference/LICENSE | 341 ++++
apps/conference/README | 125 ++
apps/conference/README.videoswitch | 87 +
apps/conference/TODO | 4 +
apps/conference/app_conference.h | 244 +++
apps/conference/cli.c | 1265 ++++++++++++++
apps/conference/cli.h | 99 ++
apps/conference/common.h | 63 +
apps/conference/conf_frame.h | 73 +
apps/conference/conference.c | 2923 +++++++++++++++++++++++++++++++
apps/conference/conference.h | 194 +++
apps/conference/frame.c | 679 ++++++++
apps/conference/frame.h | 75 +
apps/conference/member.c | 3332 ++++++++++++++++++++++++++++++++++++
apps/conference/member.h | 315 ++++
19 files changed, 10073 insertions(+), 0 deletions(-)
create mode 100644 apps/app_conference.c
create mode 100644 apps/conference/CLI.txt
create mode 100644 apps/conference/Flags.txt
create mode 100644 apps/conference/LICENSE
create mode 100644 apps/conference/README
create mode 100644 apps/conference/README.videoswitch
create mode 100644 apps/conference/TODO
create mode 100644 apps/conference/app_conference.h
create mode 100644 apps/conference/cli.c
create mode 100644 apps/conference/cli.h
create mode 100644 apps/conference/common.h
create mode 100644 apps/conference/conf_frame.h
create mode 100644 apps/conference/conference.c
create mode 100644 apps/conference/conference.h
create mode 100644 apps/conference/frame.c
create mode 100644 apps/conference/frame.h
create mode 100644 apps/conference/member.c
create mode 100644 apps/conference/member.h
diff --git a/apps/Makefile b/apps/Makefile
index d0e9215..0722e82 100644
--- a/apps/Makefile
+++ b/apps/Makefile
@@ -38,4 +38,18 @@ endif
all: _all
+app_conference.o: ASTCFLAGS+=-DSILDET=2 -Iconference
+
+conference/conference.o: ASTCFLAGS+=-DSILDET=2 -Iconference
+
+conference/member.o: ASTCFLAGS+=-DSILDET=2 -Iconference
+
+conference/frame.o: ASTCFLAGS+=-DSILDET=2 -Iconference
+
+conference/cli.o: ASTCFLAGS+=-DSILDET=2 -Iconference
+
+app_conference.so: app_conference.o conference/conference.o conference/member.o conference/frame.o conference/cli.o
+ $(ECHO_PREFIX) echo " [LD] $^ -> $@"
+ $(CMD_PREFIX) $(CXX) $(PTHREAD_CFLAGS) $(ASTLDFLAGS) $(SOLINK) -o $@ $^ -lspeex
+
include $(ASTTOPDIR)/Makefile.moddir_rules
diff --git a/apps/app_conference.c b/apps/app_conference.c
new file mode 100644
index 0000000..824d5dd
--- /dev/null
+++ b/apps/app_conference.c
@@ -0,0 +1,113 @@
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
+ *
+ * Video Conferencing support added by
+ * Neil Stratford <neils@vipadia.com>
+ * Copyright (C) 2005, 2005 Vipadia Limited
+ *
+ * VAD driven video conferencing, text message support
+ * and miscellaneous enhancements added by
+ * Mihai Balea <mihai at hates dot ms>
+ *
+ * This program may be modified and distributed under the
+ * terms of the GNU General Public License. You should have received
+ * a copy of the GNU General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc.
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "asterisk.h"
+
+// SVN revision number, provided by make
+#ifndef REVISION
+#define REVISION "unknown"
+#endif
+
+static char *revision = REVISION;
+
+ASTERISK_FILE_VERSION(__FILE__, REVISION)
+
+#include "app_conference.h"
+#include "common.h"
+
+/*
+ * a conference has n + 1 threads, where n is the number of
+ * members and 1 is a conference thread which sends audio
+ * back to the members.
+ *
+ * each member thread reads frames from the channel and
+ * add's them to the member's frame queue.
+ *
+ * the conference thread reads frames from each speaking members
+ * queue, mixes them, and then re-queues them for the member thread
+ * to send back to the user.
+ */
+
+static char *app = "Conference";
+static char *synopsis = "Channel Independent Conference";
+static char *descrip = "Channel Independent Conference Application";
+
+static int app_conference_main(struct ast_channel* chan, void* data)
+{
+ int res ;
+ struct ast_module_user *u ;
+
+ u = ast_module_user_add(chan);
+
+ // call member thread function
+ res = member_exec( chan, data ) ;
+
+ ast_module_user_remove(u);
+
+ return res ;
+}
+
+static int unload_module( void )
+{
+ ast_log( LOG_NOTICE, "unloading app_conference module\n" ) ;
+
+ ast_module_user_hangup_all();
+
+ unregister_conference_cli() ;
+
+ return ast_unregister_application( app ) ;
+}
+
+static int load_module( void )
+{
+ ast_log( LOG_NOTICE, "Loading app_conference module, revision=%s\n", revision) ;
+
+ init_conference() ;
+
+ register_conference_cli() ;
+
+ return ast_register_application( app, app_conference_main, synopsis, descrip ) ;
+}
+
+// increment a timeval by ms milliseconds
+void add_milliseconds(struct timeval* tv, long ms)
+{
+ // add the microseconds to the microseconds field
+ tv->tv_usec += ( ms * 1000 ) ;
+
+ // calculate the number of seconds to increment
+ long s = ( tv->tv_usec / 1000000 ) ;
+
+ // adjust the microsends field
+ if ( s > 0 ) tv->tv_usec -= ( s * 1000000 ) ;
+
+ // increment the seconds field
+ tv->tv_sec += s ;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY,
+ "Channel Independent Conference Application");
diff --git a/apps/conference/CLI.txt b/apps/conference/CLI.txt
new file mode 100644
index 0000000..48acf5a
--- /dev/null
+++ b/apps/conference/CLI.txt
@@ -0,0 +1,96 @@
+Current command line used by app_conference
+Please note that app_conference is still work in progress, so this document might be outdated. As always, the source code is the definitive reference (cli.[ch] and conference.[ch]). You can also obtain help/usage information by using Asterisk CLI help system ("help conference")
+
+A member in a conference can be referred to by its id or by its channel. Id is a positive number assigned automatically when the member joins the conference. Channel is Asterisk channel identifier. To obtain a list of member ids and channels in a conference, do:
+
+*CLI> conference list <conference name>
+
+
+- conference debug: enable debugging for a conference
+ usage: conference debug <conference_name> [ on | off ]
+
+- conference end: stops a conference
+ usage: conference end <conference name>
+
+- conference kick: kick member from a conference
+ usage: conference kick <conference_name> <member id>
+
+- conference kickchannel: kick channel from a conference
+ usage: conference kickchannel <conference_name> <channel>
+
+- conference list: list members of a conference. If no conference is specified, all conferences are listed
+ usage: conference list {conference_name}
+
+- conference lock: locks incoming video to a member
+ usage: conference lock <conference name> <member id>
+
+- conference lockchannel: locks incoming video to a channel
+ usage: conference lockchannel <conference name> <channel>
+
+- conference mute: mute member in a conference
+ usage: conference mute <conference_name> <member id>
+
+- conference mutechannel: mute channel in a conference
+ usage: conference mutechannel <channel>
+
+- conference play sound: play a sound to a conference member
+ usage: conference play sound <channel-id> <sound-file> [mute]
+ If mute is specified, all other audio is muted while the sound is played back.
+
+- conference restart: kick all users in all conferences
+ usage: conference restart
+
+- conference set default: sets default video source
+ usage: conference set default <conference name> <member>
+ use a negative value for member if you want to clear the default
+
+- conference set defaultchannel: sets default video source channel
+ usage: conference set defaultchannel <conference name> <channel>
+
+- conference show stats: show conference stats
+ usage: conference show stats
+
+- conference text: sends a text message to a member. Depends on the member's channel capabilities.
+ usage: conference text <conference name> <member> <text>
+
+- conference textbroadcast: sends a text message to all members in a conference
+ usage: conference textbroadcast <conference name> <text>
+
+- conference textchannel: sends a text message to a channel
+ usage: conference textchannel <conference name> <channel> <text>
+
+- conference unlock: unlocks incoming video
+ usage: conference unlock <conference name>
+
+- conference unmute: unmute member in a conference
+ usage: conference unmute <conference_name> <member id>
+
+- conference unmutechannel: unmute channel in a conference
+ usage: conference unmutechannel <channel>
+
+- conference video mute: mutes video from a member
+ usage: conference video mute <conference name> <member>
+
+- conference video mutechannel: mutes video from a channel
+ usage: conference video mutechannel <conference name> <channel>
+
+- conference video unmute: unmutes video from a member
+ usage: conference video unmute <conference name> <member>
+
+- conference video unmutechannel: unmutes video from a channel
+ usage: conference video unmutechannel <conference name> <channel>
+
+- conference viewchannel: switch video for a channel in a conference
+ usage: conference viewchannel <conference_name> <dest channel> <src channel>
+
+- conference viewstream: switch video for a member a conference
+ usage: conference viewstream <conference_name> <member id> <stream no>
+
+- conference drive: drive VAD video switching of destination member using audio from source member
+ usage: conference drive <conference name> <source member> [destination member]
+ If destination member is missing or negative, break existing connection
+
+- conference drivechannel: drive VAD video switching of destination channel using audio from source channel
+ usage: conference drivechannel <conference name> <source channel> [destination channel]
+ If destination channel is missing, break existing connection
+
diff --git a/apps/conference/Flags.txt b/apps/conference/Flags.txt
new file mode 100644
index 0000000..dbb9732
--- /dev/null
+++ b/apps/conference/Flags.txt
@@ -0,0 +1,31 @@
+Current dialplan flags used by app_conference
+Please note that app_conference is still work in progress, so this document might be outdated. As always, the source code is the definitive reference (member.c in create_member())
+
+Mute/no receive options:
+'C' : member starts with video muted
+'c' : member starts unable to receive video
+'L' : member starts with audio muted
+'l' : member starts unable to receive audio
+
+Speex preprocessing options (right now app_conference does preprocessing only for Zaptel members):
+'V' : enable speex preprocessing Voice Activity Detection
+'D' : enable speex preprocessing De-noise
+'A' : enable speex preprocessing Automatic Gain Control
+'T' : member connects through Zaptel, so speex preprocessing should be enabled
+
+DTMF options:
+'X' : enable DTMF switch: video can be switched by users using DTMF. Do not use with 'S'.
+'R' : enable DTMF relay: DTMF tones generate a manager event
+If neither 'X' nor 'R' are present, DTMF tones will be forwarded to all members in the conference
+
+Moderator/video switch options:
+'M' : member is a "moderator". When a moderator quits, all members are kicked and the conference is disabled.
+'S' : member accepts VAD controlled video switching. Do not use with 'X'.
+
+Miscellaneous:
+'t' : member accepts text based control messages. The messages are described in a separate document
+'N' : Assume that the member starts off with camera disabled.
+
+Future development (these are not implemented yet):
+'x' : marked member. We plan to change the behavior so that when ALL moderators quit, all members that are marked will get kicked. Other members in the conference are not affected.
+
diff --git a/apps/conference/LICENSE b/apps/conference/LICENSE
new file mode 100644
index 0000000..a52b16e
--- /dev/null
+++ b/apps/conference/LICENSE
@@ -0,0 +1,341 @@
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/apps/conference/README b/apps/conference/README
new file mode 100644
index 0000000..4c103ce
--- /dev/null
+++ b/apps/conference/README
@@ -0,0 +1,125 @@
+Introduction
+
+App_conference is a channel-independent conference application.
+It features efficient audio mixing algorithms as well as video selection
+support based on VAD, DTMF or CLI.
+
+
+Design goals
+
+Appconference has several design goals which are different than Meetme:
+
+ * It does not require a zap channel for timing.
+ * It is very efficient when used with channels which support DTX (silence
+ detection/discontinuous transmission).
+
+ * It can do VAD on channels which do not support DTX (although this
+ is more expensive than just mixing them, but less expensive then
+ encoding; therefore it might still be a win).
+ * It presents messages on the Monitor interface for determine which
+ speakers are active.
+
+Mixing design
+
+ * Minimize encoding/decoding, minimize mixing.
+ * Minimize generational loss from trancoding.
+ * Usual cases are handled very efficiently:
+ o One speaker: That speaker's frame is sent directly to each
+ participant which uses the same codec. It is trancoded
+ _once_ for each additional codec type used by participants.
+ o Two speakers: Each speaker gets the other speaker's frames.
+ The two speaker's frames are decoded and mixed, and then
+ encoded _once_ for each codec type used by participants.
+
+Video features
+
+ * Video passthrough: video from selected member is passed to every
+ member of the conference.
+ * Multiple ways to select video
+ - VAD
+ - DTMF from conference members
+ - CLI
+ * Ability to set default video sources and to lock/unlock video sources.
+
+
+License
+
+Naturally, app_conference is GPL. The SVN repository also includes parts of
+libspeex, which is distributed under a BSD-style license. See LICENSE for more
+details.
+
+
+Getting app_conference
+
+app_conference is available via SVN from its own home on sourceforge:
+
+ * http://sourceforge.net/projects/appconference
+
+
+Compiling app_conference
+
+ * Checkout sources
+ * Modify Makefile to point to your Asterisk include directory
+ * make
+ * sudo make install
+
+
+Using app_conference
+
+There is no configuration file. Conferences are created on-the-fly.
+
+Dialplan syntax: Conference(ConferenceName/Flags/Priority[/VADSTART/VADCONTINUE])
+
+ * ConferenceName: Whatever you want to name the conference
+ * Flags: please see Flags.txt for a comprehensive list of dialplan flags
+ * Priority: Currently ignored; was to be a "speaking priority" so a
+ higher priority caller could "override" others.
+ * VADSTART: Optional: "probability" to use to detect start of speech.
+ * VADCONTINUE: Optional: "probability" to use to detect continuation
+ of speech.
+
+
+CLI Commands
+
+Please look at CLI.txt for a comprehensive list of CLI commands and parameters.
+
+
+Manager Events
+
+<This section is outdated>
+app_conference generates several detailed manager events so that applications
+interfacing with the manager API can monitor conferences:
+
+ * ConferenceState: sent as members begin/end speaking.
+ Channel: The channel
+ State: "speaking" or "silent"
+
+ * ConferenceDTMF: sent when conference members send DTMF to the conference
+ Channel: The channel
+ Key: The DTMF key send [0-9*#]
+
+ * ConferenceSoundComplete: send when the conference has finished playing
+ a sound to a user
+ Channel: The channel
+ Sound: The first 255 bytes of the file requested in conference play
+ sound CLI/Mgr command.
+
+
+Benchmarking
+
+It would be nice to have solid benchmarks to present, but a good size
+machine should be able to handle many callers when either (a) they are
+using DTX, or (b) they are listen-only. It's used often with hundreds of
+simultaneous callers.
+
+
+Discussion
+
+The appconference-devel mailing list is the place to discuss everything related
+to app_conference. The bug tracker on SourceForge gets a little bit of
+attention now and then.
+
+
+---
+
+app_conference is brought to you by the letter q, and the number e
diff --git a/apps/conference/README.videoswitch b/apps/conference/README.videoswitch
new file mode 100644
index 0000000..2612194
--- /dev/null
+++ b/apps/conference/README.videoswitch
@@ -0,0 +1,87 @@
+VideoSwitch
+-----------
+(c) Vipadia Limited 2005-2006
+Neil Stratford <neils@vipadia.com>
+
+Based on app_conference, see README.
+
+Including contributions from John Martin <John.Martin@AuPix.com>
+
+Example use:
+
+exten => 2300,1,Videoswitch(test/RX)
+
+This puts the user into a conference 'test' with the options SRX.
+
+The options are the same as app_conference, except:
+
+X - enable the caller to switch video stream using DTMF
+R - relay the DTMF to the management interface
+C - Mute video - no video from this client
+c - No Receive video - send no video to this client
+L - Mute audio - no audio from this client
+l - No Receive audio - send no audio to this client
+M - member is moderator - when they leave everyone else is kicked
+
+Stream selection options: two integers, first is receive id, second is send id.
+Both are optional.
+
+0-9 - Set initial receive stream to n
+0-9 - Set this stream id to n (will stop any other video with that id already)
+
+eg: Videoswitch(test/01) will set our id to 1, and we will receive id 0's video
+
+CLI commands (which may also be invoked from the manager interface
+using the Command action):
+
+Most commands have two versions, which can either take a member number (obtained from 'videoswitch list') or a channel identifier (such as SIP/2304-1e82).
+
+fidwell*CLI> help videoswitch
+ videoswitch debug enable debugging for a videoswitch
+ videoswitch kick kick member from a videoswitch
+ videoswitch list list members of a videoswitch
+ videoswitch mute mute member in a videoswitch
+ videoswitch mutechannel mute channel in a videoswitch
+ videoswitch show stats show videoswitch stats
+ videoswitch unmute unmute member in a videoswitch
+videoswitch unmutechannel unmute channel in a videoswitch
+ videoswitch viewchannel switch channel in a videoswitch
+ videoswitch viewstream switch view in a videoswitch
+
+fidwell*CLI> help videoswitch debug
+usage: videoswitch debug <videoswitch_name> [ on | off ]
+ enable debugging for a videoswitch
+
+fidwell*CLI> help videoswitch kick
+usage: videoswitch kick <videoswitch_name> <member no>
+ kick member form a videoswitch
+
+fidwell*CLI> help videoswitch list
+usage: videoswitch list {<videoswitch_name>}
+ list members of a videoswitch or list of videoswitches if no name
+
+fidwell*CLI> help videoswitch mute
+usage: videoswitch mute <videoswitch_name> <member no>
+ mute member in a videoswitch
+
+fidwell*CLI> help videoswitch unmute
+usage: videoswitch unmute <videoswitch_name> <member no>
+ unmute member in a videoswitch
+
+fidwell*CLI> help videoswitch mutechannel
+usage: videoswitch mute <videoswitch_name> <channel>
+ mute channel in a videoswitch
+
+fidwell*CLI> help videoswitch unmutechannel
+usage: videoswitch unmute <videoswitch_name> <channel>
+ unmute channel in a videoswitch
+
+fidwell*CLI> help videoswitch viewchannel
+usage: videoswitch viewchannel <videoswitch_name> <dest channel> <src channel>
+ channel <dest channel> will receive video stream <src channel>
+
+fidwell*CLI> help videoswitch viewstream
+usage: videoswitch viewstream <videoswitch_name> <member no> <stream no>
+ member <member no> will receive video stream <stream no>
+
+
diff --git a/apps/conference/TODO b/apps/conference/TODO
new file mode 100644
index 0000000..121e83e
--- /dev/null
+++ b/apps/conference/TODO
@@ -0,0 +1,4 @@
+Things we need to do for the next release
+- Enable speex based VAD for all members instead of just for telephone
+members (configurable)
+- Documentation!
diff --git a/apps/conference/app_conference.h b/apps/conference/app_conference.h
new file mode 100644
index 0000000..17aca58
--- /dev/null
+++ b/apps/conference/app_conference.h
@@ -0,0 +1,244 @@
+
+// $Id: app_conference.h 839 2007-01-17 22:32:03Z sbalea $
+
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
+ *
+ * Video Conferencing support added by
+ * Neil Stratford <neils@vipadia.com>
+ * Copyright (C) 2005, 2005 Vipadia Limited
+ *
+ * VAD driven video conferencing, text message support
+ * and miscellaneous enhancements added by
+ * Mihai Balea <mihai at hates dot ms>
+ *
+ * This program may be modified and distributed under the
+ * terms of the GNU General Public License. You should have received
+ * a copy of the GNU General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc.
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _ASTERISK_CONF_H
+#define _ASTERISK_CONF_H
+
+
+/* standard includes */
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <stdio.h>
+
+
+#include <pthread.h>
+
+/* asterisk includes */
+#include <asterisk/utils.h>
+#include <asterisk/pbx.h>
+#include <asterisk/module.h>
+#include <asterisk/logger.h>
+#include <asterisk/lock.h>
+#include <asterisk/frame.h>
+#include <asterisk/manager.h>
+#include <asterisk/dsp.h>
+#include <asterisk/translate.h>
+#include <asterisk/channel.h>
+#include <asterisk/file.h>
+//#include <asterisk/channel_pvt.h>
+#include <asterisk/cli.h>
+
+
+#if (SILDET == 2)
+#include <speex/speex_preprocess.h>
+#endif
+
+//
+// app_conference defines
+//
+
+// debug logging level
+
+// LOG_NOTICE for debugging, LOG_DEBUG for production
+#ifdef APP_CONFERENCE_DEBUG
+#define AST_CONF_DEBUG LOG_NOTICE
+#else
+#define AST_CONF_DEBUG LOG_DEBUG
+#endif
+
+//
+// feature defines
+//
+
+// number of times the last non-silent frame should be
+// repeated after silence starts
+#define AST_CONF_CACHE_LAST_FRAME 1
+
+//
+// debug defines
+//
+
+//#define DEBUG_USE_TIMELOG
+
+//#define DEBUG_FRAME_TIMESTAMPS
+
+// #define DEBUG_OUTPUT_PCM
+
+//
+// !!! THESE CONSTANTS SHOULD BE CLEANED UP AND CLARIFIED !!!
+//
+
+//
+// sample information for AST_FORMAT_SLINEAR format
+//
+
+#define AST_CONF_SAMPLE_RATE 8000
+#define AST_CONF_SAMPLE_SIZE 16
+#define AST_CONF_FRAME_INTERVAL 20
+//neils#define AST_CONF_FRAME_INTERVAL 30
+
+//
+// so, since we cycle approximately every 20ms,
+// we can compute the following values:
+//
+// 160 samples per 20 ms frame -or-
+// ( 8000 samples-per-second * ( 20 ms / 1000 ms-per-second ) ) = 160 samples
+//
+// 320 bytes ( 2560 bits ) of data 20 ms frame -or-
+// ( 160 samples * 16 bits-per-sample / 8 bits-per-byte ) = 320 bytes
+//
+
+// 160 samples 16-bit signed linear
+#define AST_CONF_BLOCK_SAMPLES 160
+
+// 2 bytes per sample ( i.e. 16-bit )
+#define AST_CONF_BYTES_PER_SAMPLE 2
+
+// 320 bytes for each 160 sample frame of 16-bit audio
+#define AST_CONF_FRAME_DATA_SIZE 320
+
+// 1000 ms-per-second / 20 ms-per-frame = 50 frames-per-second
+#define AST_CONF_FRAMES_PER_SECOND ( 1000 / AST_CONF_FRAME_INTERVAL )
+
+
+//
+// buffer and queue values
+//
+
+// account for friendly offset when allocating buffer for frame
+#define AST_CONF_BUFFER_SIZE ( AST_CONF_FRAME_DATA_SIZE + AST_FRIENDLY_OFFSET )
+
+// maximum number of frames queued per member
+#define AST_CONF_MAX_QUEUE 100
+
+// max video frames in the queue
+#define AST_CONF_MAX_VIDEO_QUEUE 800
+
+// max dtmf frames in the queue
+#define AST_CONF_MAX_DTMF_QUEUE 8
+
+// max text frames in the queue
+#define AST_CONF_MAX_TEXT_QUEUE 8
+
+// minimum number of frames queued per member
+#define AST_CONF_MIN_QUEUE 0
+
+// number of queued frames before we start dropping
+#define AST_CONF_QUEUE_DROP_THRESHOLD 40
+
+// number of milliseconds between frame drops
+#define AST_CONF_QUEUE_DROP_TIME_LIMIT 750
+
+//
+// timer and sleep values
+//
+
+// milliseconds we're willing to wait for a channel
+// event before we check for outgoing frames
+#define AST_CONF_WAITFOR_LATENCY 40
+
+// milliseconds to sleep before trying to process frames
+#define AST_CONF_CONFERENCE_SLEEP 40
+
+// milliseconds to wait between state notification updates
+#define AST_CONF_NOTIFICATION_SLEEP 200
+
+//
+// warning threshold values
+//
+
+// number of frames behind before warning
+#define AST_CONF_OUTGOING_FRAMES_WARN 70
+
+// number of milliseconds off AST_CONF_FRAME_INTERVAL before warning
+#define AST_CONF_INTERVAL_WARNING 1000
+
+//
+// silence detection values
+//
+
+// toggle silence detection
+#define ENABLE_SILENCE_DETECTION 1
+
+// silence threshold
+#define AST_CONF_SILENCE_THRESHOLD 128
+
+// speech tail (delay before dropping silent frames, in ms.
+// #define AST_CONF_SPEECH_TAIL 180
+
+// number of frames to ignore speex_preprocess() after speech detected
+#define AST_CONF_SKIP_SPEEX_PREPROCESS 20
+
+// our speex probability values
+#define AST_CONF_PROB_START 0.05
+#define AST_CONF_PROB_CONTINUE 0.02
+
+
+//
+// format translation values
+//
+#ifdef AC_USE_G729A
+ #define AC_SUPPORTED_FORMATS 6
+ enum { AC_SLINEAR_INDEX = 0, AC_ULAW_INDEX, AC_ALAW_INDEX, AC_GSM_INDEX, AC_SPEEX_INDEX, AC_G729A_INDEX } ;
+#else
+ #define AC_SUPPORTED_FORMATS 5
+ enum { AC_SLINEAR_INDEX = 0, AC_ULAW_INDEX, AC_ALAW_INDEX, AC_GSM_INDEX, AC_SPEEX_INDEX } ;
+#endif
+
+//
+// VAD based video switching parameters
+// All time related values are in ms
+//
+
+// Amount of silence required before we decide somebody stopped talking
+#define AST_CONF_VIDEO_STOP_TIMEOUT 2000
+
+// Amount of audio required before we decide somebody started talking
+#define AST_CONF_VIDEO_START_TIMEOUT 2000
+
+//
+// Text frame control protocol
+//
+#define AST_CONF_CONTROL_CAMERA_DISABLED "CONTROL:CAMERA_DISABLED"
+#define AST_CONF_CONTROL_CAMERA_ENABLED "CONTROL:CAMERA_ENABLED"
+#define AST_CONF_CONTROL_START_VIDEO "CONTROL:STARTVIDEO"
+#define AST_CONF_CONTROL_STOP_VIDEO "CONTROL:STOPVIDEO"
+#define AST_CONF_CONTROL_STOP_VIDEO_TRANSMIT "CONTROL:STOP_VIDEO_TRANSMIT"
+#define AST_CONF_CONTROL_START_VIDEO_TRANSMIT "CONTROL:START_VIDEO_TRANSMIT"
+
+// utility functions
+void add_milliseconds( struct timeval* tv, long ms ) ;
+
+#endif
+
+
diff --git a/apps/conference/cli.c b/apps/conference/cli.c
new file mode 100644
index 0000000..3d88ddf
--- /dev/null
+++ b/apps/conference/cli.c
@@ -0,0 +1,1265 @@
+
+// $Id: cli.c 884 2007-06-27 14:56:21Z sbalea $
+
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
+ *
+ * Video Conferencing support added by
+ * Neil Stratford <neils@vipadia.com>
+ * Copyright (C) 2005, 2005 Vipadia Limited
+ *
+ * VAD driven video conferencing, text message support
+ * and miscellaneous enhancements added by
+ * Mihai Balea <mihai at hates dot ms>
+ *
+ * This program may be modified and distributed under the
+ * terms of the GNU General Public License. You should have received
+ * a copy of the GNU General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc.
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "asterisk/autoconfig.h"
+#include "cli.h"
+
+static char conference_restart_usage[] =
+ "usage: conference restart\n"
+ " kick all users in all conferences\n"
+;
+
+static struct ast_cli_entry cli_restart = {
+ { "conference", "restart", NULL },
+ conference_restart,
+ "restart a conference",
+ conference_restart_usage
+} ;
+
+
+int conference_restart( int fd, int argc, char *argv[] )
+{
+ if ( argc < 2 )
+ return RESULT_SHOWUSAGE ;
+
+ kick_all();
+ return RESULT_SUCCESS ;
+}
+
+
+//
+// debug functions
+//
+
+static char conference_debug_usage[] =
+ "usage: conference debug <conference_name> [ on | off ]\n"
+ " enable debugging for a conference\n"
+;
+
+static struct ast_cli_entry cli_debug = {
+ { "conference", "debug", NULL },
+ conference_debug,
+ "enable debugging for a conference",
+ conference_debug_usage
+} ;
+
+
+int conference_debug( int fd, int argc, char *argv[] )
+{
+ if ( argc < 3 )
+ return RESULT_SHOWUSAGE ;
+
+ // get the conference name
+ const char* name = argv[2] ;
+
+ // get the new state
+ int state = 0 ;
+
+ if ( argc == 3 )
+ {
+ // no state specified, so toggle it
+ state = -1 ;
+ }
+ else
+ {
+ if ( strncasecmp( argv[3], "on", 4 ) == 0 )
+ state = 1 ;
+ else if ( strncasecmp( argv[3], "off", 3 ) == 0 )
+ state = 0 ;
+ else
+ return RESULT_SHOWUSAGE ;
+ }
+
+ int new_state = set_conference_debugging( name, state ) ;
+
+ if ( new_state == 1 )
+ {
+ ast_cli( fd, "enabled conference debugging, name => %s, new_state => %d\n",
+ name, new_state ) ;
+ }
+ else if ( new_state == 0 )
+ {
+ ast_cli( fd, "disabled conference debugging, name => %s, new_state => %d\n",
+ name, new_state ) ;
+ }
+ else
+ {
+ // error setting state
+ ast_cli( fd, "\nunable to set debugging state, name => %s\n\n", name ) ;
+ }
+
+ return RESULT_SUCCESS ;
+}
+
+//
+// stats functions
+//
+
+static char conference_show_stats_usage[] =
+ "usage: conference show stats\n"
+ " display stats for active conferences.\n"
+;
+
+static struct ast_cli_entry cli_show_stats = {
+ { "conference", "show", "stats", NULL },
+ conference_show_stats,
+ "show conference stats",
+ conference_show_stats_usage
+} ;
+
+int conference_show_stats( int fd, int argc, char *argv[] )
+{
+ if ( argc < 3 )
+ return RESULT_SHOWUSAGE ;
+
+ // get count of active conferences
+ int count = get_conference_count() ;
+
+ ast_cli( fd, "\n\nCONFERENCE STATS, ACTIVE( %d )\n\n", count ) ;
+
+ // if zero, go no further
+ if ( count <= 0 )
+ return RESULT_SUCCESS ;
+
+ //
+ // get the conference stats
+ //
+
+ // array of stats structs
+ ast_conference_stats stats[ count ] ;
+
+ // get stats structs
+ count = get_conference_stats( stats, count ) ;
+
+ // make sure we were able to fetch some
+ if ( count <= 0 )
+ {
+ ast_cli( fd, "!!! error fetching conference stats, available => %d !!!\n", count ) ;
+ return RESULT_SUCCESS ;
+ }
+
+ //
+ // output the conference stats
+ //
+
+ // output header
+ ast_cli( fd, "%-20.20s %-40.40s\n", "Name", "Stats") ;
+ ast_cli( fd, "%-20.20s %-40.40s\n", "----", "-----") ;
+
+ ast_conference_stats* s = NULL ;
+
+ int i;
+
+ for ( i = 0 ; i < count ; ++i )
+ {
+ s = &(stats[i]) ;
+
+ // output this conferences stats
+ ast_cli( fd, "%-20.20s\n", (char*)( &(s->name) )) ;
+ }
+
+ ast_cli( fd, "\n" ) ;
+
+ //
+ // drill down to specific stats
+ //
+
+ if ( argc == 4 )
+ {
+ // show stats for a particular conference
+ conference_show_stats_name( fd, argv[3] ) ;
+ }
+
+ return RESULT_SUCCESS ;
+}
+
+int conference_show_stats_name( int fd, const char* name )
+{
+ // not implemented yet
+ return RESULT_SUCCESS ;
+}
+
+static char conference_list_usage[] =
+ "usage: conference list {<conference_name>}\n"
+ " list members of a conference\n"
+;
+
+static struct ast_cli_entry cli_list = {
+ { "conference", "list", NULL },
+ conference_list,
+ "list members of a conference",
+ conference_list_usage
+} ;
+
+
+
+int conference_list( int fd, int argc, char *argv[] )
+{
+ int index;
+
+ if ( argc < 2 )
+ return RESULT_SHOWUSAGE ;
+
+ if (argc >= 3)
+ {
+ for (index = 2; index < argc; index++)
+ {
+ // get the conference name
+ const char* name = argv[index] ;
+ show_conference_list( fd, name );
+ }
+ }
+ else
+ {
+ show_conference_stats(fd);
+ }
+ return RESULT_SUCCESS ;
+}
+
+
+int conference_kick( int fd, int argc, char *argv[] )
+{
+ if ( argc < 4 )
+ return RESULT_SHOWUSAGE ;
+
+ // get the conference name
+ const char* name = argv[2] ;
+
+ int member_id;
+ sscanf(argv[3], "%d", &member_id);
+
+ int res = kick_member( name, member_id );
+
+ if (res) ast_cli( fd, "User #: %d kicked\n", member_id) ;
+
+ return RESULT_SUCCESS ;
+}
+
+static char conference_kick_usage[] =
+ "usage: conference kick <conference> <member id>\n"
+ " kick member <member id> from conference <conference>\n"
+;
+
+static struct ast_cli_entry cli_kick = {
+ { "conference", "kick", NULL },
+ conference_kick,
+ "kick member from a conference",
+ conference_kick_usage
+} ;
+
+int conference_kickchannel( int fd, int argc, char *argv[] )
+{
+ if ( argc < 4 )
+ return RESULT_SHOWUSAGE ;
+
+ const char *name = argv[2] ;
+ const char *channel = argv[3];
+
+ int res = kick_channel( name, channel );
+
+ if ( !res )
+ {
+ ast_cli( fd, "Cannot kick channel %s in conference %s\n", channel, name);
+ return RESULT_FAILURE;
+ }
+
+ return RESULT_SUCCESS ;
+}
+
+static char conference_kickchannel_usage[] =
+ "usage: conference kickchannel <conference_name> <channel>\n"
+ " kick channel from conference\n"
+;
+
+static struct ast_cli_entry cli_kickchannel = {
+ { "conference", "kickchannel", NULL },
+ conference_kickchannel,
+ "kick channel from conference",
+ conference_kickchannel_usage
+} ;
+
+int conference_exit( int fd, int argc, char *argv[] )
+{
+ if ( argc < 3 )
+ return RESULT_SHOWUSAGE ;
+
+ const char *channel = argv[2];
+
+ struct ast_conf_member *member = find_member(channel, 1);
+ if(!member)
+ {
+ ast_cli(fd, "Member %s not found\n", channel);
+ return RESULT_FAILURE;
+ }
+ const char * name = member->conf_name;
+ int res = kick_channel( name, channel );
+
+ if ( !res )
+ {
+ ast_cli(fd, "Cannot exit channel %s from conference %s\n", channel, name);
+ ast_mutex_unlock(&member->lock);
+ return RESULT_FAILURE;
+ }
+
+ ast_mutex_unlock( &member->lock ) ;
+ return RESULT_SUCCESS ;
+}
+
+static char conference_exit_usage[] =
+ "usage: conference exit <channel>\n"
+ " exit channel from any conference where it in\n";
+
+static struct ast_cli_entry cli_exit = {
+ { "conference", "exit", NULL },
+ conference_exit,
+ "exit channel from any conference where it in",
+ conference_exit_usage
+};
+
+int conference_mute( int fd, int argc, char *argv[] )
+{
+ if ( argc < 4 )
+ return RESULT_SHOWUSAGE ;
+
+ // get the conference name
+ const char* name = argv[2] ;
+
+ int member_id;
+ sscanf(argv[3], "%d", &member_id);
+
+ int res = mute_member( name, member_id );
+
+ if (res) ast_cli( fd, "User #: %d muted\n", member_id) ;
+
+ return RESULT_SUCCESS ;
+}
+
+static char conference_mute_usage[] =
+ "usage: conference mute <conference_name> <member id>\n"
+ " mute member in a conference\n"
+;
+
+static struct ast_cli_entry cli_mute = {
+ { "conference", "mute", NULL },
+ conference_mute,
+ "mute member in a conference",
+ conference_mute_usage
+} ;
+
+int conference_mutechannel( int fd, int argc, char *argv[] )
+{
+ struct ast_conf_member *member;
+ char *channel;
+
+ if ( argc < 3 )
+ return RESULT_SHOWUSAGE ;
+
+ channel = argv[2];
+
+ member = find_member(channel, 1);
+ if(!member) {
+ ast_cli(fd, "Member %s not found\n", channel);
+ return RESULT_FAILURE;
+ }
+
+ member->mute_audio = 1;
+ ast_mutex_unlock( &member->lock ) ;
+
+ ast_cli( fd, "Channel #: %s muted\n", argv[2]) ;
+
+ return RESULT_SUCCESS ;
+}
+
+static char conference_mutechannel_usage[] =
+ "usage: conference mutechannel <channel>\n"
+ " mute channel in a conference\n"
+;
+
+static struct ast_cli_entry cli_mutechannel = {
+ { "conference", "mutechannel", NULL },
+ conference_mutechannel,
+ "mute channel in a conference",
+ conference_mutechannel_usage
+} ;
+
+int conference_viewstream( int fd, int argc, char *argv[] )
+{
+ int res;
+
+ if ( argc < 5 )
+ return RESULT_SHOWUSAGE ;
+
+ // get the conference name
+ const char* switch_name = argv[2] ;
+
+ int member_id, viewstream_id;
+ sscanf(argv[3], "%d", &member_id);
+ sscanf(argv[4], "%d", &viewstream_id);
+
+ res = viewstream_switch( switch_name, member_id, viewstream_id );
+
+ if (res) ast_cli( fd, "User #: %d viewing %d\n", member_id, viewstream_id) ;
+
+ return RESULT_SUCCESS ;
+}
+
+static char conference_viewstream_usage[] =
+ "usage: conference viewstream <conference_name> <member id> <stream no>\n"
+ " member <member id> will receive video stream <stream no>\n"
+;
+
+static struct ast_cli_entry cli_viewstream = {
+ { "conference", "viewstream", NULL },
+ conference_viewstream,
+ "switch view in a conference",
+ conference_viewstream_usage
+} ;
+
+int conference_viewchannel( int fd, int argc, char *argv[] )
+{
+ int res;
+
+ if ( argc < 5 )
+ return RESULT_SHOWUSAGE ;
+
+ // get the conference name
+ const char* switch_name = argv[2] ;
+
+ res = viewchannel_switch( switch_name, argv[3], argv[4] );
+
+ if (res) ast_cli( fd, "Channel #: %s viewing %s\n", argv[3], argv[4]) ;
+
+ return RESULT_SUCCESS ;
+}
+
+static char conference_viewchannel_usage[] =
+ "usage: conference viewchannel <conference_name> <dest channel> <src channel>\n"
+ " channel <dest channel> will receive video stream <src channel>\n"
+;
+
+static struct ast_cli_entry cli_viewchannel = {
+ { "conference", "viewchannel", NULL },
+ conference_viewchannel,
+ "switch channel in a conference",
+ conference_viewchannel_usage
+} ;
+
+int conference_unmute( int fd, int argc, char *argv[] )
+{
+ if ( argc < 3 )
+ return RESULT_SHOWUSAGE ;
+
+ // get the conference name
+ const char* name = argv[2] ;
+
+ int member_id;
+ sscanf(argv[3], "%d", &member_id);
+
+ int res = unmute_member( name, member_id );
+
+ if (res) ast_cli( fd, "User #: %d unmuted\n", member_id) ;
+
+ return RESULT_SUCCESS ;
+}
+
+static char conference_unmute_usage[] =
+ "usage: conference unmute <conference_name> <member id>\n"
+ " unmute member in a conference\n"
+;
+
+static struct ast_cli_entry cli_unmute = {
+ { "conference", "unmute", NULL },
+ conference_unmute,
+ "unmute member in a conference",
+ conference_unmute_usage
+} ;
+
+int conference_unmutechannel( int fd, int argc, char *argv[] )
+{
+ struct ast_conf_member *member;
+ char *channel;
+
+ if ( argc < 3 )
+ return RESULT_SHOWUSAGE ;
+
+ channel = argv[2];
+
+ member = find_member(channel, 1);
+ if(!member) {
+ ast_cli(fd, "Member %s not found\n", channel);
+ return RESULT_FAILURE;
+ }
+
+ member->mute_audio = 0;
+ ast_mutex_unlock( &member->lock ) ;
+
+ ast_cli( fd, "Channel #: %s unmuted\n", argv[2]) ;
+
+ return RESULT_SUCCESS ;
+}
+
+static char conference_unmutechannel_usage[] =
+ "usage: conference unmutechannel <channel>\n"
+ " unmute channel in a conference\n"
+;
+
+static struct ast_cli_entry cli_unmutechannel = {
+ { "conference", "unmutechannel", NULL },
+ conference_unmutechannel,
+ "unmute channel in a conference",
+ conference_unmutechannel_usage
+} ;
+
+//
+// play sound
+//
+static char conference_play_sound_usage[] =
+ "usage: conference play sound <channel-id> <sound-file> [mute]\n"
+ " play sound <sound-file> to conference member <channel-id>.\n"
+ " If mute is specified, all other audio is muted while the sound is played back.\n"
+;
+
+static struct ast_cli_entry cli_play_sound = {
+ { "conference", "play", "sound", NULL },
+ conference_play_sound,
+ "play a sound to a conference member",
+ conference_play_sound_usage
+} ;
+
+int conference_play_sound( int fd, int argc, char *argv[] )
+{
+ char *channel, *file;
+ int mute = 0;
+
+ if ( argc < 5 )
+ return RESULT_SHOWUSAGE ;
+
+ channel = argv[3];
+ file = argv[4];
+
+ if(argc > 5 && !strcmp(argv[5], "mute"))
+ mute = 1;
+
+ int res = play_sound_channel(fd, channel, file, mute);
+
+ if ( !res )
+ {
+ ast_cli(fd, "Sound playback failed failed\n");
+ return RESULT_FAILURE;
+ }
+ return RESULT_SUCCESS ;
+}
+
+//
+// stop sounds
+//
+
+static char conference_stop_sounds_usage[] =
+ "usage: conference stop sounds <channel-id>\n"
+ " stop sounds for conference member <channel-id>.\n"
+;
+
+static struct ast_cli_entry cli_stop_sounds = {
+ { "conference", "stop", "sounds", NULL },
+ conference_stop_sounds,
+ "stop sounds for a conference member",
+ conference_stop_sounds_usage
+} ;
+
+int conference_stop_sounds( int fd, int argc, char *argv[] )
+{
+ char *channel;
+
+ if ( argc < 4 )
+ return RESULT_SHOWUSAGE ;
+
+ channel = argv[3];
+
+ int res = stop_sound_channel(fd, channel);
+
+ if ( !res )
+ {
+ ast_cli(fd, "Sound stop failed failed\n");
+ return RESULT_FAILURE;
+ }
+ return RESULT_SUCCESS ;
+}
+
+//
+// end conference
+//
+
+static char conference_end_usage[] =
+ "usage: conference end <conference name>\n"
+ " ends a conference.\n"
+;
+
+static struct ast_cli_entry cli_end = {
+ { "conference", "end", NULL },
+ conference_end,
+ "stops a conference",
+ conference_end_usage
+} ;
+
+int conference_end( int fd, int argc, char *argv[] )
+{
+ // check the args length
+ if ( argc < 3 )
+ return RESULT_SHOWUSAGE ;
+
+ // conference name
+ const char* name = argv[2] ;
+
+ // get the conference
+ if ( end_conference( name, 1 ) != 0 )
+ {
+ ast_cli( fd, "unable to end the conference, name => %s\n", name ) ;
+ return RESULT_SHOWUSAGE ;
+ }
+
+ return RESULT_SUCCESS ;
+}
+
+//
+// E.BUU - Manager conference end. Additional option to just kick everybody out
+// without hangin up channels
+//
+int manager_conference_end(struct mansession *s, const struct message *m)
+{
+ const char *confname = astman_get_header(m,"Conference");
+ int hangup = 1;
+
+ const char * h = astman_get_header(m, "Hangup");
+ if (h)
+ {
+ hangup = atoi(h);
+ }
+
+ ast_log( LOG_NOTICE, "Terminating conference %s on manager's request. Hangup: %s.\n", confname, hangup?"YES":"NO" );
+ if ( end_conference( confname, hangup ) != 0 )
+ {
+ ast_log( LOG_ERROR, "manager end conf: unable to terminate conference %s.\n", confname );
+ astman_send_error(s, m, "Failed to terminate\r\n");
+ return RESULT_FAILURE;
+ }
+
+ astman_send_ack(s, m, "Conference terminated");
+ return RESULT_SUCCESS;
+}
+//
+// lock conference to a video source
+//
+static char conference_lock_usage[] =
+ "usage: conference lock <conference name> <member id>\n"
+ " locks incoming video stream for conference <conference name> to member <member id>\n"
+;
+
+static struct ast_cli_entry cli_lock = {
+ { "conference", "lock", NULL },
+ conference_lock,
+ "locks incoming video to a member",
+ conference_lock_usage
+} ;
+
+int conference_lock( int fd, int argc, char *argv[] )
+{
+ // check args
+ if ( argc < 4 )
+ return RESULT_SHOWUSAGE;
+
+ const char *conference = argv[2];
+ int member;
+ sscanf(argv[3], "%d", &member);
+
+ int res = lock_conference(conference, member);
+
+ if ( !res )
+ {
+ ast_cli(fd, "Locking failed\n");
+ return RESULT_FAILURE;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+//
+// lock conference to a video source channel
+//
+static char conference_lockchannel_usage[] =
+ "usage: conference lockchannel <conference name> <channel>\n"
+ " locks incoming video stream for conference <conference name> to channel <channel>\n"
+;
+
+static struct ast_cli_entry cli_lockchannel = {
+ { "conference", "lockchannel", NULL },
+ conference_lockchannel,
+ "locks incoming video to a channel",
+ conference_lockchannel_usage
+} ;
+
+int conference_lockchannel( int fd, int argc, char *argv[] )
+{
+ // check args
+ if ( argc < 4 )
+ return RESULT_SHOWUSAGE;
+
+ const char *conference = argv[2];
+ const char *channel = argv[3];
+
+ int res = lock_conference_channel(conference, channel);
+
+ if ( !res )
+ {
+ ast_cli(fd, "Locking failed\n");
+ return RESULT_FAILURE;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+//
+// unlock conference
+//
+static char conference_unlock_usage[] =
+ "usage: conference unlock <conference name>\n"
+ " unlocks conference <conference name>\n"
+;
+
+static struct ast_cli_entry cli_unlock = {
+ { "conference", "unlock", NULL },
+ conference_unlock,
+ "unlocks conference",
+ conference_unlock_usage
+} ;
+
+int conference_unlock( int fd, int argc, char *argv[] )
+{
+ // check args
+ if ( argc < 3 )
+ return RESULT_SHOWUSAGE;
+
+
+ const char *conference = argv[2];
+
+ int res = unlock_conference(conference);
+
+ if ( !res )
+ {
+ ast_cli(fd, "Unlocking failed\n");
+ return RESULT_FAILURE;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+//
+// Set conference default video source
+//
+static char conference_set_default_usage[] =
+ "usage: conference set default <conference name> <member id>\n"
+ " sets the default video source for conference <conference name> to member <member id>\n"
+ " Use a negative value for member if you want to clear the default\n"
+;
+
+static struct ast_cli_entry cli_set_default = {
+ { "conference", "set", "default", NULL },
+ conference_set_default,
+ "sets default video source",
+ conference_set_default_usage
+} ;
+
+int conference_set_default(int fd, int argc, char *argv[] )
+{
+ // check args
+ if ( argc < 5 )
+ return RESULT_SHOWUSAGE;
+
+ const char *conference = argv[3];
+ int member;
+ sscanf(argv[4], "%d", &member);
+
+ int res = set_default_id(conference, member);
+
+ if ( !res )
+ {
+ ast_cli(fd, "Setting default video id failed\n");
+ return RESULT_FAILURE;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+//
+// Set conference default video source channel
+//
+static char conference_set_defaultchannel_usage[] =
+ "usage: conference set defaultchannel <conference name> <channel>\n"
+ " sets the default video source channel for conference <conference name> to channel <channel>\n"
+;
+
+static struct ast_cli_entry cli_set_defaultchannel = {
+ { "conference", "set", "defaultchannel", NULL },
+ conference_set_defaultchannel,
+ "sets default video source channel",
+ conference_set_defaultchannel_usage
+} ;
+
+int conference_set_defaultchannel(int fd, int argc, char *argv[] )
+{
+ // check args
+ if ( argc < 5 )
+ return RESULT_SHOWUSAGE;
+
+ const char *conference = argv[3];
+ const char *channel = argv[4];
+
+ int res = set_default_channel(conference, channel);
+
+ if ( !res )
+ {
+ ast_cli(fd, "Setting default video id failed\n");
+ return RESULT_FAILURE;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+//
+// Mute video from a member
+//
+static char conference_video_mute_usage[] =
+ "usage: conference video mute <conference name> <member id>\n"
+ " mutes video from member <member id> in conference <conference name>\n"
+;
+
+static struct ast_cli_entry cli_video_mute = {
+ { "conference", "video", "mute", NULL },
+ conference_video_mute,
+ "mutes video from a member",
+ conference_video_mute_usage
+} ;
+
+int conference_video_mute(int fd, int argc, char *argv[] )
+{
+ // check args
+ if ( argc < 5 )
+ return RESULT_SHOWUSAGE;
+
+ const char *conference = argv[3];
+ int member;
+ sscanf(argv[4], "%d", &member);
+
+ int res = video_mute_member(conference, member);
+
+ if ( !res )
+ {
+ ast_cli(fd, "Muting video from member %d failed\n", member);
+ return RESULT_FAILURE;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+//
+// Unmute video from a member
+//
+static char conference_video_unmute_usage[] =
+ "usage: conference video unmute <conference name> <member id>\n"
+ " unmutes video from member <member id> in conference <conference name>\n"
+;
+
+static struct ast_cli_entry cli_video_unmute = {
+ { "conference", "video", "unmute", NULL },
+ conference_video_unmute,
+ "unmutes video from a member",
+ conference_video_unmute_usage
+} ;
+
+int conference_video_unmute(int fd, int argc, char *argv[] )
+{
+ // check args
+ if ( argc < 5 )
+ return RESULT_SHOWUSAGE;
+
+ const char *conference = argv[3];
+ int member;
+ sscanf(argv[4], "%d", &member);
+
+ int res = video_unmute_member(conference, member);
+
+ if ( !res )
+ {
+ ast_cli(fd, "Unmuting video from member %d failed\n", member);
+ return RESULT_FAILURE;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+//
+// Mute video from a channel
+//
+static char conference_video_mutechannel_usage[] =
+ "usage: conference video mutechannel <conference name> <channel>\n"
+ " mutes video from channel <channel> in conference <conference name>\n"
+;
+
+static struct ast_cli_entry cli_video_mutechannel = {
+ { "conference", "video", "mutechannel", NULL },
+ conference_video_mutechannel,
+ "mutes video from a channel",
+ conference_video_mutechannel_usage
+} ;
+
+int conference_video_mutechannel(int fd, int argc, char *argv[] )
+{
+ // check args
+ if ( argc < 5 )
+ return RESULT_SHOWUSAGE;
+
+ const char *conference = argv[3];
+ const char *channel = argv[4];
+
+ int res = video_mute_channel(conference, channel);
+
+ if ( !res )
+ {
+ ast_cli(fd, "Muting video from channel %s failed\n", channel);
+ return RESULT_FAILURE;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+//
+// Unmute video from a channel
+//
+static char conference_video_unmutechannel_usage[] =
+ "usage: conference video unmutechannel <conference name> <channel>\n"
+ " unmutes video from channel <channel> in conference <conference name>\n"
+;
+
+static struct ast_cli_entry cli_video_unmutechannel = {
+ { "conference", "video", "unmutechannel", NULL },
+ conference_video_unmutechannel,
+ "unmutes video from a channel",
+ conference_video_unmutechannel_usage
+} ;
+
+int conference_video_unmutechannel(int fd, int argc, char *argv[] )
+{
+ // check args
+ if ( argc < 5 )
+ return RESULT_SHOWUSAGE;
+
+ const char *conference = argv[3];
+ const char *channel = argv[4];
+
+ int res = video_unmute_channel(conference, channel);
+
+ if ( !res )
+ {
+ ast_cli(fd, "Unmuting video from channel %s failed\n", channel);
+ return RESULT_FAILURE;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+
+//
+// Text message functions
+// Send a text message to a member
+//
+static char conference_text_usage[] =
+ "usage: conference text <conference name> <member id> <text>\n"
+ " Sends text message <text> to member <member id> in conference <conference name>\n"
+;
+
+static struct ast_cli_entry cli_text = {
+ { "conference", "text", NULL },
+ conference_text,
+ "sends a text message to a member",
+ conference_text_usage
+} ;
+
+int conference_text(int fd, int argc, char *argv[] )
+{
+ // check args
+ if ( argc < 5 )
+ return RESULT_SHOWUSAGE;
+
+ const char *conference = argv[2];
+ int member;
+ sscanf(argv[3], "%d", &member);
+ const char *text = argv[4];
+
+ int res = send_text(conference, member, text);
+
+ if ( !res )
+ {
+ ast_cli(fd, "Sending a text message to member %d failed\n", member);
+ return RESULT_FAILURE;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+//
+// Send a text message to a channel
+//
+static char conference_textchannel_usage[] =
+ "usage: conference textchannel <conference name> <channel> <text>\n"
+ " Sends text message <text> to channel <channel> in conference <conference name>\n"
+;
+
+static struct ast_cli_entry cli_textchannel = {
+ { "conference", "textchannel", NULL },
+ conference_textchannel,
+ "sends a text message to a channel",
+ conference_textchannel_usage
+} ;
+
+int conference_textchannel(int fd, int argc, char *argv[] )
+{
+ // check args
+ if ( argc < 5 )
+ return RESULT_SHOWUSAGE;
+
+ const char *conference = argv[2];
+ const char *channel = argv[3];
+ const char *text = argv[4];
+
+ int res = send_text_channel(conference, channel, text);
+
+ if ( !res )
+ {
+ ast_cli(fd, "Sending a text message to channel %s failed\n", channel);
+ return RESULT_FAILURE;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+//
+// Send a text message to all members in a conference
+//
+static char conference_textbroadcast_usage[] =
+ "usage: conference textbroadcast <conference name> <text>\n"
+ " Sends text message <text> to all members in conference <conference name>\n"
+;
+
+static struct ast_cli_entry cli_textbroadcast = {
+ { "conference", "textbroadcast", NULL },
+ conference_textbroadcast,
+ "sends a text message to all members in a conference",
+ conference_textbroadcast_usage
+} ;
+
+int conference_textbroadcast(int fd, int argc, char *argv[] )
+{
+ // check args
+ if ( argc < 4 )
+ return RESULT_SHOWUSAGE;
+
+ const char *conference = argv[2];
+ const char *text = argv[3];
+
+ int res = send_text_broadcast(conference, text);
+
+ if ( !res )
+ {
+ ast_cli(fd, "Sending a text broadcast to conference %s failed\n", conference);
+ return RESULT_FAILURE;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+//
+// Associate two members
+// Audio from the source member will drive VAD based video switching for the destination member
+// If the destination member is missing or negative, break any existing association
+//
+static char conference_drive_usage[] =
+ "usage: conference drive <conference name> <source member> [destination member]\n"
+ " Drives VAD video switching of <destination member> using audio from <source member> in conference <conference name>\n"
+ " If destination is missing or negative, break existing association\n"
+;
+
+static struct ast_cli_entry cli_drive = {
+ { "conference", "drive", NULL },
+ conference_drive,
+ "pairs two members to drive VAD-based video switching",
+ conference_drive_usage
+} ;
+
+int conference_drive(int fd, int argc, char *argv[] )
+{
+ // check args
+ if ( argc < 4 )
+ return RESULT_SHOWUSAGE;
+
+ const char *conference = argv[2];
+ int src_member = -1;
+ int dst_member = -1;
+ sscanf(argv[3], "%d", &src_member);
+ if ( argc > 4 )
+ sscanf(argv[4], "%d", &dst_member);
+
+ int res = drive(conference, src_member, dst_member);
+
+ if ( !res )
+ {
+ ast_cli(fd, "Pairing members %d and %d failed\n", src_member, dst_member);
+ return RESULT_FAILURE;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+//
+// Associate two channels
+// Audio from the source channel will drive VAD based video switching for the destination channel
+// If the destination channel is missing, break any existing association
+//
+static char conference_drivechannel_usage[] =
+ "usage: conference drive <conference name> <source channel> [destination channel]\n"
+ " Drives VAD video switching of <destination member> using audio from <source channel> in conference <conference channel>\n"
+ " If destination is missing, break existing association\n"
+;
+
+static struct ast_cli_entry cli_drivechannel = {
+ { "conference", "drivechannel", NULL },
+ conference_drivechannel,
+ "pairs two channels to drive VAD-based video switching",
+ conference_drivechannel_usage
+} ;
+
+int conference_drivechannel(int fd, int argc, char *argv[] )
+{
+ // check args
+ if ( argc < 4 )
+ return RESULT_SHOWUSAGE;
+
+ const char *conference = argv[2];
+ const char *src_channel = argv[3];
+ const char *dst_channel = NULL;
+ if ( argc > 4 )
+ dst_channel = argv[4];
+
+ int res = drive_channel(conference, src_channel, dst_channel);
+
+ if ( !res )
+ {
+ ast_cli(fd, "Pairing channels %s and %s failed\n", src_channel, dst_channel);
+ return RESULT_FAILURE;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+
+//
+// cli initialization function
+//
+
+void register_conference_cli( void )
+{
+ ast_cli_register( &cli_restart );
+ ast_cli_register( &cli_debug ) ;
+ ast_cli_register( &cli_show_stats ) ;
+ ast_cli_register( &cli_list );
+ ast_cli_register( &cli_kick );
+ ast_cli_register( &cli_kickchannel );
+ ast_cli_register( &cli_exit );
+ ast_cli_register( &cli_mute );
+ ast_cli_register( &cli_mutechannel );
+ ast_cli_register( &cli_viewstream );
+ ast_cli_register( &cli_viewchannel );
+ ast_cli_register( &cli_unmute );
+ ast_cli_register( &cli_unmutechannel );
+ ast_cli_register( &cli_play_sound ) ;
+ ast_cli_register( &cli_stop_sounds ) ;
+ ast_cli_register( &cli_end );
+ ast_cli_register( &cli_lock );
+ ast_cli_register( &cli_lockchannel );
+ ast_cli_register( &cli_unlock );
+ ast_cli_register( &cli_set_default );
+ ast_cli_register( &cli_set_defaultchannel );
+ ast_cli_register( &cli_video_mute ) ;
+ ast_cli_register( &cli_video_unmute ) ;
+ ast_cli_register( &cli_video_mutechannel ) ;
+ ast_cli_register( &cli_video_unmutechannel ) ;
+ ast_cli_register( &cli_text );
+ ast_cli_register( &cli_textchannel );
+ ast_cli_register( &cli_textbroadcast );
+ ast_cli_register( &cli_drive );
+ ast_cli_register( &cli_drivechannel );
+ ast_manager_register( "ConferenceList", 0, manager_conference_list, "Conference List" );
+ ast_manager_register( "ConferenceEnd", EVENT_FLAG_CALL, manager_conference_end, "Terminate a conference" );
+
+}
+
+void unregister_conference_cli( void )
+{
+ ast_cli_unregister( &cli_restart );
+ ast_cli_unregister( &cli_debug ) ;
+ ast_cli_unregister( &cli_show_stats ) ;
+ ast_cli_unregister( &cli_list );
+ ast_cli_unregister( &cli_kick );
+ ast_cli_unregister( &cli_kickchannel );
+ ast_cli_unregister( &cli_exit );
+ ast_cli_unregister( &cli_mute );
+ ast_cli_unregister( &cli_mutechannel );
+ ast_cli_unregister( &cli_viewstream );
+ ast_cli_unregister( &cli_viewchannel );
+ ast_cli_unregister( &cli_unmute );
+ ast_cli_unregister( &cli_unmutechannel );
+ ast_cli_unregister( &cli_play_sound ) ;
+ ast_cli_unregister( &cli_stop_sounds ) ;
+ ast_cli_unregister( &cli_end );
+ ast_cli_unregister( &cli_lock );
+ ast_cli_unregister( &cli_lockchannel );
+ ast_cli_unregister( &cli_unlock );
+ ast_cli_unregister( &cli_set_default );
+ ast_cli_unregister( &cli_set_defaultchannel );
+ ast_cli_unregister( &cli_video_mute ) ;
+ ast_cli_unregister( &cli_video_unmute ) ;
+ ast_cli_unregister( &cli_video_mutechannel ) ;
+ ast_cli_unregister( &cli_video_unmutechannel ) ;
+ ast_cli_unregister( &cli_text );
+ ast_cli_unregister( &cli_textchannel );
+ ast_cli_unregister( &cli_textbroadcast );
+ ast_cli_unregister( &cli_drive );
+ ast_cli_unregister( &cli_drivechannel );
+ ast_manager_unregister( "ConferenceList" );
+ ast_manager_unregister( "ConferenceEnd" );
+}
diff --git a/apps/conference/cli.h b/apps/conference/cli.h
new file mode 100644
index 0000000..34b79d6
--- /dev/null
+++ b/apps/conference/cli.h
@@ -0,0 +1,99 @@
+
+// $Id: cli.h 880 2007-04-25 15:23:59Z jpgrayson $
+
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
+ *
+ * Video Conferencing support added by
+ * Neil Stratford <neils@vipadia.com>
+ * Copyright (C) 2005, 2005 Vipadia Limited
+ *
+ * VAD driven video conferencing, text message support
+ * and miscellaneous enhancements added by
+ * Mihai Balea <mihai at hates dot ms>
+ *
+ * This program may be modified and distributed under the
+ * terms of the GNU General Public License. You should have received
+ * a copy of the GNU General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc.
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _APP_CONF_CLI_H
+#define _APP_CONF_CLI_H
+
+//
+// includes
+//
+
+#include "app_conference.h"
+#include "common.h"
+
+//
+// function declarations
+//
+
+int conference_show_stats( int fd, int argc, char *argv[] ) ;
+int conference_show_stats_name( int fd, const char* name ) ;
+
+int conference_restart( int fd, int argc, char *argv[] );
+
+int conference_debug( int fd, int argc, char *argv[] ) ;
+int conference_no_debug( int fd, int argc, char *argv[] ) ;
+
+int conference_list( int fd, int argc, char *argv[] ) ;
+int conference_kick( int fd, int argc, char *argv[] ) ;
+int conference_kickchannel( int fd, int argc, char *argv[] ) ;
+
+int conference_exit( int fd, int argc, char *argv[] ) ;
+
+int conference_mute( int fd, int argc, char *argv[] ) ;
+int conference_unmute( int fd, int argc, char *argv[] ) ;
+int conference_mutechannel( int fd, int argc, char *argv[] ) ;
+int conference_unmutechannel( int fd, int argc, char *argv[] ) ;
+int conference_viewstream( int fd, int argc, char *argv[] ) ;
+int conference_viewchannel( int fd, int argc, char *argv[] ) ;
+
+int conference_play_sound( int fd, int argc, char *argv[] ) ;
+int conference_stop_sounds( int fd, int argc, char *argv[] ) ;
+
+int conference_play_video( int fd, int argc, char *argv[] ) ;
+int conference_stop_videos( int fd, int argc, char *argv[] ) ;
+
+int conference_end( int fd, int argc, char *argv[] ) ;
+
+int conference_lock( int fd, int argc, char *argv[] ) ;
+int conference_lockchannel( int fd, int argc, char *argv[] ) ;
+int conference_unlock( int fd, int argc, char *argv[] ) ;
+
+int conference_set_default(int fd, int argc, char *argv[] ) ;
+int conference_set_defaultchannel(int fd, int argc, char *argv[] ) ;
+
+int conference_video_mute(int fd, int argc, char *argv[] ) ;
+int conference_video_mutechannel(int fd, int argc, char *argv[] ) ;
+int conference_video_unmute(int fd, int argc, char *argv[] ) ;
+int conference_video_unmutechannel(int fd, int argc, char *argv[] ) ;
+
+int conference_text( int fd, int argc, char *argv[] ) ;
+int conference_textchannel( int fd, int argc, char *argv[] ) ;
+int conference_textbroadcast( int fd, int argc, char *argv[] ) ;
+
+int conference_drive( int fd, int argc, char *argv[] ) ;
+int conference_drivechannel(int fd, int argc, char *argv[] );
+
+int manager_conference_end(struct mansession *s, const struct message *m);
+
+void register_conference_cli( void ) ;
+void unregister_conference_cli( void ) ;
+
+
+#endif
diff --git a/apps/conference/common.h b/apps/conference/common.h
new file mode 100644
index 0000000..989398c
--- /dev/null
+++ b/apps/conference/common.h
@@ -0,0 +1,63 @@
+
+// $Id: common.h 880 2007-04-25 15:23:59Z jpgrayson $
+
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
+ *
+ * Video Conferencing support added by
+ * Neil Stratford <neils@vipadia.com>
+ * Copyright (C) 2005, 2005 Vipadia Limited
+ *
+ * VAD driven video conferencing, text message support
+ * and miscellaneous enhancements added by
+ * Mihai Balea <mihai at hates dot ms>
+ *
+ * This program may be modified and distributed under the
+ * terms of the GNU General Public License. You should have received
+ * a copy of the GNU General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc.
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _APP_CONF_COMMON_H
+#define _APP_CONF_COMMON_H
+
+#include <asterisk/time.h>
+
+// typedef includes
+#include "conf_frame.h"
+
+// function includesee
+//#include "member.h"
+#include "conference.h"
+#include "frame.h"
+#include "cli.h"
+
+/* Utility functions */
+
+/* LOG the time taken to execute a function (like lock acquisition */
+#if 1
+#define TIMELOG(func,min,message) \
+ do { \
+ struct timeval t1, t2; \
+ int diff; \
+ t1 = ast_tvnow(); \
+ func; \
+ t2 = ast_tvnow(); \
+ if ( (diff = ast_tvdiff_ms(t2, t1)) > min ) \
+ ast_log( AST_CONF_DEBUG, "TimeLog: %s: %d ms\n", message, diff); \
+ } while (0)
+#else
+#define TIMELOG(func,min,message) func
+#endif
+
+#endif
diff --git a/apps/conference/conf_frame.h b/apps/conference/conf_frame.h
new file mode 100644
index 0000000..e73e57a
--- /dev/null
+++ b/apps/conference/conf_frame.h
@@ -0,0 +1,73 @@
+
+// $Id: conf_frame.h 880 2007-04-25 15:23:59Z jpgrayson $
+
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
+ *
+ * Video Conferencing support added by
+ * Neil Stratford <neils@vipadia.com>
+ * Copyright (C) 2005, 2005 Vipadia Limited
+ *
+ * VAD driven video conferencing, text message support
+ * and miscellaneous enhancements added by
+ * Mihai Balea <mihai at hates dot ms>
+ *
+ * This program may be modified and distributed under the
+ * terms of the GNU General Public License. You should have received
+ * a copy of the GNU General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc.
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _APP_CONF_STRUCTS_H
+#define _APP_CONF_STRUCTS_H
+
+//
+// includes
+//
+
+#include "app_conference.h"
+#include "common.h"
+
+//
+// struct declarations
+//
+
+typedef struct conf_frame
+{
+ // frame audio data
+ struct ast_frame* fr ;
+
+ // array of converted versions for listeners
+ struct ast_frame* converted[ AC_SUPPORTED_FORMATS ] ;
+
+ // pointer to the frame's owner
+ struct ast_conf_member* member ; // who sent this frame
+
+ // frame meta data
+// struct timeval timestamp ;
+// unsigned long cycleid ;
+// int priority ;
+
+ // linked-list pointers
+ struct conf_frame* next ;
+ struct conf_frame* prev ;
+
+ // should this frame be preserved
+ short static_frame ;
+
+ // pointer to mixing buffer
+ char* mixed_buffer ;
+} conf_frame ;
+
+
+#endif
diff --git a/apps/conference/conference.c b/apps/conference/conference.c
new file mode 100644
index 0000000..72c71d9
--- /dev/null
+++ b/apps/conference/conference.c
@@ -0,0 +1,2923 @@
+
+// $Id: conference.c 886 2007-08-06 14:33:34Z bcholew $
+
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
+ *
+ * Video Conferencing support added by
+ * Neil Stratford <neils@vipadia.com>
+ * Copyright (C) 2005, 2005 Vipadia Limited
+ *
+ * VAD driven video conferencing, text message support
+ * and miscellaneous enhancements added by
+ * Mihai Balea <mihai at hates dot ms>
+ *
+ * This program may be modified and distributed under the
+ * terms of the GNU General Public License. You should have received
+ * a copy of the GNU General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc.
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "asterisk/autoconfig.h"
+#include "conference.h"
+#include "asterisk/utils.h"
+
+//
+// static variables
+//
+
+// single-linked list of current conferences
+struct ast_conference *conflist = NULL ;
+
+// mutex for synchronizing access to conflist
+//static ast_mutex_t conflist_lock = AST_MUTEX_INITIALIZER ;
+AST_MUTEX_DEFINE_STATIC(conflist_lock);
+
+static int conference_count = 0 ;
+
+
+//
+// main conference function
+//
+
+void conference_exec( struct ast_conference *conf )
+{
+
+ struct ast_conf_member *next_member;
+ struct ast_conf_member *member, *video_source_member, *dtmf_source_member;;
+ struct conf_frame *cfr, *spoken_frames, *send_frames;
+
+ // count number of speakers, number of listeners
+ int speaker_count ;
+ int listener_count ;
+
+ ast_log( AST_CONF_DEBUG, "Entered conference_exec, name => %s\n", conf->name ) ;
+
+ // timer timestamps
+ struct timeval base, curr, notify ;
+ base = notify = ast_tvnow();
+
+ // holds differences of curr and base
+ long time_diff = 0 ;
+ long time_sleep = 0 ;
+
+ int since_last_slept = 0 ;
+
+ //
+ // variables for checking thread frequency
+ //
+
+ // count to AST_CONF_FRAMES_PER_SECOND
+ int tf_count = 0 ;
+ long tf_diff = 0 ;
+ float tf_frequency = 0.0 ;
+
+ struct timeval tf_base, tf_curr ;
+ tf_base = ast_tvnow();
+
+ //
+ // main conference thread loop
+ //
+
+
+ while ( 42 == 42 )
+ {
+ // update the current timestamp
+ curr = ast_tvnow();
+
+ // calculate difference in timestamps
+ time_diff = ast_tvdiff_ms(curr, base);
+
+ // calculate time we should sleep
+ time_sleep = AST_CONF_FRAME_INTERVAL - time_diff ;
+
+ if ( time_sleep > 0 )
+ {
+ // sleep for sleep_time ( as milliseconds )
+ usleep( time_sleep * 1000 ) ;
+
+ // reset since last slept counter
+ since_last_slept = 0 ;
+
+ continue ;
+ }
+ else
+ {
+ // long sleep warning
+ if (
+ since_last_slept == 0
+ && time_diff > AST_CONF_CONFERENCE_SLEEP * 2
+ )
+ {
+ ast_log(
+ AST_CONF_DEBUG,
+ "long scheduling delay, time_diff => %ld, AST_CONF_FRAME_INTERVAL => %d\n",
+ time_diff, AST_CONF_FRAME_INTERVAL
+ ) ;
+ }
+
+ // increment times since last slept
+ ++since_last_slept ;
+
+ // sleep every other time
+ if ( since_last_slept % 2 )
+ usleep( 0 ) ;
+ }
+
+ // adjust the timer base ( it will be used later to timestamp outgoing frames )
+ add_milliseconds( &base, AST_CONF_FRAME_INTERVAL ) ;
+
+ //
+ // check thread frequency
+ //
+
+ if ( ++tf_count >= AST_CONF_FRAMES_PER_SECOND )
+ {
+ // update current timestamp
+ tf_curr = ast_tvnow();
+
+ // compute timestamp difference
+ tf_diff = ast_tvdiff_ms(tf_curr, tf_base);
+
+ // compute sampling frequency
+ tf_frequency = ( float )( tf_diff ) / ( float )( tf_count ) ;
+
+ if (
+ ( tf_frequency <= ( float )( AST_CONF_FRAME_INTERVAL - 1 ) )
+ || ( tf_frequency >= ( float )( AST_CONF_FRAME_INTERVAL + 1 ) )
+ )
+ {
+ ast_log(
+ LOG_WARNING,
+ "processed frame frequency variation, name => %s, tf_count => %d, tf_diff => %ld, tf_frequency => %2.4f\n",
+ conf->name, tf_count, tf_diff, tf_frequency
+ ) ;
+ }
+
+ // reset values
+ tf_base = tf_curr ;
+ tf_count = 0 ;
+ }
+
+ //-----------------//
+ // INCOMING FRAMES //
+ //-----------------//
+
+ // ast_log( AST_CONF_DEBUG, "PROCESSING FRAMES, conference => %s, step => %d, ms => %ld\n",
+ // conf->name, step, ( base.tv_usec / 20000 ) ) ;
+
+ //
+ // check if the conference is empty and if so
+ // remove it and break the loop
+ //
+
+ // acquire the conference list lock
+ ast_mutex_lock(&conflist_lock);
+
+ // acquire the conference mutex
+ ast_mutex_lock(&conf->lock);
+
+ if ( conf->membercount == 0 )
+ {
+ if (conf->debug_flag)
+ {
+ ast_log( LOG_NOTICE, "removing conference, count => %d, name => %s\n", conf->membercount, conf->name ) ;
+ }
+ remove_conf( conf ) ; // stop the conference
+
+ // We don't need to release the conf mutex, since it was destroyed anyway
+
+ // release the conference list lock
+ ast_mutex_unlock(&conflist_lock);
+
+ break ; // break from main processing loop
+ }
+
+ // release the conference mutex
+ ast_mutex_unlock(&conf->lock);
+
+ // release the conference list lock
+ ast_mutex_unlock(&conflist_lock);
+
+
+ //
+ // Start processing frames
+ //
+
+ // acquire conference mutex
+ TIMELOG(ast_mutex_lock( &conf->lock ),1,"conf thread conf lock");
+
+ if ( conf->membercount == 0 )
+ {
+ // release the conference mutex
+ ast_mutex_unlock(&conf->lock);
+ continue; // We'll check again at the top of the loop
+ }
+
+ // update the current delivery time
+ conf->delivery_time = base ;
+
+ //
+ // loop through the list of members
+ // ( conf->memberlist is a single-linked list )
+ //
+
+ // ast_log( AST_CONF_DEBUG, "begin processing incoming audio, name => %s\n", conf->name ) ;
+
+ // reset speaker and listener count
+ speaker_count = 0 ;
+ listener_count = 0 ;
+
+ // get list of conference members
+ member = conf->memberlist ;
+
+ // reset pointer lists
+ spoken_frames = NULL ;
+
+ // reset video source
+ video_source_member = NULL;
+
+ // reset dtmf source
+ dtmf_source_member = NULL;
+
+ // loop over member list to retrieve queued frames
+ while ( member != NULL )
+ {
+ // take note of next member - before it's too late
+ next_member = member->next;
+
+ // this MIGHT delete member
+ member_process_spoken_frames(conf,member,&spoken_frames,time_diff,
+ &listener_count, &speaker_count);
+
+ // adjust our pointer to the next inline
+ member = next_member;
+ }
+
+ // ast_log( AST_CONF_DEBUG, "finished processing incoming audio, name => %s\n", conf->name ) ;
+
+
+ //---------------//
+ // MIXING FRAMES //
+ //---------------//
+
+ // mix frames and get batch of outgoing frames
+ send_frames = mix_frames( spoken_frames, speaker_count, listener_count ) ;
+
+ // accounting: if there are frames, count them as one incoming frame
+ if ( send_frames != NULL )
+ {
+ // set delivery timestamp
+ //set_conf_frame_delivery( send_frames, base ) ;
+// ast_log ( LOG_WARNING, "base = %d,%d: conf->delivery_time = %d,%d\n",base.tv_sec,base.tv_usec, conf->delivery_time.tv_sec, conf->delivery_time.tv_usec);
+
+ // ast_log( AST_CONF_DEBUG, "base => %ld.%ld %d\n", base.tv_sec, base.tv_usec, ( int )( base.tv_usec / 1000 ) ) ;
+
+ conf->stats.frames_in++ ;
+ }
+
+ //-----------------//
+ // OUTGOING FRAMES //
+ //-----------------//
+
+ //
+ // loop over member list to queue outgoing frames
+ //
+ for ( member = conf->memberlist ; member != NULL ; member = member->next )
+ {
+ member_process_outgoing_frames(conf, member, send_frames);
+ }
+
+ //-------//
+ // VIDEO //
+ //-------//
+
+ // loop over the incoming frames and send to all outgoing
+ // TODO: this is an O(n^2) algorithm. Can we speed it up without sacrificing per-member switching?
+ for (video_source_member = conf->memberlist;
+ video_source_member != NULL;
+ video_source_member = video_source_member->next)
+ {
+ while ((cfr = get_incoming_video_frame( video_source_member )))
+ {
+ for (member = conf->memberlist; member != NULL; member = member->next)
+ {
+ // skip members that are not ready or are not supposed to receive video
+ if ( !member->ready_for_outgoing || member->norecv_video )
+ continue ;
+
+ if ( conf->video_locked )
+ {
+ // Always send video from the locked source
+ if ( conf->current_video_source_id == video_source_member->id )
+ queue_outgoing_video_frame(member, cfr->fr, conf->delivery_time);
+ } else
+ {
+ // If the member has vad switching disabled and dtmf switching enabled, use that
+ if ( member->dtmf_switch &&
+ !member->vad_switch &&
+ member->req_id == video_source_member->id
+ )
+ {
+ queue_outgoing_video_frame(member, cfr->fr, conf->delivery_time);
+ } else
+ {
+ // If no dtmf switching, then do VAD switching
+ // The VAD switching decision code should make sure that our video source
+ // is legit
+ if ( (conf->current_video_source_id == video_source_member->id) ||
+ (conf->current_video_source_id < 0 &&
+ conf->default_video_source_id == video_source_member->id
+ )
+ )
+ {
+ queue_outgoing_video_frame(member, cfr->fr, conf->delivery_time);
+ }
+ }
+
+
+ }
+ }
+ // Garbage collection
+ delete_conf_frame(cfr);
+ }
+ }
+
+ //------//
+ // DTMF //
+ //------//
+
+ // loop over the incoming frames and send to all outgoing
+ for (dtmf_source_member = conf->memberlist; dtmf_source_member != NULL; dtmf_source_member = dtmf_source_member->next)
+ {
+ while ((cfr = get_incoming_dtmf_frame( dtmf_source_member )))
+ {
+ for (member = conf->memberlist; member != NULL; member = member->next)
+ {
+ // skip members that are not ready
+ if ( member->ready_for_outgoing == 0 )
+ {
+ continue ;
+ }
+
+ if (member != dtmf_source_member)
+ {
+ // Send the latest frame
+ queue_outgoing_dtmf_frame(member, cfr->fr);
+ }
+ }
+ // Garbage collection
+ delete_conf_frame(cfr);
+ }
+ }
+
+ //---------//
+ // CLEANUP //
+ //---------//
+
+ // clean up send frames
+ while ( send_frames != NULL )
+ {
+ // accouting: count all frames and mixed frames
+ if ( send_frames->member == NULL )
+ conf->stats.frames_out++ ;
+ else
+ conf->stats.frames_mixed++ ;
+
+ // delete the frame
+ send_frames = delete_conf_frame( send_frames ) ;
+ }
+
+ //
+ // notify the manager of state changes every 100 milliseconds
+ // we piggyback on this for VAD switching logic
+ //
+
+ if ( ( ast_tvdiff_ms(curr, notify) / AST_CONF_NOTIFICATION_SLEEP ) >= 1 )
+ {
+ // Do VAD switching logic
+ // We need to do this here since send_state_change_notifications
+ // resets the flags
+ if ( !conf->video_locked )
+ do_VAD_switching(conf);
+
+ // send the notifications
+ send_state_change_notifications( conf->memberlist ) ;
+
+ // increment the notification timer base
+ add_milliseconds( ¬ify, AST_CONF_NOTIFICATION_SLEEP ) ;
+ }
+
+ // release conference mutex
+ ast_mutex_unlock( &conf->lock ) ;
+
+ // !!! TESTING !!!
+ // usleep( 1 ) ;
+ }
+ // end while ( 42 == 42 )
+
+ //
+ // exit the conference thread
+ //
+
+ ast_log( AST_CONF_DEBUG, "exit conference_exec\n" ) ;
+
+ // exit the thread
+ pthread_exit( NULL ) ;
+
+ return ;
+}
+
+//
+// manange conference functions
+//
+
+// called by app_conference.c:load_module()
+void init_conference( void )
+{
+ ast_mutex_init( &conflist_lock ) ;
+}
+
+struct ast_conference* start_conference( struct ast_conf_member* member )
+{
+ // check input
+ if ( member == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to handle null member\n" ) ;
+ return NULL ;
+ }
+
+ struct ast_conference* conf = NULL ;
+
+ // acquire the conference list lock
+ ast_mutex_lock(&conflist_lock);
+
+
+
+ // look for an existing conference
+ ast_log( AST_CONF_DEBUG, "attempting to find requested conference\n" ) ;
+ conf = find_conf( member->conf_name ) ;
+
+ // unable to find an existing conference, try to create one
+ if ( conf == NULL )
+ {
+ // create a new conference
+ ast_log( AST_CONF_DEBUG, "attempting to create requested conference\n" ) ;
+
+ // create the new conference with one member
+ conf = create_conf( member->conf_name, member ) ;
+
+ // return an error if create_conf() failed
+ if ( conf == NULL )
+ ast_log( LOG_ERROR, "unable to find or create requested conference\n" ) ;
+ }
+ else
+ {
+ //
+ // existing conference found, add new member to the conference
+ //
+ // once we call add_member(), this thread
+ // is responsible for calling delete_member()
+ //
+ add_member( member, conf ) ;
+ }
+
+ // release the conference list lock
+ ast_mutex_unlock(&conflist_lock);
+
+ return conf ;
+}
+
+// This function should be called with conflist_lock mutex being held
+struct ast_conference* find_conf( const char* name )
+{
+ // no conferences exist
+ if ( conflist == NULL )
+ {
+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", name ) ;
+ return NULL ;
+ }
+
+ struct ast_conference *conf = conflist ;
+
+ // loop through conf list
+ while ( conf != NULL )
+ {
+ if ( strncasecmp( (char*)&(conf->name), name, 80 ) == 0 )
+ {
+ // found conf name match
+ ast_log( AST_CONF_DEBUG, "found conference in conflist, name => %s\n", name ) ;
+ return conf;
+ }
+ conf = conf->next ;
+ }
+
+ ast_log( AST_CONF_DEBUG, "unable to find conference in conflist, name => %s\n", name ) ;
+ return NULL;
+}
+
+// This function should be called with conflist_lock held
+struct ast_conference* create_conf( char* name, struct ast_conf_member* member )
+{
+ ast_log( AST_CONF_DEBUG, "entered create_conf, name => %s\n", name ) ;
+
+ //
+ // allocate memory for conference
+ //
+
+ struct ast_conference *conf = malloc( sizeof( struct ast_conference ) ) ;
+
+ if ( conf == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to malloc ast_conference\n" ) ;
+ return NULL ;
+ }
+
+ //
+ // initialize conference
+ //
+
+ conf->next = NULL ;
+ conf->memberlist = NULL ;
+
+ conf->membercount = 0 ;
+ conf->conference_thread = -1 ;
+
+ conf->debug_flag = 0 ;
+
+ conf->id_count = 0;
+
+ conf->default_video_source_id = -1;
+ conf->current_video_source_id = -1;
+ //conf->current_video_source_timestamp = ast_tvnow();
+ conf->video_locked = 0;
+
+ // zero stats
+ memset( &conf->stats, 0x0, sizeof( ast_conference_stats ) ) ;
+
+ // record start time
+ conf->stats.time_entered = ast_tvnow();
+
+ // copy name to conference
+ strncpy( (char*)&(conf->name), name, sizeof(conf->name) - 1 ) ;
+ strncpy( (char*)&(conf->stats.name), name, sizeof(conf->name) - 1 ) ;
+
+ // initialize mutexes
+ ast_mutex_init( &conf->lock ) ;
+
+ // build translation paths
+ conf->from_slinear_paths[ AC_SLINEAR_INDEX ] = NULL ;
+ conf->from_slinear_paths[ AC_ULAW_INDEX ] = ast_translator_build_path( AST_FORMAT_ULAW, AST_FORMAT_SLINEAR ) ;
+ conf->from_slinear_paths[ AC_ALAW_INDEX ] = ast_translator_build_path( AST_FORMAT_ALAW, AST_FORMAT_SLINEAR ) ;
+ conf->from_slinear_paths[ AC_GSM_INDEX ] = ast_translator_build_path( AST_FORMAT_GSM, AST_FORMAT_SLINEAR ) ;
+ conf->from_slinear_paths[ AC_SPEEX_INDEX ] = ast_translator_build_path( AST_FORMAT_SPEEX, AST_FORMAT_SLINEAR ) ;
+#ifdef AC_USE_G729A
+ conf->from_slinear_paths[ AC_G729A_INDEX ] = ast_translator_build_path( AST_FORMAT_G729A, AST_FORMAT_SLINEAR ) ;
+#endif
+
+ // add the initial member
+ add_member( member, conf ) ;
+
+ ast_log( AST_CONF_DEBUG, "added new conference to conflist, name => %s\n", name ) ;
+
+ //
+ // spawn thread for new conference, using conference_exec( conf )
+ //
+ // acquire conference mutexes
+ ast_mutex_lock( &conf->lock ) ;
+
+ if ( ast_pthread_create( &conf->conference_thread, NULL, (void*)conference_exec, conf ) == 0 )
+ {
+ // detach the thread so it doesn't leak
+ pthread_detach( conf->conference_thread ) ;
+
+ // prepend new conference to conflist
+ conf->next = conflist ;
+ conflist = conf ;
+
+ // release conference mutexes
+ ast_mutex_unlock( &conf->lock ) ;
+
+ ast_log( AST_CONF_DEBUG, "started conference thread for conference, name => %s\n", conf->name ) ;
+ }
+ else
+ {
+ ast_log( LOG_ERROR, "unable to start conference thread for conference %s\n", conf->name ) ;
+
+ conf->conference_thread = -1 ;
+
+ // release conference mutexes
+ ast_mutex_unlock( &conf->lock ) ;
+
+ // clean up conference
+ free( conf ) ;
+ conf = NULL ;
+ }
+
+ // count new conference
+ if ( conf != NULL )
+ ++conference_count ;
+
+ return conf ;
+}
+
+//This function should be called with conflist_lock and conf->lock held
+void remove_conf( struct ast_conference *conf )
+{
+ int c;
+
+ // ast_log( AST_CONF_DEBUG, "attempting to remove conference, name => %s\n", conf->name ) ;
+
+ struct ast_conference *conf_current = conflist ;
+ struct ast_conference *conf_temp = NULL ;
+
+ // loop through list of conferences
+ while ( conf_current != NULL )
+ {
+ // if conf_current point to the passed conf,
+ if ( conf_current == conf )
+ {
+ if ( conf_temp == NULL )
+ {
+ // this is the first conf in the list, so we just point
+ // conflist past the current conf to the next
+ conflist = conf_current->next ;
+ }
+ else
+ {
+ // this is not the first conf in the list, so we need to
+ // point the preceeding conf to the next conf in the list
+ conf_temp->next = conf_current->next ;
+ }
+
+ //
+ // do some frame clean up
+ //
+
+ for ( c = 0 ; c < AC_SUPPORTED_FORMATS ; ++c )
+ {
+ // free the translation paths
+ if ( conf_current->from_slinear_paths[ c ] != NULL )
+ {
+ ast_translator_free_path( conf_current->from_slinear_paths[ c ] ) ;
+ conf_current->from_slinear_paths[ c ] = NULL ;
+ }
+ }
+
+ // calculate time in conference
+ // total time converted to seconds
+ long tt = ast_tvdiff_ms(ast_tvnow(),
+ conf_current->stats.time_entered) / 1000;
+
+ // report accounting information
+ if (conf->debug_flag)
+ {
+ ast_log( LOG_NOTICE, "conference accounting, fi => %ld, fo => %ld, fm => %ld, tt => %ld\n",
+ conf_current->stats.frames_in, conf_current->stats.frames_out, conf_current->stats.frames_mixed, tt ) ;
+
+ ast_log( AST_CONF_DEBUG, "removed conference, name => %s\n", conf_current->name ) ;
+ }
+
+ ast_mutex_unlock( &conf_current->lock ) ;
+
+ free( conf_current ) ;
+ conf_current = NULL ;
+
+ break ;
+ }
+
+ // save a refence to the soon to be previous conf
+ conf_temp = conf_current ;
+
+ // move conf_current to the next in the list
+ conf_current = conf_current->next ;
+ }
+
+ // count new conference
+ --conference_count ;
+
+ return ;
+}
+
+int get_new_id( struct ast_conference *conf )
+{
+ // must have the conf lock when calling this
+ int newid;
+ struct ast_conf_member *othermember;
+ // get a video ID for this member
+ newid = 0;
+ othermember = conf->memberlist;
+ while (othermember)
+ {
+ if (othermember->id == newid)
+ {
+ newid++;
+ othermember = conf->memberlist;
+ }
+ else
+ {
+ othermember = othermember->next;
+ }
+ }
+ return newid;
+}
+
+
+int end_conference(const char *name, int hangup )
+{
+ struct ast_conference *conf;
+
+ // acquire the conference list lock
+ ast_mutex_lock(&conflist_lock);
+
+ conf = find_conf(name);
+ if ( conf == NULL )
+ {
+ ast_log( LOG_WARNING, "could not find conference\n" ) ;
+
+ // release the conference list lock
+ ast_mutex_unlock(&conflist_lock);
+
+ return -1 ;
+ }
+
+ // acquire the conference lock
+ ast_mutex_lock( &conf->lock ) ;
+
+ // get list of conference members
+ struct ast_conf_member* member = conf->memberlist ;
+
+ // loop over member list and request hangup
+ while ( member != NULL )
+ {
+ // acquire member mutex and request hangup
+ // or just kick
+ ast_mutex_lock( &member->lock ) ;
+ if (hangup)
+ ast_softhangup( member->chan, 1 ) ;
+ else
+ member->kick_flag = 1;
+ ast_mutex_unlock( &member->lock ) ;
+
+ // go on to the next member
+ // ( we have the conf lock, so we know this is okay )
+ member = member->next ;
+ }
+
+ // release the conference lock
+ ast_mutex_unlock( &conf->lock ) ;
+
+ // release the conference list lock
+ ast_mutex_unlock(&conflist_lock);
+
+ return 0 ;
+}
+
+//
+// member-related functions
+//
+
+// This function should be called with conflist_lock held
+void add_member( struct ast_conf_member *member, struct ast_conference *conf )
+{
+ int newid, last_id;
+ struct ast_conf_member *othermember;
+ int count;
+
+ if ( conf == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to add member to NULL conference\n" ) ;
+ return ;
+ }
+
+ // acquire the conference lock
+ ast_mutex_lock( &conf->lock ) ;
+
+ if (member->id < 0)
+ {
+ // get an ID for this member
+ newid = get_new_id( conf );
+ member->id = newid;
+ } else
+ {
+ // boot anyone who has this id already
+ othermember = conf->memberlist;
+ while (othermember)
+ {
+ if (othermember->id == member->id)
+ othermember->id = -1;
+ othermember = othermember->next;
+ }
+ }
+
+ if ( member->mute_video )
+ {
+ send_text_message_to_member(member, AST_CONF_CONTROL_STOP_VIDEO);
+ }
+
+ // set a long term id
+ int new_initial_id = 0;
+ othermember = conf->memberlist;
+ while (othermember)
+ {
+ if (othermember->initial_id >= new_initial_id)
+ new_initial_id++;
+
+ othermember = othermember->next;
+ }
+ member->initial_id = new_initial_id;
+
+
+ ast_log( AST_CONF_DEBUG, "new video id %d\n", newid) ;
+
+ if (conf->memberlist) last_id = conf->memberlist->id;
+ else last_id = 0;
+
+ if (member->req_id < 0) // otherwise pre-selected in create_member
+ {
+ // want to watch the last person to 0 or 1 (for now)
+ if (member->id > 0) member->req_id = 0;
+ else member->req_id = 1;
+ }
+
+ member->next = conf->memberlist ; // next is now list
+ conf->memberlist = member ; // member is now at head of list
+
+ // update conference stats
+ count = count_member( member, conf, 1 ) ;
+
+ ast_log( AST_CONF_DEBUG, "member added to conference, name => %s\n", conf->name ) ;
+
+ // release the conference lock
+ ast_mutex_unlock( &conf->lock ) ;
+
+ return ;
+}
+
+int remove_member( struct ast_conf_member* member, struct ast_conference* conf )
+{
+ // check for member
+ if ( member == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to remove NULL member\n" ) ;
+ return -1 ;
+ }
+
+ // check for conference
+ if ( conf == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to remove member from NULL conference\n" ) ;
+ return -1 ;
+ }
+
+ //
+ // loop through the member list looking
+ // for the requested member
+ //
+
+ ast_mutex_lock( &conf->lock );
+
+ struct ast_conf_member *member_list = conf->memberlist ;
+ struct ast_conf_member *member_temp = NULL ;
+
+ int count = -1 ; // default return code
+
+ while ( member_list != NULL )
+ {
+ // set conference to send no_video to anyone who was watching us
+ ast_mutex_lock( &member_list->lock ) ;
+ if (member_list->req_id == member->id)
+ {
+ member_list->conference = 1;
+ }
+ ast_mutex_unlock( &member_list->lock ) ;
+ member_list = member_list->next ;
+ }
+
+ member_list = conf->memberlist ;
+
+ int member_is_moderator = member->ismoderator;
+
+ while ( member_list != NULL )
+ {
+ // If member is driven by the currently visited member, break the association
+ if ( member_list->driven_member == member )
+ {
+ // Acquire member mutex
+ ast_mutex_lock(&member_list->lock);
+
+ member_list->driven_member = NULL;
+
+ // Release member mutex
+ ast_mutex_unlock(&member_list->lock);
+ }
+
+ if ( member_list == member )
+ {
+
+ //
+ // log some accounting information
+ //
+
+ // calculate time in conference (in seconds)
+ long tt = ast_tvdiff_ms(ast_tvnow(),
+ member->time_entered) / 1000;
+
+ if (conf->debug_flag)
+ {
+ ast_log(
+ LOG_NOTICE,
+ "member accounting, channel => %s, te => %ld, fi => %ld, fid => %ld, fo => %ld, fod => %ld, tt => %ld\n",
+ member->channel_name,
+ member->time_entered.tv_sec, member->frames_in, member->frames_in_dropped,
+ member->frames_out, member->frames_out_dropped, tt
+ ) ;
+ }
+
+ //
+ // if this is the first member in the linked-list,
+ // skip over the first member in the list, else
+ //
+ // point the previous 'next' to the current 'next',
+ // thus skipping the current member in the list
+ //
+ if ( member_temp == NULL )
+ conf->memberlist = member->next ;
+ else
+ member_temp->next = member->next ;
+
+ // update conference stats
+ count = count_member( member, conf, 0 ) ;
+
+ // Check if member is the default or current video source
+ if ( conf->current_video_source_id == member->id )
+ {
+ if ( conf->video_locked )
+ unlock_conference(conf->name);
+ do_video_switching(conf, conf->default_video_source_id, 0);
+ } else if ( conf->default_video_source_id == member->id )
+ {
+ conf->default_video_source_id = -1;
+ }
+
+ // output to manager...
+ manager_event(
+ EVENT_FLAG_CALL,
+ "ConferenceLeave",
+ "ConferenceName: %s\r\n"
+ "Member: %d\r\n"
+ "Channel: %s\r\n"
+ "CallerID: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "Duration: %ld\r\n"
+ "Count: %d\r\n",
+ conf->name,
+ member->id,
+ member->channel_name,
+ member->callerid,
+ member->callername,
+ tt, count
+ ) ;
+
+ // save a pointer to the current member,
+ // and then point to the next member in the list
+ member_list = member_list->next ;
+
+ // leave member_temp alone.
+ // it already points to the previous (or NULL).
+ // it will still be the previous after member is deleted
+
+ // notify others about leave
+ char * leave_snd = member->leave_snd;
+ if (conf->membercount && strcmp(leave_snd, "-")>1)
+ {
+ struct ast_conf_member *membertest = conf->memberlist;
+ while (membertest != NULL)
+ {
+ if (membertest != member)
+ {
+ // lock member for basic_play_sound
+ ast_mutex_lock(&membertest->lock);
+
+ // basic_play_sound unlock member automatically. do not mute on enter message
+ if (!basic_play_sound (membertest, leave_snd, 0))
+ {
+ ast_log(LOG_ERROR, "playing conference[%d] leave message <%s> FAILED on <%s>\n", conf->membercount, leave_snd, membertest->channel_name) ;
+ }
+ else
+ {
+ ast_log(LOG_NOTICE, "playing conference[%d] leave message <%s> on <%s>\n", conf->membercount, leave_snd, membertest->channel_name);
+ }
+ membertest = membertest->next;
+ }
+ }
+ }
+
+ // delete the member
+ delete_member( member ) ;
+
+ ast_log( AST_CONF_DEBUG, "removed member from conference, name => %s, remaining => %d\n",
+ conf->name, conf->membercount ) ;
+
+ //break ;
+ }
+ else
+ {
+ // if member is a moderator, we end the conference when they leave
+ if ( member_is_moderator )
+ {
+ ast_mutex_lock( &member_list->lock ) ;
+ member_list->kick_flag = 1;
+ ast_mutex_unlock( &member_list->lock ) ;
+ }
+
+ // save a pointer to the current member,
+ // and then point to the next member in the list
+ member_temp = member_list ;
+ member_list = member_list->next ;
+ }
+ }
+ ast_mutex_unlock( &conf->lock );
+
+ // return -1 on error, or the number of members
+ // remaining if the requested member was deleted
+ return count ;
+}
+
+int count_member( struct ast_conf_member* member, struct ast_conference* conf, short add_member )
+{
+ if ( member == NULL || conf == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to count member\n" ) ;
+ return -1 ;
+ }
+
+ short delta = ( add_member == 1 ) ? 1 : -1 ;
+
+ // increment member count
+ conf->membercount += delta ;
+
+ return conf->membercount ;
+}
+
+//
+// queue incoming frame functions
+//
+
+
+
+
+//
+// get conference stats
+//
+
+//
+// returns: -1 => error, 0 => debugging off, 1 => debugging on
+// state: on => 1, off => 0, toggle => -1
+//
+int set_conference_debugging( const char* name, int state )
+{
+ if ( name == NULL )
+ return -1 ;
+
+ // acquire mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ struct ast_conference *conf = conflist ;
+ int new_state = -1 ;
+
+ // loop through conf list
+ while ( conf != NULL )
+ {
+ if ( strncasecmp( (const char*)&(conf->name), name, 80 ) == 0 )
+ {
+ // lock conference
+ // ast_mutex_lock( &(conf->lock) ) ;
+
+ // toggle or set the state
+ if ( state == -1 )
+ conf->debug_flag = ( conf->debug_flag == 0 ) ? 1 : 0 ;
+ else
+ conf->debug_flag = ( state == 0 ) ? 0 : 1 ;
+
+ new_state = conf->debug_flag ;
+
+ // unlock conference
+ // ast_mutex_unlock( &(conf->lock) ) ;
+
+ break ;
+ }
+
+ conf = conf->next ;
+ }
+
+ // release mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return new_state ;
+}
+
+
+int get_conference_count( void )
+{
+ return conference_count ;
+}
+
+int show_conference_stats ( int fd )
+{
+ // no conferences exist
+ if ( conflist == NULL )
+ {
+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized.\n") ;
+ return 0 ;
+ }
+
+ // acquire mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ struct ast_conference *conf = conflist ;
+
+ ast_cli( fd, "%-20.20s %-40.40s\n", "Name", "Members") ;
+
+ // loop through conf list
+ while ( conf != NULL )
+ {
+ ast_cli( fd, "%-20.20s %3d\n", conf->name, conf->membercount ) ;
+ conf = conf->next ;
+ }
+
+ // release mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return 1 ;
+}
+
+int show_conference_list ( int fd, const char *name )
+{
+ struct ast_conf_member *member;
+
+ // no conferences exist
+ if ( conflist == NULL )
+ {
+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", name ) ;
+ return 0 ;
+ }
+
+ // acquire mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ struct ast_conference *conf = conflist ;
+
+ // loop through conf list
+ while ( conf != NULL )
+ {
+ if ( strncasecmp( (const char*)&(conf->name), name, 80 ) == 0 )
+ {
+ // acquire conference mutex
+ ast_mutex_lock(&conf->lock);
+
+ // do the biz
+ member = conf->memberlist ;
+ while ( member != NULL )
+ {
+ ast_cli( fd, "User #: %d ", member->id ) ;
+ ast_cli( fd, "Channel: %s ", member->channel_name ) ;
+
+ ast_cli( fd, "Flags:");
+ if ( member->mute_video ) ast_cli( fd, "C");
+ if ( member->norecv_video ) ast_cli( fd, "c");
+ if ( member->mute_audio ) ast_cli( fd, "L");
+ if ( member->norecv_audio ) ast_cli( fd, "l");
+ if ( member->vad_flag ) ast_cli( fd, "V");
+ if ( member->denoise_flag ) ast_cli( fd, "D");
+ if ( member->agc_flag ) ast_cli( fd, "A");
+ if ( member->dtmf_switch ) ast_cli( fd, "X");
+ if ( member->dtmf_relay ) ast_cli( fd, "R");
+ if ( member->vad_switch ) ast_cli( fd, "S");
+ if ( member->ismoderator ) ast_cli( fd, "M");
+ if ( member->no_camera ) ast_cli( fd, "N");
+ if ( member->does_text ) ast_cli( fd, "t");
+ if ( member->via_telephone ) ast_cli( fd, "T");
+ ast_cli( fd, " " );
+
+ if ( member->id == conf->default_video_source_id )
+ ast_cli(fd, "Default ");
+ if ( member->id == conf->current_video_source_id )
+ {
+ ast_cli(fd, "Showing ");
+ if ( conf->video_locked )
+ ast_cli(fd, "Locked ");
+ }
+ if ( member->driven_member != NULL )
+ {
+ ast_cli(fd, "Driving:%s(%d) ", member->driven_member->channel_name, member->driven_member->id);
+ }
+
+ ast_cli( fd, "\n");
+ member = member->next;
+ }
+
+ // release conference mutex
+ ast_mutex_unlock(&conf->lock);
+
+ break ;
+ }
+
+ conf = conf->next ;
+ }
+
+ // release mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return 1 ;
+}
+
+/* Dump list of conference info */
+int manager_conference_list( struct mansession *s, const struct message *m )
+{
+ const char *id = astman_get_header(m,"ActionID");
+ const char *conffilter = astman_get_header(m,"Conference");
+ char idText[256] = "";
+ struct ast_conf_member *member;
+
+ astman_send_ack(s, m, "Conference list will follow");
+
+ // no conferences exist
+ if ( conflist == NULL )
+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", conffilter );;
+
+ if (!ast_strlen_zero(id)) {
+ snprintf(idText,256,"ActionID: %s\r\n",id);
+ }
+
+ // acquire mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ struct ast_conference *conf = conflist ;
+
+ // loop through conf list
+ while ( conf != NULL )
+ {
+ if ( strncasecmp( (const char*)&(conf->name), conffilter, 80 ) == 0 )
+ {
+ // do the biz
+ member = conf->memberlist ;
+ while (member != NULL)
+ {
+ astman_append(s, "Event: ConferenceEntry\r\n"
+ "ConferenceName: %s\r\n"
+ "Member: %d\r\n"
+ "Channel: %s\r\n"
+ "CallerID: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "Muted: %s\r\n"
+ "VideoMuted: %s\r\n"
+ "Default: %s\r\n"
+ "Current: %s\r\n"
+ "%s"
+ "\r\n",
+ conf->name,
+ member->id,
+ member->channel_name,
+ member->chan->cid.cid_num ? member->chan->cid.cid_num : "unknown",
+ member->chan->cid.cid_name ? member->chan->cid.cid_name : "unknown",
+ member->mute_audio ? "YES" : "NO",
+ member->mute_video ? "YES" : "NO",
+ member->id == conf->default_video_source_id ? "YES" : "NO",
+ member->id == conf->current_video_source_id ? "YES" : "NO",
+ idText);
+ member = member->next;
+ }
+ break ;
+ }
+
+ conf = conf->next ;
+ }
+
+ // release mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ astman_append(s,
+ "Event: ConferenceListComplete\r\n"
+ "%s"
+ "\r\n",idText);
+
+ return RESULT_SUCCESS;
+}
+
+int kick_member ( const char* confname, int user_id)
+{
+ struct ast_conf_member *member;
+ int res = 0;
+
+ // no conferences exist
+ if ( conflist == NULL )
+ {
+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ;
+ return 0 ;
+ }
+
+ // acquire mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ struct ast_conference *conf = conflist ;
+
+ // loop through conf list
+ while ( conf != NULL )
+ {
+ if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 )
+ {
+ // do the biz
+ ast_mutex_lock( &conf->lock ) ;
+ member = conf->memberlist ;
+ while (member != NULL)
+ {
+ if (member->id == user_id)
+ {
+ ast_mutex_lock( &member->lock ) ;
+ member->kick_flag = 1;
+ //ast_soft_hangup(member->chan);
+ ast_mutex_unlock( &member->lock ) ;
+
+ res = 1;
+ }
+ member = member->next;
+ }
+ ast_mutex_unlock( &conf->lock ) ;
+ break ;
+ }
+
+ conf = conf->next ;
+ }
+
+ // release mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return res ;
+}
+
+int kick_channel ( const char *confname, const char *channel)
+{
+ struct ast_conf_member *member;
+ int res = 0;
+
+ // no conferences exist
+ if ( conflist == NULL )
+ {
+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ;
+ return 0 ;
+ }
+
+ if ( confname == NULL || channel == NULL || strlen(confname) == 0 || strlen(channel) == 0 )
+ return 0;
+
+ // acquire mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ struct ast_conference *conf = conflist ;
+
+ // loop through conf list
+ while ( conf != NULL )
+ {
+ if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 )
+ {
+ // do the biz
+ ast_mutex_lock( &conf->lock ) ;
+ member = conf->memberlist ;
+ while ( member != NULL )
+ {
+ if ( strncasecmp( member->channel_name, channel, 80 ) == 0 )
+ {
+ ast_mutex_lock( &member->lock ) ;
+ member->kick_flag = 1;
+ //ast_soft_hangup(member->chan);
+ ast_mutex_unlock( &member->lock ) ;
+
+ res = 1;
+ }
+ member = member->next;
+ }
+ ast_mutex_unlock( &conf->lock ) ;
+ break ;
+ }
+
+ conf = conf->next ;
+ }
+
+ // release mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return res ;
+}
+
+int kick_all ( void )
+{
+ struct ast_conf_member *member;
+ int res = 0;
+
+ // no conferences exist
+ if ( conflist == NULL )
+ {
+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized\n" ) ;
+ return 0 ;
+ }
+
+ // acquire mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ struct ast_conference *conf = conflist ;
+
+ // loop through conf list
+ while ( conf != NULL )
+ {
+ // do the biz
+ ast_mutex_lock( &conf->lock ) ;
+ member = conf->memberlist ;
+ while (member != NULL)
+ {
+ ast_mutex_lock( &member->lock ) ;
+ member->kick_flag = 1;
+ ast_mutex_unlock( &member->lock ) ;
+ member = member->next;
+ }
+ ast_mutex_unlock( &conf->lock ) ;
+ break ;
+
+ conf = conf->next ;
+ }
+
+ // release mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return res ;
+}
+
+int mute_member ( const char* confname, int user_id)
+{
+ struct ast_conf_member *member;
+ int res = 0;
+
+ // no conferences exist
+ if ( conflist == NULL )
+ {
+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ;
+ return 0 ;
+ }
+
+ // acquire mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ struct ast_conference *conf = conflist ;
+
+ // loop through conf list
+ while ( conf != NULL )
+ {
+ if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 )
+ {
+ // do the biz
+ ast_mutex_lock( &conf->lock ) ;
+ member = conf->memberlist ;
+ while (member != NULL)
+ {
+ if (member->id == user_id)
+ {
+ ast_mutex_lock( &member->lock ) ;
+ member->mute_audio = 1;
+ ast_mutex_unlock( &member->lock ) ;
+ res = 1;
+ }
+ member = member->next;
+ }
+ ast_mutex_unlock( &conf->lock ) ;
+ break ;
+ }
+
+ conf = conf->next ;
+ }
+
+ // release mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return res ;
+}
+
+int mute_channel ( const char* confname, const char* user_chan)
+{
+ struct ast_conf_member *member;
+ int res = 0;
+
+ // no conferences exist
+ if ( conflist == NULL )
+ {
+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ;
+ return 0 ;
+ }
+
+ // acquire mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ struct ast_conference *conf = conflist ;
+
+ // loop through conf list
+ while ( conf != NULL )
+ {
+ if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 )
+ {
+ // do the biz
+ ast_mutex_lock( &conf->lock ) ;
+ member = conf->memberlist ;
+ while (member != NULL)
+ {
+ if (strncasecmp( member->channel_name, user_chan, 80 ) == 0)
+ {
+ ast_mutex_lock( &member->lock ) ;
+ member->mute_audio = 1;
+ ast_mutex_unlock( &member->lock ) ;
+ res = 1;
+ }
+ member = member->next;
+ }
+ ast_mutex_unlock( &conf->lock ) ;
+ break ;
+ }
+
+ conf = conf->next ;
+ }
+
+ // release mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return res ;
+}
+
+int unmute_member ( const char* confname, int user_id)
+{
+ struct ast_conf_member *member;
+ int res = 0;
+
+ // no conferences exist
+ if ( conflist == NULL )
+ {
+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ;
+ return 0 ;
+ }
+
+ // acquire mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ struct ast_conference *conf = conflist ;
+
+ // loop through conf list
+ while ( conf != NULL )
+ {
+ if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 )
+ {
+ // do the biz
+ ast_mutex_lock( &conf->lock ) ;
+ member = conf->memberlist ;
+ while (member != NULL)
+ {
+ if (member->id == user_id)
+ {
+ ast_mutex_lock( &member->lock ) ;
+ member->mute_audio = 0;
+ ast_mutex_unlock( &member->lock ) ;
+ res = 1;
+ }
+ member = member->next;
+ }
+ ast_mutex_unlock( &conf->lock ) ;
+ break ;
+ }
+
+ conf = conf->next ;
+ }
+
+ // release mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return res ;
+}
+
+int unmute_channel (const char* confname, const char* user_chan)
+{
+ struct ast_conf_member *member;
+ int res = 0;
+
+ // no conferences exist
+ if ( conflist == NULL )
+ {
+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ;
+ return 0 ;
+ }
+
+ // acquire mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ struct ast_conference *conf = conflist ;
+
+ // loop through conf list
+ while ( conf != NULL )
+ {
+ if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 )
+ {
+ // do the biz
+ ast_mutex_lock( &conf->lock ) ;
+ member = conf->memberlist ;
+ while (member != NULL)
+ {
+ if (strncasecmp( member->channel_name, user_chan, 80 ) == 0)
+ {
+ ast_mutex_lock( &member->lock ) ;
+ member->mute_audio = 0;
+ ast_mutex_unlock( &member->lock ) ;
+ res = 1;
+ }
+ member = member->next;
+ }
+ ast_mutex_unlock( &conf->lock ) ;
+ break ;
+ }
+
+ conf = conf->next ;
+ }
+
+ // release mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return res ;
+}
+
+int viewstream_switch ( const char* confname, int user_id, int stream_id )
+{
+ struct ast_conf_member *member;
+ int res = 0;
+
+ // no conferences exist
+ if ( conflist == NULL )
+ {
+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ;
+ return 0 ;
+ }
+
+ // acquire mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ struct ast_conference *conf = conflist ;
+
+ // loop through conf list
+ while ( conf != NULL )
+ {
+ if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 )
+ {
+ // do the biz
+ ast_mutex_lock( &conf->lock ) ;
+ member = conf->memberlist ;
+ while (member != NULL)
+ {
+ if (member->id == user_id)
+ {
+ // switch the video
+ ast_mutex_lock( &member->lock ) ;
+
+ member->req_id = stream_id;
+ member->conference = 1;
+
+ ast_mutex_unlock( &member->lock ) ;
+ res = 1;
+ }
+ member = member->next;
+ }
+ ast_mutex_unlock( &conf->lock ) ;
+ break ;
+ }
+
+ conf = conf->next ;
+ }
+
+ // release mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return res ;
+}
+
+int viewchannel_switch ( const char* confname, const char* userchan, const char* streamchan )
+{
+ struct ast_conf_member *member;
+ int res = 0;
+ int stream_id = -1;
+
+ // no conferences exist
+ if ( conflist == NULL )
+ {
+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", confname ) ;
+ return 0 ;
+ }
+
+ // acquire mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ struct ast_conference *conf = conflist ;
+
+ // loop through conf list
+ while ( conf != NULL )
+ {
+ if ( strncasecmp( (const char*)&(conf->name), confname, 80 ) == 0 )
+ {
+ // do the biz
+ ast_mutex_lock( &conf->lock ) ;
+ member = conf->memberlist ;
+ while (member != NULL)
+ {
+ if (strncasecmp( member->channel_name, streamchan, 80 ) == 0)
+ {
+ stream_id = member->id;
+ }
+ member = member->next;
+ }
+ ast_mutex_unlock( &conf->lock ) ;
+ if (stream_id >= 0)
+ {
+ // do the biz
+ ast_mutex_lock( &conf->lock ) ;
+ member = conf->memberlist ;
+ while (member != NULL)
+ {
+ if (strncasecmp( member->channel_name, userchan, 80 ) == 0)
+ {
+ // switch the video
+ ast_mutex_lock( &member->lock ) ;
+
+ member->req_id = stream_id;
+ member->conference = 1;
+
+ ast_mutex_unlock( &member->lock ) ;
+ res = 1;
+ }
+ member = member->next;
+ }
+ ast_mutex_unlock( &conf->lock ) ;
+ }
+ break ;
+ }
+
+ conf = conf->next ;
+ }
+
+ // release mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return res ;
+}
+
+int get_conference_stats( ast_conference_stats* stats, int requested )
+{
+ // no conferences exist
+ if ( conflist == NULL )
+ {
+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialize\n" ) ;
+ return 0 ;
+ }
+
+ // acquire mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ // compare the number of requested to the number of available conferences
+ requested = ( get_conference_count() < requested ) ? get_conference_count() : requested ;
+
+ //
+ // loop through conf list
+ //
+
+ struct ast_conference* conf = conflist ;
+ int count = 0 ;
+
+ while ( count <= requested && conf != NULL )
+ {
+ // copy stats struct to array
+ stats[ count ] = conf->stats ;
+
+ conf = conf->next ;
+ ++count ;
+ }
+
+ // release mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return count ;
+}
+
+int get_conference_stats_by_name( ast_conference_stats* stats, const char* name )
+{
+ // no conferences exist
+ if ( conflist == NULL )
+ {
+ ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", name ) ;
+ return 0 ;
+ }
+
+ // make sure stats is null
+ stats = NULL ;
+
+ // acquire mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ struct ast_conference *conf = conflist ;
+
+ // loop through conf list
+ while ( conf != NULL )
+ {
+ if ( strncasecmp( (const char*)&(conf->name), name, 80 ) == 0 )
+ {
+ // copy stats for found conference
+ *stats = conf->stats ;
+ break ;
+ }
+
+ conf = conf->next ;
+ }
+
+ // release mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return ( stats == NULL ) ? 0 : 1 ;
+}
+
+struct ast_conf_member *find_member (const char *chan, int lock)
+{
+ struct ast_conf_member *found = NULL;
+ struct ast_conf_member *member;
+ struct ast_conference *conf;
+
+ ast_mutex_lock( &conflist_lock ) ;
+
+ conf = conflist;
+
+ // loop through conf list
+ while ( conf != NULL && !found )
+ {
+ // lock conference
+ ast_mutex_lock( &conf->lock );
+
+ member = conf->memberlist ;
+
+ while (member != NULL)
+ {
+ if(!strcmp(member->channel_name, chan)) {
+ found = member;
+ if(lock)
+ ast_mutex_lock(&member->lock);
+ break;
+ }
+ member = member->next;
+ }
+
+ // unlock conference
+ ast_mutex_unlock( &conf->lock );
+
+ conf = conf->next ;
+ }
+
+ // release mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return found;
+}
+
+// All the VAD-based video switching magic happens here
+// This function should be called inside conference_exec
+// The conference mutex should be locked, we don't have to do it here
+void do_VAD_switching(struct ast_conference *conf)
+{
+ struct ast_conf_member *member;
+ struct timeval current_time;
+ long longest_speaking;
+ struct ast_conf_member *longest_speaking_member;
+ int current_silent, current_no_camera, current_video_mute;
+ int default_no_camera, default_video_mute;
+
+ current_time = ast_tvnow();
+
+ // Scan the member list looking for the longest speaking member
+ // We also check if the currently speaking member has been silent for a while
+ // Also, we check for camera disabled or video muted members
+ // We say that a member is speaking after his speaking state has been on for
+ // at least AST_CONF_VIDEO_START_TIMEOUT ms
+ // We say that a member is silent after his speaking state has been off for
+ // at least AST_CONF_VIDEO_STOP_TIMEOUT ms
+ longest_speaking = 0;
+ longest_speaking_member = NULL;
+ current_silent = 0;
+ current_no_camera = 0;
+ current_video_mute = 0;
+ default_no_camera = 0;
+ default_video_mute = 0;
+ for ( member = conf->memberlist ;
+ member != NULL ;
+ member = member->next )
+ {
+ // Has the state changed since last time through this loop? Notify!
+ if ( member->speaking_state_notify )
+ {
+/* fprintf(stderr, "Mihai: member %d, channel %s has changed state to %s\n",
+ member->id,
+ member->channel_name,
+ ((member->speaking_state == 1 ) ? "speaking" : "silent")
+ ); */
+ }
+
+ // If a member connects via telephone, they don't have video
+ if ( member->via_telephone )
+ continue;
+
+ // We check for no VAD switching, video-muted or camera disabled
+ // If yes, this member will not be considered as a candidate for switching
+ // If this is the currently speaking member, then mark it so we force a switch
+ if ( !member->vad_switch )
+ continue;
+
+ if ( member->mute_video )
+ {
+ if ( member->id == conf->default_video_source_id )
+ default_video_mute = 1;
+ if ( member->id == conf->current_video_source_id )
+ current_video_mute = 1;
+ else
+ continue;
+ }
+
+ if ( member->no_camera )
+ {
+ if ( member->id == conf->default_video_source_id )
+ default_no_camera = 1;
+ if ( member->id == conf->current_video_source_id )
+ current_no_camera = 1;
+ else
+ continue;
+ }
+
+ // Check if current speaker has been silent for a while
+ if ( member->id == conf->current_video_source_id &&
+ member->speaking_state == 0 &&
+ ast_tvdiff_ms(current_time, member->last_state_change) > AST_CONF_VIDEO_STOP_TIMEOUT )
+ {
+ current_silent = 1;
+ }
+
+ // Find a candidate to switch to by looking for the longest speaking member
+ // We exclude the current video source from the search
+ if ( member->id != conf->current_video_source_id && member->speaking_state == 1 )
+ {
+ long tmp = ast_tvdiff_ms(current_time, member->last_state_change);
+ if ( tmp > AST_CONF_VIDEO_START_TIMEOUT && tmp > longest_speaking )
+ {
+ longest_speaking = tmp;
+ longest_speaking_member = member;
+ }
+ }
+ }
+
+ // We got our results, now let's make a decision
+ // If the currently speaking member has been marked as silent, then we take the longest
+ // speaking member. If no member is speaking, we go to default
+ // As a policy we don't want to switch away from a member that is speaking
+ // however, we might need to refine this to avoid a situation when a member has a
+ // low noise threshold or its VAD is simply stuck
+ if ( current_silent || current_no_camera || current_video_mute || conf->current_video_source_id < 0 )
+ {
+ if ( longest_speaking_member != NULL )
+ {
+ do_video_switching(conf, longest_speaking_member->id, 0);
+ } else
+ {
+ // If there's nobody speaking and we have a default that can send video, switch to it
+ // If not, then switch to empty (-1)
+ if ( conf->default_video_source_id >= 0 &&
+ !default_no_camera &&
+ !default_video_mute
+ )
+ do_video_switching(conf, conf->default_video_source_id, 0);
+ else
+ do_video_switching(conf, -1, 0);
+ }
+ }
+}
+
+int lock_conference(const char *conference, int member_id)
+{
+ struct ast_conference *conf;
+ struct ast_conf_member *member;
+ int res;
+
+ if ( conference == NULL || member_id < 0 )
+ return -1 ;
+
+ // acquire mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ // Look for conference
+ res = 0;
+ for ( conf = conflist ; conf != NULL ; conf = conf->next )
+ {
+ if ( strcmp(conference, conf->name) == 0 )
+ {
+ // Search member list for our member
+ // acquire conference mutex
+ ast_mutex_lock( &conf->lock );
+
+ for ( member = conf->memberlist ; member != NULL ; member = member->next )
+ {
+ if ( member->id == member_id && !member->mute_video )
+ {
+ do_video_switching(conf, member_id, 0);
+ conf->video_locked = 1;
+ res = 1;
+
+ manager_event(EVENT_FLAG_CALL, "ConferenceLock", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name);
+ break;
+ }
+ }
+
+ // Release conference mutex
+ ast_mutex_unlock( &conf->lock );
+ break;
+ }
+ }
+
+ // release mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return res;
+}
+
+int lock_conference_channel(const char *conference, const char *channel)
+{
+ struct ast_conference *conf;
+ struct ast_conf_member *member;
+ int res;
+
+ if ( conference == NULL || channel == NULL )
+ return -1 ;
+
+ // acquire mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ // Look for conference
+ res = 0;
+ for ( conf = conflist ; conf != NULL ; conf = conf->next )
+ {
+ if ( strcmp(conference, conf->name) == 0 )
+ {
+ // Search member list for our member
+ // acquire conference mutex
+ ast_mutex_lock( &conf->lock );
+
+ for ( member = conf->memberlist ; member != NULL ; member = member->next )
+ {
+ if ( strcmp(channel, member->channel_name) == 0 && !member->mute_video )
+ {
+ do_video_switching(conf, member->id, 0);
+ conf->video_locked = 1;
+ res = 1;
+
+ manager_event(EVENT_FLAG_CALL, "ConferenceLock", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name);
+ break;
+ }
+ }
+
+ // Release conference mutex
+ ast_mutex_unlock( &conf->lock );
+ break;
+ }
+ }
+
+ // release mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return res;
+}
+
+int unlock_conference(const char *conference)
+{
+ struct ast_conference *conf;
+ int res;
+
+ if ( conference == NULL )
+ return -1;
+
+ // acquire conference list mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ // Look for conference
+ res = 0;
+ for ( conf = conflist ; conf != NULL ; conf = conf->next )
+ {
+ if ( strcmp(conference, conf->name) == 0 )
+ {
+ conf->video_locked = 0;
+ manager_event(EVENT_FLAG_CALL, "ConferenceUnlock", "ConferenceName: %s\r\n", conf->name);
+ do_video_switching(conf, conf->default_video_source_id, 0);
+ res = 1;
+
+ break;
+ }
+ }
+
+ // release conference list mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return res;
+}
+
+int set_default_id(const char *conference, int member_id)
+{
+ struct ast_conference *conf;
+ struct ast_conf_member *member;
+ int res;
+
+ if ( conference == NULL )
+ return -1 ;
+
+ // acquire conference list mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ // Look for conference
+ res = 0;
+ for ( conf = conflist ; conf != NULL ; conf = conf->next )
+ {
+ if ( strcmp(conference, conf->name) == 0 )
+ {
+ if ( member_id < 0 )
+ {
+ conf->default_video_source_id = -1;
+ manager_event(EVENT_FLAG_CALL, "ConferenceDefault", "ConferenceName: %s\r\nChannel: empty\r\n", conf->name);
+ res = 1;
+ break;
+ } else
+ {
+ // Search member list for our member
+ // acquire conference mutex
+ ast_mutex_lock( &conf->lock );
+
+ for ( member = conf->memberlist ; member != NULL ; member = member->next )
+ {
+ // We do not allow video muted members or members that do not support
+ // VAD switching to become defaults
+ if ( member->id == member_id &&
+ !member->mute_video &&
+ member->vad_switch
+ )
+ {
+ conf->default_video_source_id = member_id;
+ res = 1;
+
+ manager_event(EVENT_FLAG_CALL, "ConferenceDefault", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name);
+ break;
+ }
+ }
+
+ // Release conference mutex
+ ast_mutex_unlock( &conf->lock );
+ break;
+ }
+ }
+ }
+
+ // release conference list mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return res;
+
+}
+
+int set_default_channel(const char *conference, const char *channel)
+{
+ struct ast_conference *conf;
+ struct ast_conf_member *member;
+ int res;
+
+ if ( conference == NULL || channel == NULL )
+ return -1 ;
+
+ // acquire conference list mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ // Look for conference
+ res = 0;
+ for ( conf = conflist ; conf != NULL ; conf = conf->next )
+ {
+ if ( strcmp(conference, conf->name) == 0 )
+ {
+ // Search member list for our member
+ // acquire conference mutex
+ ast_mutex_lock( &conf->lock );
+
+ for ( member = conf->memberlist ; member != NULL ; member = member->next )
+ {
+ // We do not allow video muted members or members that do not support
+ // VAD switching to become defaults
+ if ( strcmp(channel, member->channel_name) == 0 &&
+ !member->mute_video &&
+ member->vad_switch
+ )
+ {
+ conf->default_video_source_id = member->id;
+ res = 1;
+
+ manager_event(EVENT_FLAG_CALL, "ConferenceDefault", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name);
+ break;
+ }
+ }
+
+ // Release conference mutex
+ ast_mutex_unlock( &conf->lock );
+ break;
+ }
+ }
+
+ // release conference list mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return res;
+}
+
+int video_mute_member(const char *conference, int member_id)
+{
+ struct ast_conference *conf;
+ struct ast_conf_member *member;
+ int res;
+
+ if ( conference == NULL || member_id < 0 )
+ return -1;
+
+ res = 0;
+
+ // acquire conference list mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ for ( conf = conflist ; conf != NULL ; conf = conf->next )
+ {
+ if ( strcmp(conference, conf->name) == 0 )
+ {
+ // acquire conference mutex
+ ast_mutex_lock( &conf->lock );
+
+ for ( member = conf->memberlist ; member != NULL ; member = member->next )
+ {
+ if ( member->id == member_id )
+ {
+ // acquire member mutex
+ ast_mutex_lock( &member->lock );
+
+ member->mute_video = 1;
+
+ // release member mutex
+ ast_mutex_unlock( &member->lock );
+
+ manager_event(EVENT_FLAG_CALL, "ConferenceVideoMute", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name);
+
+ if ( member->id == conf->current_video_source_id )
+ {
+ do_video_switching(conf, conf->default_video_source_id, 0);
+ }
+
+ res = 1;
+ break;
+ }
+ }
+
+ // release conference mutex
+ ast_mutex_unlock( &conf->lock );
+
+ break;
+ }
+ }
+
+ // release conference list mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return res;
+}
+
+int video_unmute_member(const char *conference, int member_id)
+{
+ struct ast_conference *conf;
+ struct ast_conf_member *member;
+ int res;
+
+ if ( conference == NULL || member_id < 0 )
+ return -1;
+
+ res = 0;
+
+ // acquire conference list mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ for ( conf = conflist ; conf != NULL ; conf = conf->next )
+ {
+ if ( strcmp(conference, conf->name) == 0 )
+ {
+ // acquire conference mutex
+ ast_mutex_lock( &conf->lock );
+
+ for ( member = conf->memberlist ; member != NULL ; member = member->next )
+ {
+ if ( member->id == member_id )
+ {
+ // acquire member mutex
+ ast_mutex_lock( &member->lock );
+
+ member->mute_video = 0;
+
+ // release member mutex
+ ast_mutex_unlock( &member->lock );
+
+ manager_event(EVENT_FLAG_CALL, "ConferenceVideoUnmute", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name);
+
+ res = 1;
+ break;
+ }
+ }
+
+ // release conference mutex
+ ast_mutex_unlock( &conf->lock );
+
+ break;
+ }
+ }
+
+ // release conference list mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return res;
+}
+
+int video_mute_channel(const char *conference, const char *channel)
+{
+ struct ast_conference *conf;
+ struct ast_conf_member *member;
+ int res;
+
+ if ( conference == NULL || channel == NULL )
+ return -1;
+
+ res = 0;
+
+ // acquire conference list mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ for ( conf = conflist ; conf != NULL ; conf = conf->next )
+ {
+ if ( strcmp(conference, conf->name) == 0 )
+ {
+ // acquire conference mutex
+ ast_mutex_lock( &conf->lock );
+
+ for ( member = conf->memberlist ; member != NULL ; member = member->next )
+ {
+ if ( strcmp(channel, member->channel_name) == 0 )
+ {
+ // acquire member mutex
+ ast_mutex_lock( &member->lock );
+
+ member->mute_video = 1;
+
+ // release member mutex
+ ast_mutex_unlock( &member->lock );
+
+ manager_event(EVENT_FLAG_CALL, "ConferenceVideoMute", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name);
+
+ if ( member->id == conf->current_video_source_id )
+ {
+ do_video_switching(conf, conf->default_video_source_id, 0);
+ }
+
+ res = 1;
+ break;
+ }
+ }
+
+ // release conference mutex
+ ast_mutex_unlock( &conf->lock );
+
+ break;
+ }
+ }
+
+ // release conference list mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return res;
+}
+
+int video_unmute_channel(const char *conference, const char *channel)
+{
+ struct ast_conference *conf;
+ struct ast_conf_member *member;
+ int res;
+
+ if ( conference == NULL || channel == NULL )
+ return -1;
+
+ res = 0;
+
+ // acquire conference list mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ for ( conf = conflist ; conf != NULL ; conf = conf->next )
+ {
+ if ( strcmp(conference, conf->name) == 0 )
+ {
+ // acquire conference mutex
+ ast_mutex_lock( &conf->lock );
+
+ for ( member = conf->memberlist ; member != NULL ; member = member->next )
+ {
+ if ( strcmp(channel, member->channel_name) == 0 )
+ {
+ // acquire member mutex
+ ast_mutex_lock( &member->lock );
+
+ member->mute_video = 0;
+
+ // release member mutex
+ ast_mutex_unlock( &member->lock );
+
+ manager_event(EVENT_FLAG_CALL, "ConferenceVideoUnmute", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name);
+
+ res = 1;
+ break;
+ }
+ }
+
+ // release conference mutex
+ ast_mutex_unlock( &conf->lock );
+
+ break;
+ }
+ }
+
+ // release conference list mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return res;
+}
+
+//
+// Text message functions
+//
+int send_text(const char *conference, int member_id, const char *text)
+{
+ struct ast_conference *conf;
+ struct ast_conf_member *member;
+ int res;
+
+ if ( conference == NULL || member_id < 0 || text == NULL )
+ return -1;
+
+ res = 0;
+
+ // acquire conference list mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ for ( conf = conflist ; conf != NULL ; conf = conf->next )
+ {
+ if ( strcmp(conference, conf->name) == 0 )
+ {
+ // acquire conference mutex
+ ast_mutex_lock( &conf->lock );
+
+ for ( member = conf->memberlist ; member != NULL ; member = member->next )
+ {
+ if ( member->id == member_id )
+ {
+ res = send_text_message_to_member(member, text) == 0;
+ break;
+ }
+ }
+
+ // release conference mutex
+ ast_mutex_unlock( &conf->lock );
+
+ break;
+ }
+ }
+
+ // release conference list mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+ return res;
+}
+
+int send_text_channel(const char *conference, const char *channel, const char *text)
+{
+ struct ast_conference *conf;
+ struct ast_conf_member *member;
+ int res;
+
+ if ( conference == NULL || channel == NULL || text == NULL )
+ return -1;
+
+ res = 0;
+
+ // acquire conference list mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ for ( conf = conflist ; conf != NULL ; conf = conf->next )
+ {
+ if ( strcmp(conference, conf->name) == 0 )
+ {
+ // acquire conference mutex
+ ast_mutex_lock( &conf->lock );
+
+ for ( member = conf->memberlist ; member != NULL ; member = member->next )
+ {
+ if ( strcmp(member->channel_name, channel) == 0 )
+ {
+ res = send_text_message_to_member(member, text) == 0;
+ break;
+ }
+ }
+
+ // release conference mutex
+ ast_mutex_unlock( &conf->lock );
+
+ break;
+ }
+ }
+
+ // release conference list mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return res;
+}
+
+int send_text_broadcast(const char *conference, const char *text)
+{
+ struct ast_conference *conf;
+ struct ast_conf_member *member;
+ int res;
+
+ if ( conference == NULL || text == NULL )
+ return -1;
+
+ res = 0;
+
+ // acquire conference list mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ for ( conf = conflist ; conf != NULL ; conf = conf->next )
+ {
+ if ( strcmp(conference, conf->name) == 0 )
+ {
+ // acquire conference mutex
+ ast_mutex_lock( &conf->lock );
+
+ for ( member = conf->memberlist ; member != NULL ; member = member->next )
+ {
+ if ( send_text_message_to_member(member, text) == 0 )
+ res = res || 1;
+ }
+
+ // release conference mutex
+ ast_mutex_unlock( &conf->lock );
+
+ break;
+ }
+ }
+
+ // release conference list mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return res;
+}
+
+// Creates a text frame and sends it to a given member
+// Returns 0 on success, -1 on failure
+int send_text_message_to_member(struct ast_conf_member *member, const char *text)
+{
+ struct ast_frame *f;
+
+ if ( member == NULL || text == NULL ) return -1;
+
+ if ( member->does_text )
+ {
+ f = create_text_frame(text, 1);
+ if ( f == NULL || queue_outgoing_text_frame(member, f) != 0) return -1;
+ ast_frfree(f);
+ }
+
+ return 0;
+}
+
+// Associates two members
+// Drives VAD-based video switching of dst_member from audio from src_member
+// This can be used when a member participates in a video conference but
+// talks using a telephone (simulcast) connection
+int drive(const char *conference, int src_member_id, int dst_member_id)
+{
+ int res;
+ struct ast_conference *conf;
+ struct ast_conf_member *member;
+ struct ast_conf_member *src;
+ struct ast_conf_member *dst;
+
+ if ( conference == NULL || src_member_id < 0 )
+ return -1;
+
+ res = 0;
+
+ // acquire conference list mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ for ( conf = conflist ; conf != NULL ; conf = conf->next )
+ {
+ if ( strcmp(conference, conf->name) == 0 )
+ {
+ // acquire conference mutex
+ ast_mutex_lock( &conf->lock );
+
+ src = NULL;
+ dst = NULL;
+ for ( member = conf->memberlist ; member != NULL ; member = member->next )
+ {
+ if ( member->id == src_member_id )
+ src = member;
+ if ( member->id == dst_member_id )
+ dst = member;
+ }
+ if ( src != NULL )
+ {
+ // acquire member mutex
+ ast_mutex_lock(&src->lock);
+
+ if ( dst != NULL )
+ {
+ src->driven_member = dst;
+ // Make sure the driven member's speaker count is correct
+ if ( src->speaking_state == 1 )
+ increment_speaker_count(src->driven_member, 1);
+ res = 1;
+ } else
+ {
+ if ( dst_member_id < 0 )
+ {
+ // Make sure the driven member's speaker count is correct
+ if ( src->speaking_state == 1 )
+ decrement_speaker_count(src->driven_member, 1);
+ src->driven_member = NULL;
+ res = 1;
+ }
+ }
+
+ // release member mutex
+ ast_mutex_unlock(&src->lock);
+ }
+
+ // release conference mutex
+ ast_mutex_unlock( &conf->lock );
+
+ break;
+ }
+ }
+
+ // release conference list mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return res;
+}
+
+// Associates two channels
+// Drives VAD-based video switching of dst_channel from audio from src_channel
+// This can be used when a member participates in a video conference but
+// talks using a telephone (simulcast) connection
+int drive_channel(const char *conference, const char *src_channel, const char *dst_channel)
+{
+ int res;
+ struct ast_conference *conf;
+ struct ast_conf_member *member;
+ struct ast_conf_member *src;
+ struct ast_conf_member *dst;
+
+ if ( conference == NULL || src_channel == NULL )
+ return -1;
+
+ res = 0;
+
+ // acquire conference list mutex
+ ast_mutex_lock( &conflist_lock ) ;
+
+ for ( conf = conflist ; conf != NULL ; conf = conf->next )
+ {
+ if ( strcmp(conference, conf->name) == 0 )
+ {
+ // acquire conference mutex
+ ast_mutex_lock( &conf->lock );
+
+ src = NULL;
+ dst = NULL;
+ for ( member = conf->memberlist ; member != NULL ; member = member->next )
+ {
+ if ( strcmp(src_channel, member->channel_name) == 0 )
+ src = member;
+ if ( dst_channel != NULL && strcmp(dst_channel, member->channel_name) == 0 )
+ dst = member;
+ }
+ if ( src != NULL )
+ {
+ // acquire member mutex
+ ast_mutex_lock(&src->lock);
+
+ if ( dst != NULL )
+ {
+ src->driven_member = dst;
+ // Make sure the driven member's speaker count is correct
+ if ( src->speaking_state == 1 )
+ increment_speaker_count(src->driven_member, 1);
+ res = 1;
+ } else
+ {
+ if ( dst_channel == NULL )
+ {
+ // Make sure the driven member's speaker count is correct
+ if ( src->speaking_state == 1 )
+ decrement_speaker_count(src->driven_member, 1);
+ src->driven_member = NULL;
+ res = 1;
+ }
+ }
+
+ // release member mutex
+ ast_mutex_unlock(&src->lock);
+ }
+
+ // release conference mutex
+ ast_mutex_unlock( &conf->lock );
+
+ break;
+ }
+ }
+
+ // release conference list mutex
+ ast_mutex_unlock( &conflist_lock ) ;
+
+ return res;
+}
+
+// Switches video source
+// Sends a manager event as well as
+// a text message notifying members of a video switch
+// The notification is sent to the current member and to the new member
+// The function locks the conference mutex as required
+void do_video_switching(struct ast_conference *conf, int new_id, int lock)
+{
+ struct ast_conf_member *member;
+ struct ast_conf_member *new_member = NULL;
+
+ if ( conf == NULL ) return;
+
+ if ( lock )
+ {
+ // acquire conference mutex
+ ast_mutex_lock( &conf->lock );
+ }
+
+ //fprintf(stderr, "Mihai: video switch from %d to %d\n", conf->current_video_source_id, new_id);
+
+ // No need to do anything if the current member is the same as the new member
+ if ( new_id != conf->current_video_source_id )
+ {
+ for ( member = conf->memberlist ; member != NULL ; member = member->next )
+ {
+ if ( member->id == conf->current_video_source_id )
+ {
+ send_text_message_to_member(member, AST_CONF_CONTROL_STOP_VIDEO);
+ }
+ if ( member->id == new_id )
+ {
+ send_text_message_to_member(member, AST_CONF_CONTROL_START_VIDEO);
+ new_member = member;
+ }
+ }
+
+ conf->current_video_source_id = new_id;
+
+ if ( new_member != NULL )
+ {
+ manager_event(EVENT_FLAG_CALL,
+ "ConferenceVideoSwitch",
+ "ConferenceName: %s\r\nChannel: %s\r\n",
+ conf->name,
+ new_member->channel_name);
+ } else
+ {
+ manager_event(EVENT_FLAG_CALL,
+ "ConferenceVideoSwitch",
+ "ConferenceName: %s\r\nChannel: empty\r\n",
+ conf->name);
+ }
+ }
+
+ if ( lock )
+ {
+ // release conference mutex
+ ast_mutex_unlock( &conf->lock );
+ }
+}
+
+int basic_play_sound(struct ast_conf_member *member, const char *file, int mute)
+{
+ struct ast_conf_soundq *newsound;
+ struct ast_conf_soundq **q;
+
+ newsound = calloc(1, sizeof(struct ast_conf_soundq));
+ newsound->stream = ast_openstream(member->chan, file, NULL);
+ if( !newsound->stream )
+ {
+ free(newsound);
+ ast_mutex_unlock(&member->lock);
+ return 0;
+ }
+ member->chan->stream = NULL;
+
+ newsound->muted = mute;
+ ast_copy_string(newsound->name, file, sizeof(newsound->name));
+
+ // append sound to the end of the list.
+ for ( q=&member->soundq; *q; q = &((*q)->next) ) ;
+ *q = newsound;
+
+ ast_mutex_unlock(&member->lock);
+
+ return 1 ;
+}
+
+int play_sound_channel(int fd, const char *channel, const char *file, int mute)
+{
+ struct ast_conf_member *member;
+
+ member = find_member(channel, 1);
+ if( !member )
+ {
+ ast_cli(fd, "Member %s not found\n", channel);
+ return 0;
+ }
+
+ if (! basic_play_sound(member, file, mute))
+ {
+ ast_cli(fd, "Sound %s not found\n", file);
+ return 0;
+ }
+
+ ast_cli(fd, "Playing sound %s to member %s %s\n",
+ file, channel, mute ? "with mute" : "");
+
+ return 1 ;
+}
+
+int stop_sound_channel(int fd, const char *channel)
+{
+ struct ast_conf_member *member;
+ struct ast_conf_soundq *sound;
+ struct ast_conf_soundq *next;
+
+ member = find_member(channel, 1);
+ if ( !member )
+ {
+ ast_cli(fd, "Member %s not found\n", channel);
+ return 0;
+ }
+
+ // clear all sounds
+ sound = member->soundq;
+ member->soundq = NULL;
+
+ while ( sound )
+ {
+ next = sound->next;
+ ast_closestream(sound->stream);
+ free(sound);
+ sound = next;
+ }
+
+ // reset write format, since we're done playing the sound
+ if ( ast_set_write_format( member->chan, member->write_format ) < 0 )
+ {
+ ast_log( LOG_ERROR, "unable to set write format to %d\n",
+ member->write_format ) ;
+ }
+
+ ast_mutex_unlock(&member->lock);
+
+ ast_cli( fd, "Stopped sounds to member %s\n", channel);
+ return 1;
+}
diff --git a/apps/conference/conference.h b/apps/conference/conference.h
new file mode 100644
index 0000000..0f7e248
--- /dev/null
+++ b/apps/conference/conference.h
@@ -0,0 +1,194 @@
+
+// $Id: conference.h 884 2007-06-27 14:56:21Z sbalea $
+
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
+ *
+ * Video Conferencing support added by
+ * Neil Stratford <neils@vipadia.com>
+ * Copyright (C) 2005, 2005 Vipadia Limited
+ *
+ * VAD driven video conferencing, text message support
+ * and miscellaneous enhancements added by
+ * Mihai Balea <mihai at hates dot ms>
+ *
+ * This program may be modified and distributed under the
+ * terms of the GNU General Public License. You should have received
+ * a copy of the GNU General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc.
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _APP_CONF_CONFERENCE_H
+#define _APP_CONF_CONFERENCE_H
+
+//
+// includes
+//
+
+#include "app_conference.h"
+#include "common.h"
+
+//
+// struct declarations
+//
+
+typedef struct ast_conference_stats
+{
+ // conference name ( copied for ease of use )
+ char name[128] ;
+
+ // type of connection
+ unsigned short phone ;
+ unsigned short iaxclient ;
+ unsigned short sip ;
+
+ // type of users
+ unsigned short moderators ;
+ unsigned short listeners ;
+
+ // accounting data
+ unsigned long frames_in ;
+ unsigned long frames_out ;
+ unsigned long frames_mixed ;
+
+ struct timeval time_entered ;
+
+} ast_conference_stats ;
+
+
+struct ast_conference
+{
+ // conference name
+ char name[128] ;
+
+ // single-linked list of members in conference
+ struct ast_conf_member* memberlist ;
+ int membercount ;
+ int id_count;
+
+ // id of the default video source
+ // If nobody is talking and video is unlocked, we use this source
+ int default_video_source_id;
+
+ // id of the current video source
+ // this changes according to VAD rules and lock requests
+ int current_video_source_id;
+
+ // timestamp of when the current source has started talking
+ // TODO: do we really need this?
+ //struct timeval current_video_source_timestamp;
+
+ // Video source locked flag, 1 -> locked, 0 -> unlocked
+ short video_locked;
+
+ // conference thread id
+ pthread_t conference_thread ;
+
+ // conference data mutex
+ ast_mutex_t lock ;
+
+ // pointer to next conference in single-linked list
+ struct ast_conference* next ;
+
+ // pointer to translation paths
+ struct ast_trans_pvt* from_slinear_paths[ AC_SUPPORTED_FORMATS ] ;
+
+ // conference stats
+ ast_conference_stats stats ;
+
+ // keep track of current delivery time
+ struct timeval delivery_time ;
+
+ // 1 => on, 0 => off
+ short debug_flag ;
+} ;
+
+
+#include "member.h"
+
+//
+// function declarations
+//
+
+struct ast_conference* start_conference( struct ast_conf_member* member ) ;
+
+void conference_exec( struct ast_conference* conf ) ;
+
+struct ast_conference* find_conf( const char* name ) ;
+struct ast_conference* create_conf( char* name, struct ast_conf_member* member ) ;
+void remove_conf( struct ast_conference* conf ) ;
+int end_conference( const char *name, int hangup ) ;
+
+// find a particular member, locking if requested.
+struct ast_conf_member *find_member ( const char *chan, int lock) ;
+
+int queue_frame_for_listener( struct ast_conference* conf, struct ast_conf_member* member, conf_frame* frame ) ;
+int queue_frame_for_speaker( struct ast_conference* conf, struct ast_conf_member* member, conf_frame* frame ) ;
+int queue_silent_frame( struct ast_conference* conf, struct ast_conf_member* member ) ;
+
+int get_new_id( struct ast_conference *conf );
+void add_member( struct ast_conf_member* member, struct ast_conference* conf ) ;
+int remove_member( struct ast_conf_member* member, struct ast_conference* conf ) ;
+int count_member( struct ast_conf_member* member, struct ast_conference* conf, short add_member ) ;
+
+void do_VAD_switching(struct ast_conference *conf);
+int send_text_message_to_member(struct ast_conf_member *member, const char *text);
+void do_video_switching(struct ast_conference *conf, int new_id, int lock);
+
+// called by app_confernce.c:load_module()
+void init_conference( void ) ;
+
+int get_conference_count( void ) ;
+
+int show_conference_list ( int fd, const char* name );
+int manager_conference_list( struct mansession *s, const struct message *m);
+int show_conference_stats ( int fd );
+int kick_member ( const char* confname, int user_id);
+int kick_channel ( const char *confname, const char *channel);
+int kick_all ( void );
+int mute_member ( const char* confname, int user_id);
+int unmute_member ( const char* confname, int user_id);
+int mute_channel ( const char* confname, const char* user_chan);
+int unmute_channel ( const char* confname, const char* user_chan);
+int viewstream_switch ( const char* confname, int user_id, int stream_id);
+int viewchannel_switch ( const char* confname, const char* user_chan, const char* stream_chan);
+
+int get_conference_stats( ast_conference_stats* stats, int requested ) ;
+int get_conference_stats_by_name( ast_conference_stats* stats, const char* name ) ;
+
+int lock_conference(const char *conference, int member_id);
+int lock_conference_channel(const char *conference, const char *channel);
+int unlock_conference(const char *conference);
+
+int set_default_id(const char *conference, int member_id);
+int set_default_channel(const char *conference, const char *channel);
+
+int video_mute_member(const char *conference, int member_id);
+int video_unmute_member(const char *conference, int member_id);
+int video_mute_channel(const char *conference, const char *channel);
+int video_unmute_channel(const char *conference, const char *channel);
+
+int send_text(const char *conference, int member, const char *text);
+int send_text_channel(const char *conference, const char *channel, const char *text);
+int send_text_broadcast(const char *conference, const char *text);
+
+int drive(const char *conference, int src_member_id, int dst_member_id);
+int drive_channel(const char *conference, const char *src_channel, const char *dst_channel);
+
+int basic_play_sound(struct ast_conf_member *member, const char *file, int mute);
+int play_sound_channel(int fd, const char *channel, const char *file, int mute);
+int stop_sound_channel(int fd, const char *channel);
+
+int set_conference_debugging( const char* name, int state ) ;
+
+#endif
diff --git a/apps/conference/frame.c b/apps/conference/frame.c
new file mode 100644
index 0000000..24e04bd
--- /dev/null
+++ b/apps/conference/frame.c
@@ -0,0 +1,679 @@
+
+// $Id: frame.c 751 2006-12-11 22:08:45Z sbalea $
+
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
+ *
+ * Video Conferencing support added by
+ * Neil Stratford <neils@vipadia.com>
+ * Copyright (C) 2005, 2005 Vipadia Limited
+ *
+ * VAD driven video conferencing, text message support
+ * and miscellaneous enhancements added by
+ * Mihai Balea <mihai at hates dot ms>
+ *
+ * This program may be modified and distributed under the
+ * terms of the GNU General Public License. You should have received
+ * a copy of the GNU General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc.
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "asterisk/autoconfig.h"
+#include "frame.h"
+
+conf_frame* mix_frames( conf_frame* frames_in, int speaker_count, int listener_count )
+{
+ if ( frames_in == NULL )
+ return NULL ;
+
+ conf_frame* frames_out = NULL ;
+
+ if ( speaker_count > 1 )
+ {
+ if ( speaker_count == 2 && listener_count == 0 )
+ {
+ // optimize here also?
+ frames_out = mix_multiple_speakers( frames_in, speaker_count, listener_count ) ;
+ }
+ else
+ {
+ // mix spoken frames for sending
+ // ( note: this call also releases us from free'ing spoken_frames )
+ frames_out = mix_multiple_speakers( frames_in, speaker_count, listener_count ) ;
+ }
+ }
+ else if ( speaker_count == 1 )
+ {
+ // pass-through frames
+ frames_out = mix_single_speaker( frames_in ) ;
+ //printf("mix single speaker\n");
+ }
+ else
+ {
+ // no frames to send, leave frames_out null
+ }
+
+ return frames_out ;
+}
+
+conf_frame* mix_single_speaker( conf_frame* frames_in )
+{
+#ifdef APP_CONFERENCE_DEBUG
+ // ast_log( AST_CONF_DEBUG, "returning single spoken frame\n" ) ;
+
+ //
+ // check input
+ //
+
+ if ( frames_in == NULL )
+ {
+ ast_log( AST_CONF_DEBUG, "unable to mix single spoken frame with null frame\n" ) ;
+ return NULL ;
+ }
+
+ if ( frames_in->fr == NULL )
+ {
+ ast_log( AST_CONF_DEBUG, "unable to mix single spoken frame with null data\n" ) ;
+ return NULL ;
+ }
+
+ if ( frames_in->member == NULL )
+ {
+ ast_log( AST_CONF_DEBUG, "unable to mix single spoken frame with null member\n" ) ;
+ return NULL ;
+ }
+#endif // APP_CONFERENCE_DEBUG
+
+ //
+ // 'mix' the frame
+ //
+
+ // copy orignal frame to converted array so listeners don't need to re-encode it
+ frames_in->converted[ frames_in->member->read_format_index ] = ast_frdup( frames_in->fr ) ;
+
+ // convert frame to slinear, if we have a path
+ frames_in->fr = convert_frame_to_slinear(
+ frames_in->member->to_slinear,
+ frames_in->fr
+ ) ;
+
+ // set the frame's member to null ( i.e. all listeners )
+ frames_in->member = NULL ;
+
+ return frames_in ;
+}
+
+// {
+ //
+ // a little optimization for testing only:
+ // when two speakers ( of the same type ) and no listeners
+ // are in a conference, we just swamp the frame's member pointers
+ //
+/*
+ if (
+ listeners == 0
+ && speakers == 2
+ && cf_spokenFrames->member->read_format == cf_spokenFrames->next->member->write_format
+ && cf_spokenFrames->member->write_format == cf_spokenFrames->next->member->read_format
+ )
+ {
+ struct ast_conf_member* m = NULL ;
+ m = cf_spokenFrames->member ;
+ cf_spokenFrames->member = cf_spokenFrames->next->member ;
+ cf_spokenFrames->next->member = m ;
+ return cf_spokenFrames ;
+ }
+*/
+// }
+
+void set_conf_frame_delivery( conf_frame* frame, struct timeval time )
+{
+ for ( ; frame != NULL ; frame = frame->next )
+ {
+ if ( frame->fr != NULL )
+ {
+ // copy passed timeval to frame's delivery timeval
+ frame->fr->delivery = time ;
+ }
+ }
+
+ return ;
+}
+
+conf_frame* mix_multiple_speakers(
+ conf_frame* frames_in,
+ int speakers,
+ int listeners
+)
+{
+#ifdef APP_CONFERENCE_DEBUG
+ //
+ // check input
+ //
+
+ // no frames to mix
+ if ( ( frames_in == NULL ) || ( frames_in->fr == NULL ) )
+ {
+ ast_log( AST_CONF_DEBUG, "passed spoken frame list was NULL\n" ) ;
+ return NULL ;
+ }
+
+ // if less than two speakers, then no frames to mix
+ if ( speakers < 2 )
+ {
+ ast_log( AST_CONF_DEBUG, "mix_multiple_speakers() called with less than two speakers\n" ) ;
+ return NULL ;
+ }
+#endif // APP_CONFERENCE_DEBUG
+
+ //
+ // at this point we know that there is more than one frame,
+ // and that the frames need to be converted to pcm to be mixed
+ //
+ // now, if there are only two frames and two members,
+ // we can swap them. ( but we'll get to that later. )
+ //
+
+ //
+ // loop through the spoken frames, making a list of spoken members,
+ // and converting gsm frames to slinear frames so we can mix them.
+ //
+
+ // pointer to the spoken frames list
+ conf_frame* cf_spoken = frames_in ;
+
+ // pointer to the new list of mixed frames
+ conf_frame* cf_sendFrames = NULL ;
+
+ while ( cf_spoken != NULL )
+ {
+ //
+ // while we're looping through the spoken frames, we'll
+ // convert the frame to a format suitable for mixing
+ //
+ // if the frame fails to convert, drop it and treat
+ // the speaking member like a listener by not adding
+ // them to the cf_sendFrames list
+ //
+
+ if ( cf_spoken->member == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to determine frame member\n" ) ;
+ }
+ else
+ {
+ // ast_log( AST_CONF_DEBUG, "converting frame to slinear, channel => %s\n", cf_spoken->member->channel_name ) ;
+ cf_spoken->fr = convert_frame_to_slinear(
+ cf_spoken->member->to_slinear,
+ cf_spoken->fr
+ ) ;
+
+ if ( cf_spoken->fr == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to convert frame to slinear\n" ) ;
+ }
+ else
+ {
+ // create new conf frame with last frame as 'next'
+ cf_sendFrames = create_conf_frame( cf_spoken->member, cf_sendFrames, NULL ) ;
+ }
+ }
+
+ // point to the next spoken frame
+ cf_spoken = cf_spoken->next ;
+ }
+
+ // if necessary, add a frame with a null member pointer.
+ // this frame will hold the audio mixed for all listeners
+ if ( listeners > 0 )
+ {
+ cf_sendFrames = create_conf_frame( NULL, cf_sendFrames, NULL ) ;
+ }
+
+ //
+ // mix the audio
+ //
+
+ // convenience pointer that skips over the friendly offset
+ char* cp_listenerData ;
+
+ // pointer to the send frames list
+ conf_frame* cf_send = NULL ;
+
+ for ( cf_send = cf_sendFrames ; cf_send != NULL ; cf_send = cf_send->next )
+ {
+ // allocate a mix buffer which fill large enough memory to
+ // hold a frame, and reset it's memory so we don't get noise
+ char* cp_listenerBuffer = malloc( AST_CONF_BUFFER_SIZE ) ;
+ memset( cp_listenerBuffer, 0x0, AST_CONF_BUFFER_SIZE ) ;
+
+ // point past the friendly offset right to the data
+ cp_listenerData = cp_listenerBuffer + AST_FRIENDLY_OFFSET ;
+
+ // reset the spoken list pointer
+ cf_spoken = frames_in ;
+
+ // really mix the audio
+ for ( ; cf_spoken != NULL ; cf_spoken = cf_spoken->next )
+ {
+ //
+ // if the members are equal, and they
+ // are not null, do not mix them.
+ //
+ if (
+ ( cf_send->member == cf_spoken->member )
+ && ( cf_send->member != NULL )
+ )
+ {
+ // don't mix this frame
+ }
+ else if ( cf_spoken->fr == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to mix conf_frame with null ast_frame\n" ) ;
+ }
+ else
+ {
+ // mix the new frame in with the existing buffer
+ mix_slinear_frames( cp_listenerData, (char*)( cf_spoken->fr->data ), AST_CONF_BLOCK_SAMPLES);//XXX NAS cf_spoken->fr->samples ) ;
+ }
+ }
+
+ // copy a pointer to the frame data to the conf_frame
+ cf_send->mixed_buffer = cp_listenerData ;
+ }
+
+ //
+ // copy the mixed buffer to a new frame
+ //
+
+ // reset the send list pointer
+ cf_send = cf_sendFrames ;
+
+ while ( cf_send != NULL )
+ {
+ cf_send->fr = create_slinear_frame( cf_send->mixed_buffer ) ;
+ cf_send = cf_send->next ;
+ }
+
+ //
+ // clean up the spoken frames we were passed
+ // ( caller will only be responsible for free'ing returns frames )
+ //
+
+ // reset the spoken list pointer
+ cf_spoken = frames_in ;
+
+ while ( cf_spoken != NULL )
+ {
+ // delete the frame
+ cf_spoken = delete_conf_frame( cf_spoken ) ;
+ }
+
+ // return the list of frames for sending
+ return cf_sendFrames ;
+}
+
+
+struct ast_frame* convert_frame_to_slinear( struct ast_trans_pvt* trans, struct ast_frame* fr )
+{
+ // check for null frame
+ if ( fr == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to translate null frame to slinear\n" ) ;
+ return NULL ;
+ }
+
+ // we don't need to duplicate this frame since
+ // the normal translation would free it anyway, so
+ // we'll just pretend we free'd and malloc'd a new one.
+ if ( fr->subclass == AST_FORMAT_SLINEAR )
+ return fr ;
+
+ // check for null translator ( after we've checked that we need to translate )
+ if ( trans == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to translate frame with null translation path\n" ) ;
+ return fr ;
+ }
+
+ // return the converted frame
+ return convert_frame( trans, fr ) ;
+}
+
+struct ast_frame* convert_frame_from_slinear( struct ast_trans_pvt* trans, struct ast_frame* fr )
+{
+ // check for null translator ( after we've checked that we need to translate )
+ if ( trans == NULL )
+ {
+ //ast_log( LOG_ERROR, "unable to translate frame with null translation path\n" ) ;
+ return fr ;
+ }
+
+ // check for null frame
+ if ( fr == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to translate null slinear frame\n" ) ;
+ return NULL ;
+ }
+
+ // if the frame is not slinear, return an error
+ if ( fr->subclass != AST_FORMAT_SLINEAR )
+ {
+ ast_log( LOG_ERROR, "unable to translate non-slinear frame\n" ) ;
+ return NULL ;
+ }
+
+ // return the converted frame
+ return convert_frame( trans, fr ) ;
+}
+
+struct ast_frame* convert_frame( struct ast_trans_pvt* trans, struct ast_frame* fr )
+{
+ if ( trans == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to convert frame with null translator\n" ) ;
+ return NULL ;
+ }
+
+ if ( fr == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to convert null frame\n" ) ;
+ return NULL ;
+ }
+
+ // convert the frame
+ struct ast_frame* translated_frame = ast_translate( trans, fr, 1 ) ;
+
+ // check for errors
+ if ( translated_frame == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to translate frame\n" ) ;
+ return NULL ;
+ }
+
+ // return the translated frame
+ return translated_frame ;
+}
+
+conf_frame* delete_conf_frame( conf_frame* cf )
+{
+ int c;
+ // check for null frames
+ if ( cf == NULL )
+ {
+ ast_log( AST_CONF_DEBUG, "unable to delete null conf frame\n" ) ;
+ return NULL ;
+ }
+
+ // check for frame marked as static
+ if ( cf->static_frame == 1 )
+ return NULL ;
+
+ if ( cf->fr != NULL )
+ {
+ ast_frfree( cf->fr ) ;
+ cf->fr = NULL ;
+ }
+
+ // make sure converted frames are set to null
+ for ( c = 0 ; c < AC_SUPPORTED_FORMATS ; ++c )
+ {
+ if ( cf->converted[ c ] != NULL )
+ {
+ ast_frfree( cf->converted[ c ] ) ;
+ cf->converted[ c ] = NULL ;
+ }
+ }
+
+ // get a pointer to the next frame
+ // in the list so we can return it
+ conf_frame* nf = cf->next ;
+
+ free( cf ) ;
+ cf = NULL ;
+
+ return nf ;
+}
+
+conf_frame* create_conf_frame( struct ast_conf_member* member, conf_frame* next, const struct ast_frame* fr )
+{
+ // pointer to list of mixed frames
+ conf_frame* cf = malloc( sizeof( struct conf_frame ) ) ;
+
+ if ( cf == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to allocate memory for conf frame\n" ) ;
+ return NULL ;
+ }
+
+ //
+ // init with some defaults
+ //
+
+ // make sure converted frames are set to null
+// for ( int c = 0 ; c < AC_SUPPORTED_FORMATS ; ++c )
+// {
+// cf->converted[ c ] = NULL ;
+// }
+
+ memset( (struct ast_frame*)( cf->converted ), 0x0, ( sizeof( struct ast_frame* ) * AC_SUPPORTED_FORMATS ) ) ;
+
+ cf->member = member ;
+ // cf->priority = 0 ;
+
+ cf->prev = NULL ;
+ cf->next = next ;
+
+ cf->static_frame = 0 ;
+
+ // establish relationship to 'next'
+ if ( next != NULL ) next->prev = cf ;
+
+ // this holds the ast_frame pointer
+ cf->fr = ( fr == NULL ) ? NULL : ast_frdup( ( struct ast_frame* )( fr ) ) ;
+
+ // this holds the temporu mix buffer
+ cf->mixed_buffer = NULL ;
+
+ return cf ;
+}
+
+conf_frame* copy_conf_frame( conf_frame* src )
+{
+ //
+ // check inputs
+ //
+
+ if ( src == NULL )
+ {
+ ast_log( AST_CONF_DEBUG, "unable to copy null conf frame\n" ) ;
+ return NULL ;
+ }
+
+ //
+ // copy the frame
+ //
+
+ struct conf_frame *cfr = NULL ;
+
+ // create a new conf frame
+ cfr = create_conf_frame( src->member, NULL, src->fr ) ;
+
+ if ( cfr == NULL )
+ {
+ ast_log( AST_CONF_DEBUG, "unable to create new conf frame for copy\n" ) ;
+ return NULL ;
+ }
+
+ return cfr ;
+}
+
+//
+// Create a TEXT frame based on a given string
+//
+struct ast_frame* create_text_frame(const char *text, int copy)
+{
+ struct ast_frame *f;
+ char *t;
+
+ f = calloc(1, sizeof(struct ast_frame));
+ if ( f == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to allocate memory for text frame\n" ) ;
+ return NULL ;
+ }
+ if ( copy )
+ {
+ t = calloc(strlen(text) + 1, 1);
+ if ( t == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to allocate memory for text data\n" ) ;
+ free(f);
+ return NULL ;
+ }
+ strncpy(t, text, strlen(text));
+ } else
+ {
+ t = (char *)text;
+ }
+
+ f->frametype = AST_FRAME_TEXT;
+ f->offset = 0;
+ f->mallocd = AST_MALLOCD_HDR;
+ if ( copy ) f->mallocd |= AST_MALLOCD_DATA;
+ f->datalen = strlen(t) + 1;
+ f->data = t;
+ f->src = NULL;
+
+ return f;
+}
+
+//
+// slinear frame functions
+//
+
+struct ast_frame* create_slinear_frame( char* data )
+{
+ struct ast_frame* f ;
+
+ f = calloc( 1, sizeof( struct ast_frame ) ) ;
+ if ( f == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to allocate memory for slinear frame\n" ) ;
+ return NULL ;
+ }
+
+ f->frametype = AST_FRAME_VOICE ;
+ f->subclass = AST_FORMAT_SLINEAR ;
+ f->samples = AST_CONF_BLOCK_SAMPLES ;
+ f->offset = AST_FRIENDLY_OFFSET ;
+ f->mallocd = AST_MALLOCD_HDR | AST_MALLOCD_DATA ;
+
+ f->datalen = AST_CONF_FRAME_DATA_SIZE ;
+ f->data = data ;
+
+ f->src = NULL ;
+
+ return f ;
+}
+
+void mix_slinear_frames( char *dst, const char *src, int samples )
+{
+ if ( dst == NULL ) return ;
+ if ( src == NULL ) return ;
+
+ int i, val ;
+
+ for ( i = 0 ; i < samples ; ++i )
+ {
+ val = ( (short*)dst )[i] + ( (short*)src )[i] ;
+
+ if ( val > 0x7fff )
+ {
+ ( (short*)dst )[i] = 0x7fff - 1 ;
+ continue ;
+ }
+ else if ( val < -0x7fff )
+ {
+ ( (short*)dst )[i] = -0x7fff + 1 ;
+ continue ;
+ }
+ else
+ {
+ ( (short*)dst )[i] = val ;
+ continue ;
+ }
+ }
+
+ return ;
+}
+
+//
+// silent frame functions
+//
+
+conf_frame* get_silent_frame( void )
+{
+ static conf_frame* static_silent_frame = NULL ;
+
+ // we'll let this leak until the application terminates
+ if ( static_silent_frame == NULL )
+ {
+ // ast_log( AST_CONF_DEBUG, "creating cached silent frame\n" ) ;
+ struct ast_frame* fr = get_silent_slinear_frame() ;
+
+ static_silent_frame = create_conf_frame( NULL, NULL, fr ) ;
+
+ if ( static_silent_frame == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to create cached silent frame\n" ) ;
+ return NULL ;
+ }
+
+ // init the 'converted' slinear silent frame
+ static_silent_frame->converted[ AC_SLINEAR_INDEX ] = get_silent_slinear_frame() ;
+
+ // mark frame as static so it's not deleted
+ static_silent_frame->static_frame = 1 ;
+ }
+
+ return static_silent_frame ;
+}
+
+struct ast_frame* get_silent_slinear_frame( void )
+{
+ static struct ast_frame* f = NULL ;
+
+ // we'll let this leak until the application terminates
+ if ( f == NULL )
+ {
+ char* data = malloc( AST_CONF_BUFFER_SIZE ) ;
+ memset( data, 0x0, AST_CONF_BUFFER_SIZE ) ;
+ f = create_slinear_frame( data ) ;
+ }
+
+ return f;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/conference/frame.h b/apps/conference/frame.h
new file mode 100644
index 0000000..2b77805
--- /dev/null
+++ b/apps/conference/frame.h
@@ -0,0 +1,75 @@
+
+// $Id: frame.h 746 2006-12-11 20:12:12Z sbalea $
+
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
+ *
+ * Video Conferencing support added by
+ * Neil Stratford <neils@vipadia.com>
+ * Copyright (C) 2005, 2005 Vipadia Limited
+ *
+ * VAD driven video conferencing, text message support
+ * and miscellaneous enhancements added by
+ * Mihai Balea <mihai at hates dot ms>
+ *
+ * This program may be modified and distributed under the
+ * terms of the GNU General Public License. You should have received
+ * a copy of the GNU General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc.
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _APP_CONF_FRAME_H
+#define _APP_CONF_FRAME_H
+
+//
+// includes
+//
+
+#include "app_conference.h"
+#include "common.h"
+
+//
+// function declarations
+//
+
+// mixing
+conf_frame* mix_frames( conf_frame* frames_in, int speaker_count, int listener_count ) ;
+
+conf_frame* mix_multiple_speakers( conf_frame* frames_in, int speakers, int listeners ) ;
+conf_frame* mix_single_speaker( conf_frame* frames_in ) ;
+
+// frame creation and deletion
+conf_frame* create_conf_frame( struct ast_conf_member* member, conf_frame* next, const struct ast_frame* fr ) ;
+conf_frame* delete_conf_frame( conf_frame* cf ) ;
+conf_frame* copy_conf_frame( conf_frame* src ) ;
+
+// convert frame functions
+struct ast_frame* convert_frame_to_slinear( struct ast_trans_pvt* trans, struct ast_frame* fr ) ;
+struct ast_frame* convert_frame_from_slinear( struct ast_trans_pvt* trans, struct ast_frame* fr ) ;
+struct ast_frame* convert_frame( struct ast_trans_pvt* trans, struct ast_frame* fr ) ;
+
+// text frame function(s)
+struct ast_frame* create_text_frame(const char *text, int copy);
+
+// slinear frame functions
+struct ast_frame* create_slinear_frame( char* data ) ;
+void mix_slinear_frames( char* dst, const char* src, int samples ) ;
+
+// silent frame functions
+conf_frame* get_silent_frame( void ) ;
+struct ast_frame* get_silent_slinear_frame( void ) ;
+
+// set delivery timestamp for frames
+void set_conf_frame_delivery( conf_frame* frame, struct timeval time ) ;
+
+#endif
diff --git a/apps/conference/member.c b/apps/conference/member.c
new file mode 100644
index 0000000..d7f98af
--- /dev/null
+++ b/apps/conference/member.c
@@ -0,0 +1,3332 @@
+
+// $Id: member.c 885 2007-06-27 15:41:18Z sbalea $
+
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
+ *
+ * Video Conferencing support added by
+ * Neil Stratford <neils@vipadia.com>
+ * Copyright (C) 2005, 2005 Vipadia Limited
+ *
+ * VAD driven video conferencing, text message support
+ * and miscellaneous enhancements added by
+ * Mihai Balea <mihai at hates dot ms>
+ *
+ * This program may be modified and distributed under the
+ * terms of the GNU General Public License. You should have received
+ * a copy of the GNU General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc.
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include "asterisk/channel.h"
+#include "asterisk/app.h"
+#include "asterisk/autoconfig.h"
+#include "member.h"
+
+
+// process an incoming frame. Returns 0 normally, 1 if hangup was received.
+static int process_incoming(struct ast_conf_member *member, struct ast_conference *conf, struct ast_frame *f)
+{
+ int silent_frame = 0;
+ struct ast_conf_member *src_member ;
+
+ // In Asterisk 1.4 AST_FRAME_DTMF is equivalent to AST_FRAME_DTMF_END
+ if (f->frametype == AST_FRAME_DTMF)
+ {
+ if (member->dtmf_switch)
+ {
+ ast_mutex_lock( &member->lock ) ;
+ switch (f->subclass) {
+ case '0' :member->req_id=0;
+ break;
+ case '1' :member->req_id=1;
+ break;
+ case '2' :member->req_id=2;
+ break;
+ case '3' :member->req_id=3;
+ break;
+ case '4' :member->req_id=4;
+ break;
+ case '5' :member->req_id=5;
+ break;
+ case '6' :member->req_id=6;
+ break;
+ case '7' :member->req_id=7;
+ break;
+ case '8' :member->req_id=8;
+ break;
+ case '9' :member->req_id=9;
+ break;
+ case '*' :
+ if (member->mute_video == 0 && member->mute_audio == 0)
+ {
+ member->mute_video = 1;
+ member->mute_audio = 1;
+ }
+ else if (member->mute_video == 1 && member->mute_audio == 1)
+ {
+ member->mute_video = 0;
+ member->mute_audio = 0;
+ }
+ break;
+ }
+ member->conference = 1; // switch me
+ ast_mutex_unlock( &member->lock ) ;
+ }
+ if (member->dtmf_relay)
+ {
+ // output to manager...
+ manager_event(
+ EVENT_FLAG_CALL,
+ "ConferenceDTMF",
+ "ConferenceName: %s\r\n"
+ "Channel: %s\r\n"
+ "CallerID: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "Key: %c\r\n",
+ conf->name,
+ member->channel_name,
+ member->chan->cid.cid_num ? member->chan->cid.cid_num : "unknown",
+ member->chan->cid.cid_name ? member->chan->cid.cid_name : "unknown",
+ f->subclass
+ ) ;
+
+ }
+ if (!member->dtmf_switch && !member->dtmf_relay)
+ {
+ // relay this to the listening channels
+ queue_incoming_dtmf_frame( member, f );
+ }
+ } else if (f->frametype == AST_FRAME_DTMF_BEGIN)
+ {
+ if (!member->dtmf_switch && !member->dtmf_relay)
+ {
+ // relay this to the listening channels
+ queue_incoming_dtmf_frame( member, f );
+ }
+ }
+
+ ast_mutex_lock( &member->lock ) ;
+ // Handle a local or remote conference
+ if (member->conference)
+ {
+ int req_id = member->req_id;
+ ast_mutex_unlock( &member->lock );
+ // this will return NULL or a locked member
+ src_member = check_active_video(req_id,conf);
+ // Stream a picture to the recipient if no active video
+ if (!src_member)
+ {
+ // Mihai: we don't want to send video here, we cannot negotiate codec
+ // and we don't know what codec the conference is using
+ //if (member->norecv_video == 0)
+ //{
+ // if(!ast_streamfile(member->chan,"novideo",member->chan->language))
+ // {
+ // ast_waitstream(member->chan,"");
+ // }
+ //}
+ }
+ else
+ {
+ // Send a FIR to the new sender
+ ast_indicate(src_member->chan,AST_CONTROL_VIDUPDATE);
+ // we will have locked in check_active_video()
+ ast_mutex_unlock( &src_member->lock);
+ }
+ ast_mutex_lock( &member->lock );
+ member->conference = 0;
+ }
+ ast_mutex_unlock( &member->lock );
+
+
+ if ((f->frametype == AST_FRAME_VOICE && member->mute_audio == 1) || (f->frametype == AST_FRAME_VIDEO && member->mute_video == 1))
+ {
+ // this is a listen-only user, ignore the frame
+ //ast_log( AST_CONF_DEBUG, "Listen only user frame");
+ ast_frfree( f ) ;
+ f = NULL ;
+ }
+ else if ( f->frametype == AST_FRAME_VOICE )
+ { //ast_log( AST_CONF_DEBUG, "Got voice frame");
+ // reset silence detection flag
+ silent_frame = 0 ;
+
+ // accounting: count the incoming frame
+ member->frames_in++ ;
+
+#if ( SILDET == 2 )
+ //
+ // make sure we have a valid dsp and frame type
+ //
+ if (
+ member->dsp != NULL
+ && f->subclass == AST_FORMAT_SLINEAR
+ && f->datalen == AST_CONF_FRAME_DATA_SIZE
+ )
+ {
+ // send the frame to the preprocessor
+ int spx_ret;
+ spx_ret = speex_preprocess( member->dsp, f->data, NULL );
+#ifdef DEBUG_USE_TIMELOG
+ TIMELOG(spx_ret, 3, "speex_preprocess");
+#endif
+ if ( spx_ret == 0 )
+ {
+ //
+ // we ignore the preprocessor's outcome if we've seen voice frames
+ // in within the last AST_CONF_SKIP_SPEEX_PREPROCESS frames
+ //
+ if ( member->ignore_speex_count > 0 )
+ {
+ // ast_log( AST_CONF_DEBUG, "ignore_speex_count => %d\n", ignore_speex_count ) ;
+
+ // skip speex_preprocess(), and decrement counter
+ --member->ignore_speex_count ;
+ }
+ else
+ {
+ // set silent_frame flag
+ silent_frame = 1 ;
+ }
+ }
+ else
+ {
+ // voice detected, reset skip count
+ member->ignore_speex_count = AST_CONF_SKIP_SPEEX_PREPROCESS ;
+ }
+ }
+#endif
+ if ( !silent_frame )
+ queue_incoming_frame( member, f );
+
+ // free the original frame
+ ast_frfree( f ) ;
+ f = NULL ;
+
+ }
+ else if (f->frametype == AST_FRAME_VIDEO)
+ {
+ queue_incoming_video_frame( member, f );
+
+ // free the original frame
+ ast_frfree( f ) ;
+ f = NULL ;
+
+ }
+ else if (
+ f->frametype == AST_FRAME_CONTROL
+ && f->subclass == AST_CONTROL_HANGUP
+ )
+ {
+ // hangup received
+
+ // free the frame
+ ast_frfree( f ) ;
+ f = NULL ;
+
+ // break out of the while ( 42 == 42 )
+ return 1;
+ }
+ else if (
+ f->frametype == AST_FRAME_CONTROL
+ && f->subclass == AST_CONTROL_VIDUPDATE
+ )
+ {
+ // say we have switched to cause a FIR to
+ // be sent to the sender
+ ast_mutex_lock( &member->lock ) ;
+ member->conference = 1;
+ ast_mutex_unlock( &member->lock ) ;
+
+ // free the original frame
+ ast_frfree( f ) ;
+ f = NULL ;
+ }
+ else if ( f->frametype == AST_FRAME_TEXT && member->does_text )
+ {
+ if ( strncmp(f->data, AST_CONF_CONTROL_CAMERA_DISABLED, strlen(AST_CONF_CONTROL_CAMERA_DISABLED)) == 0 )
+ {
+ ast_mutex_lock(&member->lock);
+ manager_event(EVENT_FLAG_CALL,
+ "ConferenceCameraDisabled",
+ "ConferenceName: %s\r\nChannel: %s\r\n",
+ conf->name,
+ member->channel_name);
+ member->no_camera = 1;
+ ast_mutex_unlock(&member->lock);
+ } else if ( strncmp(f->data, AST_CONF_CONTROL_CAMERA_ENABLED, strlen(AST_CONF_CONTROL_CAMERA_ENABLED)) == 0 )
+ {
+ ast_mutex_lock(&member->lock);
+ manager_event(EVENT_FLAG_CALL,
+ "ConferenceCameraEnabled",
+ "ConferenceName: %s\r\nChannel: %s\r\n",
+ conf->name,
+ member->channel_name);
+ member->no_camera = 0;
+ ast_mutex_unlock(&member->lock);
+ } else if ( strncmp(f->data, AST_CONF_CONTROL_STOP_VIDEO_TRANSMIT, strlen(AST_CONF_CONTROL_STOP_VIDEO_TRANSMIT)) == 0 )
+ {
+ ast_mutex_lock(&member->lock);
+ manager_event(EVENT_FLAG_CALL,
+ "ConferenceStopVideoTransmit",
+ "ConferenceName: %s\r\nChannel: %s\r\n",
+ conf->name,
+ member->channel_name);
+ member->norecv_video = 1;
+ ast_mutex_unlock(&member->lock);
+ } else if ( strncmp(f->data, AST_CONF_CONTROL_START_VIDEO_TRANSMIT, strlen(AST_CONF_CONTROL_START_VIDEO_TRANSMIT)) == 0 )
+ {
+ ast_mutex_lock(&member->lock);
+ manager_event(EVENT_FLAG_CALL,
+ "ConferenceStartVideoTransmit",
+ "ConferenceName: %s\r\nChannel: %s\r\n",
+ conf->name,
+ member->channel_name);
+ member->norecv_video = 0;
+ ast_mutex_unlock(&member->lock);
+ }
+ ast_frfree(f);
+ f = NULL;
+ } else
+ {
+ // undesirables
+ ast_frfree( f ) ;
+ f = NULL ;
+ }
+
+ return 0;
+}
+
+// get the next frame from the soundq; must be called with member locked.
+static struct ast_frame *get_next_soundframe(struct ast_conf_member *member, struct ast_frame
+ *exampleframe) {
+ struct ast_frame *f;
+
+again:
+ f=ast_readframe(member->soundq->stream);
+
+ if(!f) { // we're done with this sound; remove it from the queue, and try again
+ struct ast_conf_soundq *toboot = member->soundq;
+
+ ast_closestream(toboot->stream);
+ member->soundq = toboot->next;
+
+ //ast_log( LOG_WARNING, "finished playing a sound, next = %x\n", member->soundq);
+ // notify applications via mgr interface that this sound has been played
+ manager_event(
+ EVENT_FLAG_CALL,
+ "ConferenceSoundComplete",
+ "Channel: %s\r\n"
+ "Sound: %s\r\n",
+ member->channel_name,
+ toboot->name
+ );
+
+ free(toboot);
+ if(member->soundq) goto again;
+
+ // if we get here, we've gotten to the end of the queue; reset write format
+ if ( ast_set_write_format( member->chan, member->write_format ) < 0 )
+ {
+ ast_log( LOG_ERROR, "unable to set write format to %d\n",
+ member->write_format ) ;
+ }
+ } else {
+ // copy delivery from exampleframe
+ f->delivery = exampleframe->delivery;
+ }
+
+ return f;
+}
+
+
+// process outgoing frames for the channel, playing either normal conference audio,
+// or requested sounds
+static int process_outgoing(struct ast_conf_member *member)
+{
+ conf_frame* cf ; // frame read from the output queue
+ struct ast_frame *f;
+ struct ast_frame *realframe = NULL;
+
+ for(;;)
+ {
+ // acquire member mutex and grab a frame.
+ ast_mutex_lock( &member->lock ) ;
+ cf = get_outgoing_frame( member ) ;
+
+ // if there's no frames exit the loop.
+ if ( !cf )
+ {
+ ast_mutex_unlock( &member->lock ) ;
+ break;
+ }
+
+
+ f = cf->fr;
+
+ // if we're playing sounds, we can just replace the frame with the
+ // next sound frame, and send it instead
+ if ( member->soundq )
+ {
+ realframe = f;
+ f = get_next_soundframe(member, f);
+ if ( !f )
+ {
+ // if we didn't get anything, just revert to "normal"
+ f = realframe;
+ realframe = NULL;
+ } else
+ {
+ // We have a sound frame now, but we need to make sure it's the same
+ // format as our channel write format
+ int wf = member->chan->writeformat & AST_FORMAT_AUDIO_MASK;
+ if ( f->frametype == AST_FRAME_VOICE && !(wf & f->subclass) )
+ {
+ // We need to change our channel's write format
+ ast_set_write_format(member->chan, f->subclass);
+ }
+ }
+ }
+
+ ast_mutex_unlock(&member->lock);
+
+
+#ifdef DEBUG_FRAME_TIMESTAMPS
+ // !!! TESTING !!!
+ int delivery_diff = usecdiff( &f->delivery, &member->lastsent_timeval ) ;
+ if ( delivery_diff != AST_CONF_FRAME_INTERVAL )
+ {
+ ast_log( AST_CONF_DEBUG, "unanticipated delivery time, delivery_diff => %d, delivery.tv_usec => %ld\n",
+ delivery_diff, f->delivery.tv_usec ) ;
+ }
+
+ // !!! TESTING !!!
+ if (
+ f->delivery.tv_sec < member->lastsent_timeval.tv_sec
+ || (
+ f->delivery.tv_sec == member->lastsent_timeval.tv_sec
+ && f->delivery.tv_usec <= member->lastsent_timeval.tv_usec
+ )
+ )
+ {
+ ast_log( LOG_WARNING, "queued frame timestamped in the past, %ld.%ld <= %ld.%ld\n",
+ f->delivery.tv_sec, f->delivery.tv_usec,
+ member->lastsent_timeval.tv_sec, member->lastsent_timeval.tv_usec ) ;
+ }
+ member->lastsent_timeval = f->delivery ;
+#endif
+
+#ifdef DEBUG_USE_TIMELOG
+ TIMELOG( ast_write( member->chan, f ), 10, "member: ast_write");
+#else
+
+ // send the voice frame
+ if ( ast_write( member->chan, f ) == 0 )
+ {
+ struct timeval tv = ast_tvnow();
+ ast_log( AST_CONF_DEBUG, "SENT VOICE FRAME, channel => %s, frames_out => %ld, s => %ld, ms => %ld\n",
+ member->channel_name, member->frames_out, tv.tv_sec, tv.tv_usec ) ;
+ }
+ else
+ {
+ // log 'dropped' outgoing frame
+ ast_log( LOG_ERROR, "unable to write voice frame to channel, channel => %s\n", member->channel_name ) ;
+
+ // accounting: count dropped outgoing frames
+ member->frames_out_dropped++ ;
+ }
+#endif
+ // clean up frame
+ delete_conf_frame( cf ) ;
+
+ }
+
+ // Do the same for video, suck it dry
+ for(;;)
+ {
+ // grab a frame.
+ cf = get_outgoing_video_frame( member ) ;
+
+ // if there's no frames exit the loop.
+ if(!cf){
+ break;
+ }
+
+ f = cf->fr;
+
+ // send the video frame
+ if ( ast_write_video( member->chan, f ) == 1 )
+ {
+ struct timeval tv = ast_tvnow();
+ ast_log( AST_CONF_DEBUG, "SENT VIDEO FRAME, channel => %s, frames_out => %ld, s => %ld, ms => %ld\n",
+ member->channel_name, member->frames_out, tv.tv_sec, tv.tv_usec ) ;
+ }
+ else
+ {
+ // log 'dropped' outgoing frame
+ ast_log( AST_CONF_DEBUG, "unable to write video frame to channel, channel => %s\n", member->channel_name ) ;
+
+ // accounting: count dropped outgoing frames
+ member->video_frames_out_dropped++ ;
+ }
+
+ // clean up frame
+ delete_conf_frame( cf ) ;
+
+ }
+
+ // Do the same for dtmf, suck it dry
+ for(;;)
+ {
+ // acquire member mutex and grab a frame.
+ cf = get_outgoing_dtmf_frame( member ) ;
+
+ // if there's no frames exit the loop.
+ if(!cf) break;
+
+ // send the dtmf frame
+ if ( ast_write( member->chan, cf->fr ) == 0 )
+ {
+ struct timeval tv = ast_tvnow();
+ ast_log( AST_CONF_DEBUG, "SENT DTMF FRAME, channel => %s, frames_out => %ld, s => %ld, ms => %ld\n",
+ member->channel_name, member->frames_out, tv.tv_sec, tv.tv_usec ) ;
+
+ }
+ else
+ {
+ // log 'dropped' outgoing frame
+ ast_log( AST_CONF_DEBUG, "unable to write dtmf frame to channel, channel => %s\n", member->channel_name ) ;
+
+ // accounting: count dropped outgoing frames
+ member->dtmf_frames_out_dropped++ ;
+ }
+
+ // clean up frame
+ delete_conf_frame( cf ) ;
+ }
+
+ // Do the same for text, hell, why not?
+ for(;;)
+ {
+ // acquire member mutex and grab a frame.
+ cf = get_outgoing_text_frame( member ) ;
+
+ // if there's no frames exit the loop.
+ if(!cf) break;
+
+ // send the text frame
+ if ( ast_write( member->chan, cf->fr ) == 0 )
+ {
+ struct timeval tv = ast_tvnow();
+ ast_log( AST_CONF_DEBUG, "SENT TEXT FRAME, channel => %s, frames_out => %ld, s => %ld, ms => %ld\n",
+ member->channel_name, member->frames_out, tv.tv_sec, tv.tv_usec ) ;
+
+ }
+ else
+ {
+ // log 'dropped' outgoing frame
+ ast_log( AST_CONF_DEBUG, "unable to write text frame to channel, channel => %s\n", member->channel_name ) ;
+
+ // accounting: count dropped outgoing frames
+ member->text_frames_out_dropped++ ;
+ }
+
+ // clean up frame
+ delete_conf_frame( cf ) ;
+ }
+
+
+ return 0;
+}
+
+static int member_checkkick( struct ast_conf_member *member )
+{
+ int kick;
+ ast_mutex_lock( &member->lock ) ;
+ kick = member->kick_flag;
+ ast_mutex_unlock( &member->lock ) ;
+ return kick;
+}
+
+//
+// main member thread function
+//
+
+int member_exec( struct ast_channel* chan, void* data )
+{
+// struct timeval start, end ;
+// start = ast_tvnow();
+
+ struct ast_conference *conf ;
+ struct ast_conf_member *member ;
+
+ struct ast_frame *f ; // frame received from ast_read()
+
+ int left = 0 ;
+ int res;
+
+ ast_log( AST_CONF_DEBUG, "Begin processing member thread, channel => %s\n", chan->name ) ;
+
+ //
+ // If the call has not yet been answered, answer the call
+ // Note: asterisk apps seem to check _state, but it seems like it's safe
+ // to just call ast_answer. It will just do nothing if it is up.
+ // it will also return -1 if the channel is a zombie, or has hung up.
+ //
+
+ res = ast_answer( chan ) ;
+ if ( res )
+ {
+ ast_log( LOG_ERROR, "unable to answer call\n" ) ;
+ return -1 ;
+ }
+
+ //
+ // create a new member for the conference
+ //
+
+// ast_log( AST_CONF_DEBUG, "creating new member, id => %s, flags => %s, p => %s\n",
+// id, flags, priority ) ;
+
+ member = create_member( chan, (const char*)( data ) ) ; // flags, atoi( priority ) ) ;
+
+ // unable to create member, return an error
+ if ( member == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to create member\n" ) ;
+ return -1 ;
+ }
+
+ //
+ // setup asterisk read/write formats
+ //
+#if 0
+ ast_log( AST_CONF_DEBUG, "CHANNEL INFO, CHANNEL => %s, DNID => %s, CALLER_ID => %s, ANI => %s\n",
+ chan->name, chan->dnid, chan->callerid, chan->ani ) ;
+
+ ast_log( AST_CONF_DEBUG, "CHANNEL CODECS, CHANNEL => %s, NATIVE => %d, READ => %d, WRITE => %d\n",
+ chan->name, chan->nativeformats, member->read_format, member->write_format ) ;
+#endif
+ if ( ast_set_read_format( chan, member->read_format ) < 0 )
+ {
+ ast_log( LOG_ERROR, "unable to set read format to signed linear\n" ) ;
+ delete_member( member ) ;
+ return -1 ;
+ }
+
+ if ( ast_set_write_format( chan, member->write_format ) < 0 ) // AST_FORMAT_SLINEAR, chan->nativeformats
+ {
+ ast_log( LOG_ERROR, "unable to set write format to signed linear\n" ) ;
+ delete_member( member ) ;
+ return -1 ;
+ }
+
+ //
+ // setup a conference for the new member
+ //
+
+ conf = start_conference( member ) ;
+
+ if ( conf == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to setup member conference\n" ) ;
+ delete_member( member) ;
+ return -1 ;
+ }
+
+
+ manager_event(
+ EVENT_FLAG_CALL,
+ "ConferenceJoin",
+ "ConferenceName: %s\r\n"
+ "Member: %d\r\n"
+ "Channel: %s\r\n"
+ "CallerID: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "Count: %d\r\n",
+ conf->name,
+ member->id,
+ member->channel_name,
+ member->chan->cid.cid_num ? member->chan->cid.cid_num : "unknown",
+ member->chan->cid.cid_name ? member->chan->cid.cid_name: "unknown",
+ conf->membercount
+ ) ;
+
+ // Store the CID information
+ if ( member->chan->cid.cid_num )
+ {
+ if ( (member->callerid = malloc(strlen(member->chan->cid.cid_num)+1)) )
+ memcpy(member->callerid,member->chan->cid.cid_num, strlen(member->chan->cid.cid_num)+1);
+ } else
+ member->callerid = NULL;
+
+ if ( member->chan->cid.cid_name )
+ {
+ if ( (member->callername = malloc(strlen(member->chan->cid.cid_name)+1)) )
+ memcpy(member->callername, member->chan->cid.cid_name, strlen(member->chan->cid.cid_name)+1);
+ } else
+ member->callername = NULL;
+
+
+ //
+ // process loop for new member ( this runs in it's own thread )
+ //
+
+ ast_log( AST_CONF_DEBUG, "begin member event loop, channel => %s\n", chan->name ) ;
+
+ // timer timestamps
+ struct timeval base, curr ;
+ base = ast_tvnow();
+
+ // tell conference_exec we're ready for frames
+ member->ready_for_outgoing = 1 ;
+
+ // notify others about enter
+ char * enter_snd = member->enter_snd;
+ if (strcmp(enter_snd, "-")!=0)
+ {
+ // lock conference
+ ast_mutex_lock( &conf->lock );
+
+ /*if (conf->membercount < 2)
+ {
+ enter_snd = "conf-onlyperson";
+ }*/
+
+ struct ast_conf_member *membertest = conf->memberlist;
+ while (membertest != NULL)
+ {
+ // lock member for basic_play_sound
+ ast_mutex_lock(&membertest->lock);
+
+ // basic_play_sound unlock member automatically. do not mute on enter message
+ if (!basic_play_sound ( membertest, enter_snd, 0 ))
+ {
+ ast_log(LOG_ERROR, "playing conference[%d] entry message <%s> FAILED on <%s>\n", conf->membercount, enter_snd, membertest->channel_name);
+ }
+ else
+ {
+ ast_log(LOG_NOTICE, "playing conference[%d] entry message <%s> on <%s>\n", conf->membercount, enter_snd, membertest->channel_name);
+ }
+
+ membertest = membertest->next;
+ }
+
+ // unlock conference
+ ast_mutex_unlock( &conf->lock );
+ }
+
+ while ( 42 == 42 )
+ {
+ // make sure we have a channel to process
+ if ( chan == NULL )
+ {
+ ast_log( LOG_NOTICE, "member channel has closed\n" ) ;
+ break ;
+ }
+
+ //-----------------//
+ // INCOMING FRAMES //
+ //-----------------//
+
+ // wait for an event on this channel
+ left = ast_waitfor( chan, AST_CONF_WAITFOR_LATENCY ) ;
+
+ //ast_log( AST_CONF_DEBUG, "received event on channel, name => %s, left => %d\n", chan->name, left ) ;
+
+ if ( left < 0 )
+ {
+ // an error occured
+ ast_log(
+ LOG_NOTICE,
+ "an error occured waiting for a frame, channel => %s, error => %d\n",
+ chan->name, left
+ ) ;
+ break; // out of the 42==42
+ }
+ else if ( left == 0 )
+ {
+ // no frame has arrived yet
+ // ast_log( LOG_NOTICE, "no frame available from channel, channel => %s\n", chan->name ) ;
+ }
+ else if ( left > 0 )
+ {
+ // a frame has come in before the latency timeout
+ // was reached, so we process the frame
+
+ f = ast_read( chan ) ;
+
+ if ( f == NULL )
+ {
+ if (conf->debug_flag)
+ {
+ ast_log( LOG_NOTICE, "unable to read from channel, channel => %s\n", chan->name ) ;
+ // They probably want to hangup...
+ }
+ break ;
+ }
+
+ // actually process the frame: break if we got hangup.
+ if(process_incoming(member, conf, f)) break;
+
+ }
+
+ if (member_checkkick(member)) break;
+
+ //-----------------//
+ // OUTGOING FRAMES //
+ //-----------------//
+
+ // update the current timestamps
+ curr = ast_tvnow();
+
+ process_outgoing(member);
+ // back to process incoming frames
+ continue ;
+ }
+
+ ast_log( AST_CONF_DEBUG, "end member event loop, time_entered => %ld\n", member->time_entered.tv_sec ) ;
+
+ //
+ // clean up
+ //
+
+#ifdef DEBUG_OUTPUT_PCM
+ // !!! TESTING !!!
+ if ( incoming_fh != NULL )
+ fclose( incoming_fh ) ;
+#endif
+
+ // If we're driving another member, make sure its speaker count is correct
+ if ( member != NULL ) member->remove_flag = 1 ;
+
+// end = ast_tvnow();
+// int expected_frames = ( int )( floor( (double)( msecdiff( &end, &start ) / AST_CONF_FRAME_INTERVAL ) ) ) ;
+// ast_log( AST_CONF_DEBUG, "expected_frames => %d\n", expected_frames ) ;
+
+ return 0 ;
+}
+
+
+
+struct ast_conf_member *check_active_video( int id, struct ast_conference *conf )
+{
+ struct ast_conf_member *member;
+
+ // acquire the conference lock
+ ast_mutex_lock( &conf->lock ) ;
+
+ member = conf->memberlist;
+ while (member)
+ {
+ if (member->id == id)
+ {
+ // lock this member
+ ast_mutex_lock( &member->lock ) ;
+ ast_mutex_unlock( &conf->lock ) ;
+ return member;
+ }
+ member = member->next;
+ }
+ ast_mutex_unlock( &conf->lock ) ;
+ return NULL;
+}
+
+//
+// manange member functions
+//
+
+struct ast_conf_member* create_member( struct ast_channel *chan, const char* data )
+{
+ char *parse;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(conf_name);
+ AST_APP_ARG(flags);
+ AST_APP_ARG(enter_snd);
+ AST_APP_ARG(leave_snd);
+ AST_APP_ARG(priority);
+ AST_APP_ARG(vad_prob_start);
+ AST_APP_ARG(vad_prob_continue);
+ );
+
+ //
+ // check input
+ //
+
+ if ( chan == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to create member with null channel\n" ) ;
+ return NULL ;
+ }
+
+ if ( chan->name == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to create member with null channel name\n" ) ;
+ return NULL ;
+ }
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "Conference requires an argument (conf_name[|flags[|enter_snd[|leave_snd[|priority[vad_prob_start[|vad_prob_continue]]]]]]))\n");
+ return NULL;
+ }
+
+ //
+ // allocate memory for new conference member
+ //
+
+ struct ast_conf_member *member = calloc( 1, sizeof( struct ast_conf_member ) ) ;
+
+ if ( member == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to malloc ast_conf_member\n" ) ;
+ return NULL ;
+ }
+
+ // initialize mutex
+ ast_mutex_init( &member->lock ) ;
+
+ //
+ // initialize member with passed data values
+ //
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ // parse the id
+ if (ast_strlen_zero(args.conf_name))
+ {
+ ast_log( LOG_ERROR, "unable to parse member id\n" ) ;
+ free( member ) ;
+ return NULL ;
+ }
+ else
+ member->conf_name = ast_strdup(args.conf_name);
+
+ // parse the flags
+ if (!ast_strlen_zero(args.flags))
+ member->flags = ast_strdup(args.flags);
+ else
+ member->flags = ast_strdup("");
+
+ // parse enter sound
+ if (!ast_strlen_zero(args.enter_snd))
+ member->enter_snd = ast_strdup(args.enter_snd);
+ else
+ member->enter_snd = ast_strdup("enter");
+
+ // parse leave sound
+ if (!ast_strlen_zero(args.leave_snd))
+ member->leave_snd = ast_strdup(args.leave_snd);
+ else
+ member->leave_snd = ast_strdup("leave");
+
+ if (!ast_strlen_zero(args.priority))
+ // parse the priority
+ member->priority = atoi(args.priority);
+ else
+ member->priority = 0;
+
+ if (!ast_strlen_zero(args.vad_prob_start))
+ member->vad_prob_start = atof(args.vad_prob_start);
+ else
+ member->vad_prob_start = AST_CONF_PROB_START;
+
+ if (!ast_strlen_zero(args.vad_prob_continue))
+ member->vad_prob_continue = atof(args.vad_prob_continue);
+ else
+ member->vad_prob_continue = AST_CONF_PROB_CONTINUE;
+
+ // debugging
+ ast_log(
+ AST_CONF_DEBUG,
+ "parsed data params, id => %s, flags => %s, priority => %d, vad_prob_start => %f, vad_prob_continue => %f\n",
+ member->conf_name, member->flags, member->priority, member->vad_prob_start, member->vad_prob_continue
+ ) ;
+
+ //
+ // initialize member with default values
+ //
+
+ // keep pointer to member's channel
+ member->chan = chan ;
+
+ // copy the channel name
+ member->channel_name = malloc( strlen( chan->name ) + 1 ) ;
+ strcpy( member->channel_name, chan->name ) ;
+
+ // ( default can be overridden by passed flags )
+ member->mute_audio = 0;
+ member->mute_video = 0;
+ member->norecv_audio = 0;
+ member->norecv_video = 0;
+ member->no_camera = 0;
+
+ // moderator?
+ member->ismoderator = 0;
+
+ // ready flag
+ member->ready_for_outgoing = 0 ;
+
+ // incoming frame queue
+ member->inFrames = NULL ;
+ member->inFramesTail = NULL ;
+ member->inFramesCount = 0 ;
+
+ member->inVideoFrames = NULL ;
+ member->inVideoFramesTail = NULL ;
+ member->inVideoFramesCount = 0 ;
+
+ member->inDTMFFrames = NULL ;
+ member->inDTMFFramesTail = NULL ;
+ member->inDTMFFramesCount = 0 ;
+
+ member->inTextFrames = NULL ;
+ member->inTextFramesTail = NULL ;
+ member->inTextFramesCount = 0 ;
+
+ member->conference = 1; // we have switched req_id
+ member->dtmf_switch = 0; // no dtmf switch by default
+ member->dtmf_relay = 0; // no dtmf relay by default
+
+ // start of day video ids
+ member->req_id = -1;
+ member->id = -1;
+
+ member->first_frame_received = 0; // cause a FIR after NAT delay
+
+ // last frame caching
+ member->inFramesRepeatLast = 0 ;
+ member->inFramesLast = NULL ;
+ member->okayToCacheLast = 0 ;
+
+ // outgoing frame queue
+ member->outFrames = NULL ;
+ member->outFramesTail = NULL ;
+ member->outFramesCount = 0 ;
+
+ member->outVideoFrames = NULL ;
+ member->outVideoFramesTail = NULL ;
+ member->outVideoFramesCount = 0 ;
+
+ member->outDTMFFrames = NULL ;
+ member->outDTMFFramesTail = NULL ;
+ member->outDTMFFramesCount = 0 ;
+
+ member->outTextFrames = NULL ;
+ member->outTextFramesTail = NULL ;
+ member->outTextFramesCount = 0 ;
+
+ // ( not currently used )
+ // member->samplesperframe = AST_CONF_BLOCK_SAMPLES ;
+
+ // used for determining need to mix frames
+ // and for management interface notification
+ // and for VAD based video switching
+ member->speaking_state_notify = 0 ;
+ member->speaking_state = 0 ;
+ member->local_speaking_state = 0;
+ member->speaker_count = 0;
+ member->driven_member = NULL;
+
+ // linked-list pointer
+ member->next = NULL ;
+
+ // account data
+ member->frames_in = 0 ;
+ member->frames_in_dropped = 0 ;
+ member->frames_out = 0 ;
+ member->frames_out_dropped = 0 ;
+ member->video_frames_in = 0 ;
+ member->video_frames_in_dropped = 0 ;
+ member->video_frames_out = 0 ;
+ member->video_frames_out_dropped = 0 ;
+ member->dtmf_frames_in = 0 ;
+ member->dtmf_frames_in_dropped = 0 ;
+ member->dtmf_frames_out = 0 ;
+ member->dtmf_frames_out_dropped = 0 ;
+ member->text_frames_in = 0 ;
+ member->text_frames_in_dropped = 0 ;
+ member->text_frames_out = 0 ;
+ member->text_frames_out_dropped = 0 ;
+
+ // for counting sequentially dropped frames
+ member->sequential_drops = 0 ;
+ member->since_dropped = 0 ;
+
+ // flags
+ member->remove_flag = 0 ;
+ member->kick_flag = 0;
+
+ // record start time
+ // init dropped frame timestamps
+ // init state change timestamp
+ member->time_entered =
+ member->last_in_dropped =
+ member->last_out_dropped =
+ member->last_state_change = ast_tvnow();
+
+ //
+ // parse passed flags
+ //
+
+ // silence detection flags w/ defaults
+ member->vad_flag = 0 ;
+ member->denoise_flag = 0 ;
+ member->agc_flag = 0 ;
+
+ // is this member using the telephone?
+ member->via_telephone = 0 ;
+
+ // temp pointer to flags string
+ char* flags = member->flags ;
+
+ int i;
+
+ for ( i = 0 ; i < strlen( flags ) ; ++i )
+ {
+
+ if (flags[i] >= (int)'0' && flags[i] <= (int)'9')
+ {
+ if (member->req_id < 0)
+ {
+ member->req_id = flags[i] - (int)'0';
+ }
+ else
+ {
+ int newid = flags[i] - (int)'0';
+ // need to boot anyone with this id already
+ // will happen in add_member
+ member->id = newid;
+ }
+ }
+ else
+ {
+ // allowed flags are C, c, L, l, V, D, A, C, X, R, T, t, M, S
+ // mute/no_recv options
+ switch ( flags[i] )
+ {
+ case 'C':
+ member->mute_video = 1;
+ break ;
+ case 'c':
+ member->norecv_video = 1;
+ break ;
+ case 'L':
+ member->mute_audio = 1;
+ break ;
+ case 'l':
+ member->norecv_audio = 1;
+ break;
+
+ // speex preprocessing options
+ case 'V':
+ member->vad_flag = 1 ;
+ break ;
+ case 'D':
+ member->denoise_flag = 1 ;
+ break ;
+ case 'A':
+ member->agc_flag = 1 ;
+ break ;
+
+ // dtmf/moderator/video switching options
+ case 'X':
+ member->dtmf_switch = 1;
+ break;
+ case 'R':
+ member->dtmf_relay = 1;
+ break;
+ case 'S':
+ member->vad_switch = 1;
+ break;
+ case 'M':
+ member->ismoderator = 1;
+ break;
+ case 'N':
+ member->no_camera = 1;
+ break;
+ case 't':
+ member->does_text = 1;
+ break;
+
+ //Telephone connection
+ case 'T':
+ member->via_telephone = 1;
+ break;
+
+ default:
+ ast_log( LOG_WARNING, "received invalid flag, chan => %s, flag => %c\n",
+ chan->name, flags[i] );
+ break ;
+ }
+ }
+ }
+
+ // set the dsp to null so silence detection is disabled by default
+ member->dsp = NULL ;
+
+#if ( SILDET == 2 )
+ //
+ // configure silence detection and preprocessing
+ // if the user is coming in via the telephone,
+ // and is not listen-only
+ //
+ if (
+ member->via_telephone == 1
+ && member->type != 'L'
+ )
+ {
+ // create a speex preprocessor
+ member->dsp = speex_preprocess_state_init( AST_CONF_BLOCK_SAMPLES, AST_CONF_SAMPLE_RATE ) ;
+
+ if ( member->dsp == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to initialize member dsp, channel => %s\n", chan->name ) ;
+ }
+ else
+ {
+ ast_log( LOG_NOTICE, "member dsp initialized, channel => %s, v => %d, d => %d, a => %d\n",
+ chan->name, member->vad_flag, member->denoise_flag, member->agc_flag ) ;
+
+ // set speex preprocessor options
+ speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_VAD, &(member->vad_flag) ) ;
+ speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_DENOISE, &(member->denoise_flag) ) ;
+ speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_AGC, &(member->agc_flag) ) ;
+
+ speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_PROB_START, &member->vad_prob_start ) ;
+ speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_PROB_CONTINUE, &member->vad_prob_continue ) ;
+
+ speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_GET_PROB_START, &member->vad_prob_start ) ;
+ speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_GET_PROB_CONTINUE, &member->vad_prob_continue ) ;
+
+ ast_log( AST_CONF_DEBUG, "speech_prob_start => %f, speech_prob_continue => %f\n",
+ member->vad_prob_start, member->vad_prob_continue ) ;
+ }
+ }
+#endif
+
+ //
+ // set connection type
+ //
+
+ if ( member->via_telephone == 1 )
+ {
+ member->connection_type = 'T' ;
+ }
+ else if ( strncmp( member->channel_name, "SIP", 3 ) == 0 )
+ {
+ member->connection_type = 'S' ;
+ }
+ else // default to iax
+ {
+ member->connection_type = 'X' ;
+ }
+
+ //
+ // read, write, and translation options
+ //
+
+ // set member's audio formats, taking dsp preprocessing into account
+ // ( chan->nativeformats, AST_FORMAT_SLINEAR, AST_FORMAT_ULAW, AST_FORMAT_GSM )
+ member->read_format = ( member->dsp == NULL ) ? chan->nativeformats : AST_FORMAT_SLINEAR ;
+
+ member->write_format = chan->nativeformats;
+
+ // 1.2 or 1.3+
+#ifdef AST_FORMAT_AUDIO_MASK
+
+ member->read_format &= AST_FORMAT_AUDIO_MASK;
+ member->write_format &= AST_FORMAT_AUDIO_MASK;
+#endif
+
+ // translation paths ( ast_translator_build_path() returns null if formats match )
+ member->to_slinear = ast_translator_build_path( AST_FORMAT_SLINEAR, member->read_format ) ;
+ member->from_slinear = ast_translator_build_path( member->write_format, AST_FORMAT_SLINEAR ) ;
+
+ ast_log( AST_CONF_DEBUG, "AST_FORMAT_SLINEAR => %d\n", AST_FORMAT_SLINEAR ) ;
+
+ // index for converted_frames array
+ switch ( member->write_format )
+ {
+ case AST_FORMAT_SLINEAR:
+ member->write_format_index = AC_SLINEAR_INDEX ;
+ break ;
+
+ case AST_FORMAT_ULAW:
+ member->write_format_index = AC_ULAW_INDEX ;
+ break ;
+
+ case AST_FORMAT_ALAW:
+ member->write_format_index = AC_ALAW_INDEX ;
+ break ;
+
+ case AST_FORMAT_GSM:
+ member->write_format_index = AC_GSM_INDEX ;
+ break ;
+
+ case AST_FORMAT_SPEEX:
+ member->write_format_index = AC_SPEEX_INDEX;
+ break;
+
+#ifdef AC_USE_G729A
+ case AST_FORMAT_G729A:
+ member->write_format_index = AC_G729A_INDEX;
+ break;
+#endif
+
+ default:
+ member->write_format_index = 0 ;
+ }
+
+ // index for converted_frames array
+ switch ( member->read_format )
+ {
+ case AST_FORMAT_SLINEAR:
+ member->read_format_index = AC_SLINEAR_INDEX ;
+ break ;
+
+ case AST_FORMAT_ULAW:
+ member->read_format_index = AC_ULAW_INDEX ;
+ break ;
+
+ case AST_FORMAT_ALAW:
+ member->read_format_index = AC_ALAW_INDEX ;
+ break ;
+
+ case AST_FORMAT_GSM:
+ member->read_format_index = AC_GSM_INDEX ;
+ break ;
+
+ case AST_FORMAT_SPEEX:
+ member->read_format_index = AC_SPEEX_INDEX;
+ break;
+
+#ifdef AC_USE_G729A
+ case AST_FORMAT_G729A:
+ member->read_format_index = AC_G729A_INDEX;
+ break;
+#endif
+
+ default:
+ member->read_format_index = 0 ;
+ }
+
+ // smoother defaults.
+ member->smooth_multiple =1;
+ member->smooth_size_in = -1;
+ member->smooth_size_out = -1;
+ member->inSmoother= NULL;
+ member->outPacker= NULL;
+
+ switch (member->read_format){
+ /* these assumptions may be incorrect */
+ case AST_FORMAT_ULAW:
+ case AST_FORMAT_ALAW:
+ member->smooth_size_in = 160; //bytes
+ member->smooth_size_out = 160; //samples
+ break;
+ case AST_FORMAT_GSM:
+ /*
+ member->smooth_size_in = 33; //bytes
+ member->smooth_size_out = 160;//samples
+ */
+ break;
+ case AST_FORMAT_SPEEX:
+ case AST_FORMAT_G729A:
+ /* this assumptions are wrong
+ member->smooth_multiple = 2 ; // for testing, force to dual frame
+ member->smooth_size_in = 39; // bytes
+ member->smooth_size_out = 160; // samples
+ */
+ break;
+ case AST_FORMAT_SLINEAR:
+ member->smooth_size_in = 320; //bytes
+ member->smooth_size_out = 160; //samples
+ break;
+ default:
+ member->inSmoother = NULL; //don't use smoother for this type.
+ //ast_log( AST_CONF_DEBUG, "smoother is NULL for member->read_format => %d\n", member->read_format);
+ }
+
+ if (member->smooth_size_in > 0){
+ member->inSmoother = ast_smoother_new(member->smooth_size_in);
+ ast_log( AST_CONF_DEBUG, "created smoother(%d) for %d\n", member->smooth_size_in , member->read_format);
+ }
+
+ //
+ // finish up
+ //
+
+ ast_log( AST_CONF_DEBUG, "created member, type => %c, priority => %d, readformat => %d\n",
+ member->type, member->priority, chan->readformat ) ;
+
+ return member ;
+}
+
+struct ast_conf_member* delete_member( struct ast_conf_member* member )
+{
+ // !!! NO RETURN TEST !!!
+ // do { sleep(1) ; } while (1) ;
+
+ // !!! CRASH TEST !!!
+ // *((int *)0) = 0;
+
+ if ( member == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to the delete null member\n" ) ;
+ return NULL ;
+ }
+
+ ast_mutex_lock(&member->lock);
+
+ // If member is driving another member, make sure its speaker count is correct
+ if ( member->driven_member != NULL && member->speaking_state == 1 )
+ decrement_speaker_count(member->driven_member, 1);
+
+ //
+ // clean up member flags
+ //
+
+ if ( member->flags != NULL )
+ {
+ // !!! DEBUGING !!!
+ ast_log( AST_CONF_DEBUG, "freeing member flags, name => %s\n",
+ member->channel_name ) ;
+ free( member->flags ) ;
+ }
+
+ //
+ // delete the members frames
+ //
+
+ conf_frame* cf ;
+
+ // !!! DEBUGING !!!
+ ast_log( AST_CONF_DEBUG, "deleting member input frames, name => %s\n",
+ member->channel_name ) ;
+
+ // incoming frames
+ cf = member->inFrames ;
+
+ while ( cf != NULL )
+ {
+ cf = delete_conf_frame( cf ) ;
+ }
+
+ if (member->inSmoother != NULL)
+ ast_smoother_free(member->inSmoother);
+
+ cf = member->inVideoFrames ;
+
+ while ( cf != NULL )
+ {
+ cf = delete_conf_frame( cf ) ;
+ }
+
+ // !!! DEBUGING !!!
+ ast_log( AST_CONF_DEBUG, "deleting member output frames, name => %s\n",
+ member->channel_name ) ;
+
+ // outgoing frames
+ cf = member->outFrames ;
+
+ while ( cf != NULL )
+ {
+ cf = delete_conf_frame( cf ) ;
+ }
+
+ cf = member->outVideoFrames ;
+
+ while ( cf != NULL )
+ {
+ cf = delete_conf_frame( cf ) ;
+ }
+
+#if ( SILDET == 2 )
+ if ( member->dsp != NULL )
+ {
+ // !!! DEBUGING !!!
+ ast_log( AST_CONF_DEBUG, "destroying member preprocessor, name => %s\n",
+ member->channel_name ) ;
+ speex_preprocess_state_destroy( member->dsp ) ;
+ }
+#endif
+
+ // !!! DEBUGING !!!
+ ast_log( AST_CONF_DEBUG, "freeing member translator paths, name => %s\n",
+ member->channel_name ) ;
+
+ // free the mixing translators
+ ast_translator_free_path( member->to_slinear ) ;
+ ast_translator_free_path( member->from_slinear ) ;
+
+ // get a pointer to the next
+ // member so we can return it
+ struct ast_conf_member* nm = member->next ;
+
+ ast_mutex_unlock(&member->lock);
+
+ // !!! DEBUGING !!!
+ ast_log( AST_CONF_DEBUG, "freeing member channel name, name => %s\n",
+ member->channel_name ) ;
+
+ // free the member's copy for the channel name
+ free( member->channel_name ) ;
+
+ // free the member's copy of the conference name
+ free(member->conf_name);
+
+ // !!! DEBUGING !!!
+ ast_log( AST_CONF_DEBUG, "freeing member\n" ) ;
+
+ // free the member's memory
+ free(member->callerid);
+ free(member->callername);
+
+ // free enter/leave sounds
+ free(member->enter_snd);
+ free(member->leave_snd);
+
+ free( member ) ;
+ member = NULL ;
+
+ return nm ;
+}
+
+//
+// incoming frame functions
+//
+
+conf_frame* get_incoming_video_frame( struct ast_conf_member *member )
+{
+ if ( member == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ;
+ return NULL ;
+ }
+
+ ast_mutex_lock(&member->lock);
+
+ if ( member->inVideoFramesCount == 0 )
+ {
+ ast_mutex_unlock(&member->lock);
+ return NULL ;
+ }
+
+ //
+ // return the next frame in the queue
+ //
+
+ conf_frame* cfr = NULL ;
+
+ // get first frame in line
+ cfr = member->inVideoFramesTail ;
+
+ // if it's the only frame, reset the queue,
+ // else, move the second frame to the front
+ if ( member->inVideoFramesTail == member->inVideoFrames )
+ {
+ member->inVideoFramesTail = NULL ;
+ member->inVideoFrames = NULL ;
+ }
+ else
+ {
+ // move the pointer to the next frame
+ member->inVideoFramesTail = member->inVideoFramesTail->prev ;
+
+ // reset it's 'next' pointer
+ if ( member->inVideoFramesTail != NULL )
+ member->inVideoFramesTail->next = NULL ;
+ }
+
+ // separate the conf frame from the list
+ cfr->next = NULL ;
+ cfr->prev = NULL ;
+
+ // decrement frame count
+ member->inVideoFramesCount-- ;
+
+ ast_mutex_unlock(&member->lock);
+ return cfr ;
+
+}
+conf_frame* get_incoming_dtmf_frame( struct ast_conf_member *member )
+{
+ if ( member == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ;
+ return NULL ;
+ }
+
+ ast_mutex_lock(&member->lock);
+
+ if ( member->inDTMFFramesCount == 0 )
+ {
+ ast_mutex_unlock(&member->lock);
+ return NULL ;
+ }
+
+ //
+ // return the next frame in the queue
+ //
+
+ conf_frame* cfr = NULL ;
+
+ // get first frame in line
+ cfr = member->inDTMFFramesTail ;
+
+ // if it's the only frame, reset the queue,
+ // else, move the second frame to the front
+ if ( member->inDTMFFramesTail == member->inDTMFFrames )
+ {
+ member->inDTMFFramesTail = NULL ;
+ member->inDTMFFrames = NULL ;
+ }
+ else
+ {
+ // move the pointer to the next frame
+ member->inDTMFFramesTail = member->inDTMFFramesTail->prev ;
+
+ // reset it's 'next' pointer
+ if ( member->inDTMFFramesTail != NULL )
+ member->inDTMFFramesTail->next = NULL ;
+ }
+
+ // separate the conf frame from the list
+ cfr->next = NULL ;
+ cfr->prev = NULL ;
+
+ // decriment frame count
+ member->inDTMFFramesCount-- ;
+
+ ast_mutex_unlock(&member->lock);
+ return cfr ;
+
+}
+
+
+conf_frame* get_incoming_frame( struct ast_conf_member *member )
+{
+ conf_frame *cf_result;
+ //
+ // sanity checks
+ //
+
+ if ( member == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ;
+ return NULL ;
+ }
+
+ ast_mutex_lock(&member->lock);
+
+ //
+ // repeat last frame a couple times to smooth transition
+ //
+
+#ifdef AST_CONF_CACHE_LAST_FRAME
+ if ( member->inFramesCount == 0 )
+ {
+ // nothing to do if there's no cached frame
+ if ( member->inFramesLast == NULL ) {
+ ast_mutex_unlock(&member->lock);
+ return NULL ;
+ }
+
+ // turn off 'okay to cache' flag
+ member->okayToCacheLast = 0 ;
+
+ if ( member->inFramesRepeatLast >= AST_CONF_CACHE_LAST_FRAME )
+ {
+ // already used this frame AST_CONF_CACHE_LAST_FRAME times
+
+ // reset repeat count
+ member->inFramesRepeatLast = 0 ;
+
+ // clear the cached frame
+ delete_conf_frame( member->inFramesLast ) ;
+ member->inFramesLast = NULL ;
+
+ // return null
+ ast_mutex_unlock(&member->lock);
+ return NULL ;
+ }
+ else
+ {
+ ast_log( AST_CONF_DEBUG, "repeating cached frame, channel => %s, inFramesRepeatLast => %d\n",
+ member->channel_name, member->inFramesRepeatLast ) ;
+
+ // increment counter
+ member->inFramesRepeatLast++ ;
+
+ // return a copy of the cached frame
+ cf_result = copy_conf_frame( member->inFramesLast ) ;
+ ast_mutex_unlock(&member->lock);
+ return cf_result;
+ }
+ }
+ else if ( member->okayToCacheLast == 0 && member->inFramesCount >= 3 )
+ {
+ ast_log( AST_CONF_DEBUG, "enabling cached frame, channel => %s, incoming => %d, outgoing => %d\n",
+ member->channel_name, member->inFramesCount, member->outFramesCount ) ;
+
+ // turn on 'okay to cache' flag
+ member->okayToCacheLast = 1 ;
+ }
+#else
+ if ( member->inFramesCount == 0 ) {
+ ast_mutex_unlock(&member->lock);
+ return NULL ;
+ }
+#endif // AST_CONF_CACHE_LAST_FRAME
+
+ //
+ // return the next frame in the queue
+ //
+
+ conf_frame* cfr = NULL ;
+
+ // get first frame in line
+ cfr = member->inFramesTail ;
+
+ // if it's the only frame, reset the queue,
+ // else, move the second frame to the front
+ if ( member->inFramesTail == member->inFrames )
+ {
+ member->inFramesTail = NULL ;
+ member->inFrames = NULL ;
+ }
+ else
+ {
+ // move the pointer to the next frame
+ member->inFramesTail = member->inFramesTail->prev ;
+
+ // reset it's 'next' pointer
+ if ( member->inFramesTail != NULL )
+ member->inFramesTail->next = NULL ;
+ }
+
+ // separate the conf frame from the list
+ cfr->next = NULL ;
+ cfr->prev = NULL ;
+
+ // decriment frame count
+ member->inFramesCount-- ;
+
+#ifdef AST_CONF_CACHE_LAST_FRAME
+ // copy frame if queue is now empty
+ if (
+ member->inFramesCount == 0
+ && member->okayToCacheLast == 1
+ )
+ {
+ // reset repeat count
+ member->inFramesRepeatLast = 0 ;
+
+ // clear cached frame
+ if ( member->inFramesLast != NULL )
+ {
+ delete_conf_frame( member->inFramesLast ) ;
+ member->inFramesLast = NULL ;
+ }
+
+ // cache new frame
+ member->inFramesLast = copy_conf_frame( cfr ) ;
+ }
+#endif // AST_CONF_CACHE_LAST_FRAME
+
+ ast_mutex_unlock(&member->lock);
+ return cfr ;
+}
+
+int queue_incoming_video_frame( struct ast_conf_member* member, const struct ast_frame* fr )
+{
+ // check on frame
+ if ( fr == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to queue null frame\n" ) ;
+ return -1 ;
+ }
+
+ // check on member
+ if ( member == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ;
+ return -1 ;
+ }
+
+ // lock the member
+ ast_mutex_lock(&member->lock);
+
+ if (!member->first_frame_received)
+ {
+ // nat=yes will be correct now
+ member->first_frame_received = 1;
+ member->conference = 1;
+ }
+
+ // We have to drop if the queue is full!
+ if ( member->inVideoFramesCount >= AST_CONF_MAX_VIDEO_QUEUE )
+ {
+ ast_log(
+ AST_CONF_DEBUG,
+ "unable to queue incoming VIDEO frame, channel => %s, incoming => %d, outgoing => %d\n",
+ member->channel_name, member->inVideoFramesCount, member->outVideoFramesCount
+ ) ;
+ ast_mutex_unlock(&member->lock);
+ return -1 ;
+ }
+
+ //
+ // create new conf frame from passed data frame
+ //
+
+ // ( member->inFrames may be null at this point )
+ conf_frame* cfr = create_conf_frame( member, member->inVideoFrames, fr ) ;
+
+ if ( cfr == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to malloc conf_frame\n" ) ;
+ ast_mutex_unlock(&member->lock);
+ return -1 ;
+ }
+
+ // copy frame data pointer to conf frame
+ // cfr->fr = fr ;
+
+ //
+ // add new frame to speaking members incoming frame queue
+ // ( i.e. save this frame data, so we can distribute it in conference_exec later )
+ //
+
+ if ( member->inVideoFrames == NULL )
+ {
+ // this is the first frame in the buffer
+ member->inVideoFramesTail = cfr ;
+ member->inVideoFrames = cfr ;
+ }
+ else
+ {
+ // put the new frame at the head of the list
+ member->inVideoFrames = cfr ;
+ }
+
+ // increment member frame count
+ member->inVideoFramesCount++ ;
+
+ ast_mutex_unlock(&member->lock);
+
+ // Everything has gone okay!
+ return 0;
+}
+
+int queue_incoming_dtmf_frame( struct ast_conf_member* member, const struct ast_frame* fr )
+{
+ //ast_log( AST_CONF_DEBUG, "queue incoming video frame\n");
+
+ // check on frame
+ if ( fr == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to queue null frame\n" ) ;
+ return -1 ;
+ }
+
+ // check on member
+ if ( member == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ;
+ return -1 ;
+ }
+
+ ast_mutex_lock(&member->lock);
+
+ // We have to drop if the queue is full!
+ if ( member->inDTMFFramesCount >= AST_CONF_MAX_DTMF_QUEUE )
+ {
+ ast_log(
+ AST_CONF_DEBUG,
+ "unable to queue incoming DTMF frame, channel => %s, incoming => %d, outgoing => %d\n",
+ member->channel_name, member->inDTMFFramesCount, member->outDTMFFramesCount
+ ) ;
+ ast_mutex_unlock(&member->lock);
+ return -1 ;
+ }
+
+ //
+ // create new conf frame from passed data frame
+ //
+
+ // ( member->inFrames may be null at this point )
+ conf_frame* cfr = create_conf_frame( member, member->inDTMFFrames, fr ) ;
+
+ if ( cfr == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to malloc conf_frame\n" ) ;
+ ast_mutex_unlock(&member->lock);
+ return -1 ;
+ }
+
+ // copy frame data pointer to conf frame
+ // cfr->fr = fr ;
+
+ //
+ // add new frame to speaking members incoming frame queue
+ // ( i.e. save this frame data, so we can distribute it in conference_exec later )
+ //
+
+ if ( member->inDTMFFrames == NULL )
+ {
+ // this is the first frame in the buffer
+ member->inDTMFFramesTail = cfr ;
+ member->inDTMFFrames = cfr ;
+ }
+ else
+ {
+ // put the new frame at the head of the list
+ member->inDTMFFrames = cfr ;
+ }
+
+ // increment member frame count
+ member->inDTMFFramesCount++ ;
+
+ ast_mutex_unlock(&member->lock);
+
+ // Everything has gone okay!
+ return 0;
+}
+
+int queue_incoming_frame( struct ast_conf_member* member, struct ast_frame* fr )
+{
+ //
+ // sanity checks
+ //
+
+ // check on frame
+ if ( fr == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to queue null frame\n" ) ;
+ return -1 ;
+ }
+
+ // check on member
+ if ( member == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ;
+ return -1 ;
+ }
+
+ ast_mutex_lock(&member->lock);
+
+ if ( member->inFramesCount > member->inFramesNeeded )
+ {
+ if ( member->inFramesCount > AST_CONF_QUEUE_DROP_THRESHOLD )
+ {
+ struct timeval curr = ast_tvnow();
+
+ // time since last dropped frame
+ long diff = ast_tvdiff_ms(curr, member->last_in_dropped);
+
+ // number of milliseconds which must pass between frame drops
+ // ( 15 frames => -100ms, 10 frames => 400ms, 5 frames => 900ms, 0 frames => 1400ms, etc. )
+ long time_limit = 1000 - ( ( member->inFramesCount - AST_CONF_QUEUE_DROP_THRESHOLD ) * 100 ) ;
+
+ if ( diff >= time_limit )
+ {
+ // count sequential drops
+ member->sequential_drops++ ;
+
+ ast_log(
+ AST_CONF_DEBUG,
+ "dropping frame from input buffer, channel => %s, incoming => %d, outgoing => %d\n",
+ member->channel_name, member->inFramesCount, member->outFramesCount
+ ) ;
+
+ // accounting: count dropped incoming frames
+ member->frames_in_dropped++ ;
+
+ // reset frames since dropped
+ member->since_dropped = 0 ;
+
+ // delete the frame
+ delete_conf_frame( get_incoming_frame( member ) ) ;
+
+ member->last_in_dropped = ast_tvnow();
+ }
+ else
+ {
+/*
+ ast_log(
+ AST_CONF_DEBUG,
+ "input buffer larger than drop threshold, channel => %s, incoming => %d, outgoing => %d\n",
+ member->channel_name, member->inFramesCount, member->outFramesCount
+ ) ;
+*/
+ }
+ }
+ }
+
+ //
+ // if we have to drop frames, we'll drop new frames
+ // because it's easier ( and doesn't matter much anyway ).
+ //
+
+ if ( member->inFramesCount >= AST_CONF_MAX_QUEUE )
+ {
+ // count sequential drops
+ member->sequential_drops++ ;
+
+ ast_log(
+ AST_CONF_DEBUG,
+ "unable to queue incoming frame, channel => %s, incoming => %d, outgoing => %d\n",
+ member->channel_name, member->inFramesCount, member->outFramesCount
+ ) ;
+
+ // accounting: count dropped incoming frames
+ member->frames_in_dropped++ ;
+
+ // reset frames since dropped
+ member->since_dropped = 0 ;
+
+ ast_mutex_unlock(&member->lock);
+ return -1 ;
+ }
+
+ // reset sequential drops
+ member->sequential_drops = 0 ;
+
+ // increment frames since dropped
+ member->since_dropped++ ;
+
+ //
+ // create new conf frame from passed data frame
+ //
+
+ // ( member->inFrames may be null at this point )
+ if (member->inSmoother == NULL ){
+ conf_frame* cfr = create_conf_frame( member, member->inFrames, fr ) ;
+ if ( cfr == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to malloc conf_frame\n" ) ;
+ ast_mutex_unlock(&member->lock);
+ return -1 ;
+ }
+
+ //
+ // add new frame to speaking members incoming frame queue
+ // ( i.e. save this frame data, so we can distribute it in conference_exec later )
+ //
+
+ if ( member->inFrames == NULL ) {
+ member->inFramesTail = cfr ;
+ }
+ member->inFrames = cfr ;
+ member->inFramesCount++ ;
+ } else {
+ //feed frame(fr) into the smoother
+
+ // smoother tmp frame
+ struct ast_frame *sfr;
+ int multiple = 1;
+ int i=0;
+
+#if 0
+ if ( (member->smooth_size_in > 0 ) && (member->smooth_size_in * member->smooth_multiple != fr->datalen) )
+ {
+ ast_log( AST_CONF_DEBUG, "resetting smooth_size_in. old size=> %d, multiple =>%d, datalen=> %d\n", member->smooth_size_in, member->smooth_multiple, fr->datalen );
+ if ( fr->datalen % member->smooth_multiple != 0) {
+ // if datalen not divisible by smooth_multiple, assume we're just getting normal encoding.
+ // ast_log(AST_CONF_DEBUG,"smooth_multiple does not divide datalen. changing smooth size from %d to %d, multiple => 1\n", member->smooth_size_in, fr->datalen);
+ member->smooth_size_in = fr->datalen;
+ member->smooth_multiple = 1;
+ } else {
+ // assume a fixed multiple, so divide into datalen.
+ int newsmooth = fr->datalen / member->smooth_multiple ;
+ // ast_log(AST_CONF_DEBUG,"datalen is divisible by smooth_multiple, changing smooth size from %d to %d\n", member->smooth_size_in, newsmooth);
+ member->smooth_size_in = newsmooth;
+ }
+
+ //free input smoother.
+ if (member->inSmoother != NULL)
+ ast_smoother_free(member->inSmoother);
+
+ //make new input smoother.
+ member->inSmoother = ast_smoother_new(member->smooth_size_in);
+ }
+#endif
+
+ ast_smoother_feed( member->inSmoother, fr );
+ast_log (AST_CONF_DEBUG, "SMOOTH:Feeding frame into inSmoother, timestamp => %ld.%ld\n", fr->delivery.tv_sec, fr->delivery.tv_usec);
+
+ if ( multiple > 1 )
+ fr->samples /= multiple;
+
+ // read smoothed version of frames, add to queue
+ while( ( sfr = ast_smoother_read( member->inSmoother ) ) ){
+
+ ++i;
+ast_log( AST_CONF_DEBUG , "\treading new frame [%d] from smoother, inFramesCount[%d], \n\tsfr->frametype -> %d , sfr->subclass -> %d , sfr->datalen => %d sfr->samples => %d\n", i , member->inFramesCount , sfr->frametype, sfr->subclass, sfr->datalen, sfr->samples);
+ast_log (AST_CONF_DEBUG, "SMOOTH:Reading frame from inSmoother, i=>%d, timestamp => %ld.%ld\n",i, sfr->delivery.tv_sec, sfr->delivery.tv_usec);
+ conf_frame* cfr = create_conf_frame( member, member->inFrames, sfr ) ;
+ if ( cfr == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to malloc conf_frame\n" ) ;
+ ast_mutex_unlock(&member->lock);
+ return -1 ;
+ }
+
+ //
+ // add new frame to speaking members incoming frame queue
+ // ( i.e. save this frame data, so we can distribute it in conference_exec later )
+ //
+
+ if ( member->inFrames == NULL ) {
+ member->inFramesTail = cfr ;
+ }
+ member->inFrames = cfr ;
+ member->inFramesCount++ ;
+ }
+ }
+ ast_mutex_unlock(&member->lock);
+ return 0 ;
+}
+
+//
+// outgoing frame functions
+//
+
+conf_frame* get_outgoing_frame( struct ast_conf_member *member )
+{
+ if ( member == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ;
+ return NULL ;
+ }
+
+ conf_frame* cfr ;
+
+ // ast_log( AST_CONF_DEBUG, "getting member frames, count => %d\n", member->outFramesCount ) ;
+
+ ast_mutex_lock(&member->lock);
+
+ if ( member->outFramesCount > AST_CONF_MIN_QUEUE )
+ {
+ cfr = member->outFramesTail ;
+
+ // if it's the only frame, reset the queu,
+ // else, move the second frame to the front
+ if ( member->outFramesTail == member->outFrames )
+ {
+ member->outFrames = NULL ;
+ member->outFramesTail = NULL ;
+ }
+ else
+ {
+ // move the pointer to the next frame
+ member->outFramesTail = member->outFramesTail->prev ;
+
+ // reset it's 'next' pointer
+ if ( member->outFramesTail != NULL )
+ member->outFramesTail->next = NULL ;
+ }
+
+ // separate the conf frame from the list
+ cfr->next = NULL ;
+ cfr->prev = NULL ;
+
+ // decriment frame count
+ member->outFramesCount-- ;
+ ast_mutex_unlock(&member->lock);
+ return cfr ;
+ }
+ ast_mutex_unlock(&member->lock);
+ return NULL ;
+}
+
+int __queue_outgoing_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery )
+{
+ // accounting: count the number of outgoing frames for this member
+ member->frames_out++ ;
+
+ //
+ // we have to drop frames, so we'll drop new frames
+ // because it's easier ( and doesn't matter much anyway ).
+ //
+ if ( member->outFramesCount >= AST_CONF_MAX_QUEUE )
+ {
+ ast_log(
+ AST_CONF_DEBUG,
+ "unable to queue outgoing frame, channel => %s, incoming => %d, outgoing => %d\n",
+ member->channel_name, member->inFramesCount, member->outFramesCount
+ ) ;
+
+ // accounting: count dropped outgoing frames
+ member->frames_out_dropped++ ;
+ return -1 ;
+ }
+
+ //
+ // create new conf frame from passed data frame
+ //
+
+ conf_frame* cfr = create_conf_frame( member, member->outFrames, fr ) ;
+
+ if ( cfr == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to create new conf frame\n" ) ;
+
+ // accounting: count dropped outgoing frames
+ member->frames_out_dropped++ ;
+ return -1 ;
+ }
+
+ // set delivery timestamp
+ cfr->fr->delivery = delivery ;
+
+ //
+ // add new frame to speaking members incoming frame queue
+ // ( i.e. save this frame data, so we can distribute it in conference_exec later )
+ //
+
+ if ( member->outFrames == NULL ) {
+ member->outFramesTail = cfr ;
+ }
+ member->outFrames = cfr ;
+ member->outFramesCount++ ;
+
+ // return success
+ return 0 ;
+}
+
+int queue_outgoing_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery )
+{
+ // check on frame
+ if ( fr == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to queue null frame\n" ) ;
+ return -1 ;
+ }
+
+ // check on member
+ if ( member == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ;
+ return -1 ;
+ }
+
+ if ( ( member->outPacker == NULL ) && ( member->smooth_multiple > 1 ) && ( member->smooth_size_out > 0 ) ){
+ //ast_log (AST_CONF_DEBUG, "creating outPacker with size => %d \n\t( multiple => %d ) * ( size => %d )\n", member->smooth_multiple * member-> smooth_size_out, member->smooth_multiple , member->smooth_size_out);
+ member->outPacker = ast_packer_new( member->smooth_multiple * member->smooth_size_out);
+ }
+
+ if (member->outPacker == NULL ){
+ return __queue_outgoing_frame( member, fr, delivery ) ;
+ }
+ else
+ {
+ struct ast_frame *sfr;
+ int exitval = 0;
+//ast_log (AST_CONF_DEBUG, "sending fr into outPacker, datalen=>%d, samples=>%d\n",fr->datalen, fr->samples);
+ ast_packer_feed( member->outPacker , fr );
+ while( (sfr = ast_packer_read( member->outPacker ) ) )
+ {
+//ast_log (AST_CONF_DEBUG, "read sfr from outPacker, datalen=>%d, samples=>%d\n",sfr->datalen, sfr->samples);
+ if ( __queue_outgoing_frame( member, sfr, delivery ) == -1 ) {
+ exitval = -1;
+ }
+ }
+
+ return exitval;
+ }
+}
+
+//
+// outgoing frame functions
+//
+
+conf_frame* get_outgoing_video_frame( struct ast_conf_member *member )
+{
+ if ( member == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ;
+ return NULL ;
+ }
+
+ conf_frame* cfr ;
+
+ ast_mutex_lock(&member->lock);
+
+ // ast_log( AST_CONF_DEBUG, "getting member frames, count => %d\n", member->outFramesCount ) ;
+
+ if ( member->outVideoFramesCount > AST_CONF_MIN_QUEUE )
+ {
+ cfr = member->outVideoFramesTail ;
+
+ // if it's the only frame, reset the queu,
+ // else, move the second frame to the front
+ if ( member->outVideoFramesTail == member->outVideoFrames )
+ {
+ member->outVideoFrames = NULL ;
+ member->outVideoFramesTail = NULL ;
+ }
+ else
+ {
+ // move the pointer to the next frame
+ member->outVideoFramesTail = member->outVideoFramesTail->prev ;
+
+ // reset it's 'next' pointer
+ if ( member->outVideoFramesTail != NULL )
+ member->outVideoFramesTail->next = NULL ;
+ }
+
+ // separate the conf frame from the list
+ cfr->next = NULL ;
+ cfr->prev = NULL ;
+
+ // decriment frame count
+ member->outVideoFramesCount-- ;
+ ast_mutex_unlock(&member->lock);
+ return cfr ;
+ }
+
+ ast_mutex_unlock(&member->lock);
+ return NULL ;
+}
+
+
+
+int queue_outgoing_video_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery )
+{
+ // check on frame
+ if ( fr == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to queue null frame\n" ) ;
+ return -1 ;
+ }
+
+ // check on member
+ if ( member == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ;
+ return -1 ;
+ }
+
+ ast_mutex_lock(&member->lock);
+
+ // accounting: count the number of outgoing frames for this member
+ member->video_frames_out++ ;
+
+ //
+ // we have to drop frames, so we'll drop new frames
+ // because it's easier ( and doesn't matter much anyway ).
+ //
+ if ( member->outVideoFramesCount >= AST_CONF_MAX_VIDEO_QUEUE)
+ {
+ ast_log(
+ AST_CONF_DEBUG,
+ "unable to queue outgoing VIDEO frame, channel => %s, incoming => %d, outgoing => %d\n",
+ member->channel_name, member->inVideoFramesCount, member->outVideoFramesCount
+ ) ;
+
+ // accounting: count dropped outgoing frames
+ member->video_frames_out_dropped++ ;
+ ast_mutex_unlock(&member->lock);
+ return -1 ;
+ }
+
+ //
+ // create new conf frame from passed data frame
+ //
+
+ conf_frame* cfr = create_conf_frame( member, member->outVideoFrames, fr ) ;
+
+ if ( cfr == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to create new conf frame\n" ) ;
+
+ // accounting: count dropped outgoing frames
+ member->video_frames_out_dropped++ ;
+ ast_mutex_unlock(&member->lock);
+ return -1 ;
+ }
+
+ // set delivery timestamp
+#ifdef VIDEO_SETTIMESTAMP
+ cfr->fr->delivery = delivery ;
+#else
+ cfr->fr->delivery.tv_sec = 0;
+ cfr->fr->delivery.tv_usec = 0;
+#endif
+ //ast_log (LOG_WARNING,"%d\n",cfr->fr->seqno);
+
+#ifdef RTP_SEQNO_ZERO
+ cfr->fr->seqno = 0;
+#endif
+
+ if ( member->outVideoFrames == NULL )
+ {
+ // this is the first frame in the buffer
+ member->outVideoFramesTail = cfr ;
+ member->outVideoFrames = cfr ;
+ }
+ else
+ {
+ // put the new frame at the head of the list
+ member->outVideoFrames = cfr ;
+ }
+
+ // increment member frame count
+ member->outVideoFramesCount++ ;
+
+ ast_mutex_unlock(&member->lock);
+
+ // return success
+ return 0 ;
+}
+
+conf_frame* get_outgoing_dtmf_frame( struct ast_conf_member *member )
+{
+ if ( member == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ;
+ return NULL ;
+ }
+
+ conf_frame* cfr ;
+
+ // ast_log( AST_CONF_DEBUG, "getting member frames, count => %d\n", member->outFramesCount ) ;
+
+ ast_mutex_lock(&member->lock);
+
+ if ( member->outDTMFFramesCount > AST_CONF_MIN_QUEUE )
+ {
+ cfr = member->outDTMFFramesTail ;
+
+ // if it's the only frame, reset the queu,
+ // else, move the second frame to the front
+ if ( member->outDTMFFramesTail == member->outDTMFFrames )
+ {
+ member->outDTMFFrames = NULL ;
+ member->outDTMFFramesTail = NULL ;
+ }
+ else
+ {
+ // move the pointer to the next frame
+ member->outDTMFFramesTail = member->outDTMFFramesTail->prev ;
+
+ // reset it's 'next' pointer
+ if ( member->outDTMFFramesTail != NULL )
+ member->outDTMFFramesTail->next = NULL ;
+ }
+
+ // separate the conf frame from the list
+ cfr->next = NULL ;
+ cfr->prev = NULL ;
+
+ // decriment frame count
+ member->outDTMFFramesCount-- ;
+ ast_mutex_unlock(&member->lock);
+ return cfr ;
+ }
+ ast_mutex_unlock(&member->lock);
+ return NULL ;
+}
+
+conf_frame* get_outgoing_text_frame( struct ast_conf_member *member )
+{
+ if ( member == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ;
+ return NULL ;
+ }
+
+ conf_frame* cfr ;
+
+ // ast_log( AST_CONF_DEBUG, "getting member frames, count => %d\n", member->outFramesCount ) ;
+
+ ast_mutex_lock(&member->lock);
+
+ if ( member->outTextFramesCount > AST_CONF_MIN_QUEUE )
+ {
+ cfr = member->outTextFramesTail ;
+
+ // if it's the only frame, reset the queu,
+ // else, move the second frame to the front
+ if ( member->outTextFramesTail == member->outTextFrames )
+ {
+ member->outTextFrames = NULL ;
+ member->outTextFramesTail = NULL ;
+ }
+ else
+ {
+ // move the pointer to the next frame
+ member->outTextFramesTail = member->outTextFramesTail->prev ;
+
+ // reset it's 'next' pointer
+ if ( member->outTextFramesTail != NULL )
+ member->outTextFramesTail->next = NULL ;
+ }
+
+ // separate the conf frame from the list
+ cfr->next = NULL ;
+ cfr->prev = NULL ;
+
+ // decriment frame count
+ member->outTextFramesCount-- ;
+ ast_mutex_unlock(&member->lock);
+ return cfr ;
+ }
+ ast_mutex_unlock(&member->lock);
+ return NULL ;
+}
+
+
+int queue_outgoing_dtmf_frame( struct ast_conf_member* member, const struct ast_frame* fr )
+{
+ // check on frame
+ if ( fr == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to queue null frame\n" ) ;
+ return -1 ;
+ }
+
+ // check on member
+ if ( member == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ;
+ return -1 ;
+ }
+
+ ast_mutex_lock(&member->lock);
+
+ // accounting: count the number of outgoing frames for this member
+ member->dtmf_frames_out++ ;
+
+ //
+ // we have to drop frames, so we'll drop new frames
+ // because it's easier ( and doesn't matter much anyway ).
+ //
+ if ( member->outDTMFFramesCount >= AST_CONF_MAX_DTMF_QUEUE)
+ {
+ ast_log(
+ AST_CONF_DEBUG,
+ "unable to queue outgoing DTMF frame, channel => %s, incoming => %d, outgoing => %d\n",
+ member->channel_name, member->inDTMFFramesCount, member->outDTMFFramesCount
+ ) ;
+
+ // accounting: count dropped outgoing frames
+ member->dtmf_frames_out_dropped++ ;
+ ast_mutex_unlock(&member->lock);
+ return -1 ;
+ }
+
+ //
+ // create new conf frame from passed data frame
+ //
+
+ conf_frame* cfr = create_conf_frame( member, member->outDTMFFrames, fr ) ;
+
+ if ( cfr == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to create new conf frame\n" ) ;
+
+ // accounting: count dropped outgoing frames
+ member->dtmf_frames_out_dropped++ ;
+ ast_mutex_unlock(&member->lock);
+ return -1 ;
+ }
+
+#ifdef RTP_SEQNO_ZERO
+ cfr->fr->seqno = 0;
+#endif
+
+ if ( member->outDTMFFrames == NULL )
+ {
+ // this is the first frame in the buffer
+ member->outDTMFFramesTail = cfr ;
+ member->outDTMFFrames = cfr ;
+ }
+ else
+ {
+ // put the new frame at the head of the list
+ member->outDTMFFrames = cfr ;
+ }
+
+ // increment member frame count
+ member->outDTMFFramesCount++ ;
+
+ ast_mutex_unlock(&member->lock);
+ // return success
+ return 0 ;
+}
+
+int queue_outgoing_text_frame( struct ast_conf_member* member, const struct ast_frame* fr)
+{
+ // check on frame
+ if ( fr == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to queue null frame\n" ) ;
+ return -1 ;
+ }
+
+ // check on member
+ if ( member == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ;
+ return -1 ;
+ }
+
+ ast_mutex_lock(&member->lock);
+
+ // accounting: count the number of outgoing frames for this member
+ member->text_frames_out++ ;
+
+ //
+ // we have to drop frames, so we'll drop new frames
+ // because it's easier ( and doesn't matter much anyway ).
+ //
+ if ( member->outTextFramesCount >= AST_CONF_MAX_TEXT_QUEUE)
+ {
+ ast_log(
+ AST_CONF_DEBUG,
+ "unable to queue outgoing text frame, channel => %s, incoming => %d, outgoing => %d\n",
+ member->channel_name, member->inTextFramesCount, member->outTextFramesCount
+ ) ;
+
+ // accounting: count dropped outgoing frames
+ member->text_frames_out_dropped++ ;
+ ast_mutex_unlock(&member->lock);
+ return -1 ;
+ }
+
+ //
+ // create new conf frame from passed data frame
+ //
+
+ conf_frame* cfr = create_conf_frame( member, member->outTextFrames, fr ) ;
+
+ if ( cfr == NULL )
+ {
+ ast_log( LOG_ERROR, "unable to create new conf frame\n" ) ;
+
+ // accounting: count dropped outgoing frames
+ member->text_frames_out_dropped++ ;
+ ast_mutex_unlock(&member->lock);
+ return -1 ;
+ }
+
+#ifdef RTP_SEQNO_ZERO
+ cfr->fr->seqno = 0;
+#endif
+
+ if ( member->outTextFrames == NULL )
+ {
+ // this is the first frame in the buffer
+ member->outTextFramesTail = cfr ;
+ member->outTextFrames = cfr ;
+ }
+ else
+ {
+ // put the new frame at the head of the list
+ member->outTextFrames = cfr ;
+ }
+
+ // increment member frame count
+ member->outTextFramesCount++ ;
+
+ ast_mutex_unlock(&member->lock);
+ // return success
+ return 0 ;
+}
+
+
+//
+// manager functions
+//
+
+void send_state_change_notifications( struct ast_conf_member* member )
+{
+ // ast_log( AST_CONF_DEBUG, "sending state change notification\n" ) ;
+
+ // loop through list of members, sending state changes
+ while ( member != NULL )
+ {
+ // has the state changed since last time through this loop?
+ if ( member->speaking_state_notify )
+ {
+ manager_event(
+ EVENT_FLAG_CALL,
+ "ConferenceState",
+ "Channel: %s\r\n"
+ "State: %s\r\n",
+ member->channel_name,
+ ( ( member->speaking_state == 1 ) ? "speaking" : "silent" )
+ ) ;
+
+ ast_log( AST_CONF_DEBUG, "member state changed, channel => %s, state => %d, incoming => %d, outgoing => %d\n",
+ member->channel_name, member->speaking_state, member->inFramesCount, member->outFramesCount ) ;
+
+ member->speaking_state_notify = 0;
+ }
+
+ // move the pointer to the next member
+ member = member->next ;
+ }
+
+ return ;
+}
+
+//
+// ast_packer, adapted from ast_smoother
+// pack multiple frames together into one packet on the wire.
+//
+
+#define PACKER_SIZE 8000
+#define PACKER_QUEUE 10 // store at most 10 complete packets in the queue
+
+struct ast_packer {
+ int framesize; // number of frames per packet on the wire.
+ int size;
+ int packet_index;
+ int format;
+ int readdata;
+ int optimizablestream;
+ int flags;
+ float samplesperbyte;
+ struct ast_frame f;
+ struct timeval delivery;
+ char data[PACKER_SIZE];
+ char framedata[PACKER_SIZE + AST_FRIENDLY_OFFSET];
+ int samples;
+ int sample_queue[PACKER_QUEUE];
+ int len_queue[PACKER_QUEUE];
+ struct ast_frame *opt;
+ int len;
+};
+
+void ast_packer_reset(struct ast_packer *s, int framesize)
+{
+ memset(s, 0, sizeof(struct ast_packer));
+ s->framesize = framesize;
+ s->packet_index=0;
+ s->len=0;
+}
+
+struct ast_packer *ast_packer_new(int framesize)
+{
+ struct ast_packer *s;
+ if (framesize < 1)
+ return NULL;
+ s = malloc(sizeof(struct ast_packer));
+ if (s)
+ ast_packer_reset(s, framesize);
+ return s;
+}
+
+int ast_packer_get_flags(struct ast_packer *s)
+{
+ return s->flags;
+}
+
+void ast_packer_set_flags(struct ast_packer *s, int flags)
+{
+ s->flags = flags;
+}
+
+int ast_packer_feed(struct ast_packer *s, const struct ast_frame *f)
+{
+ if (f->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Huh? Can't pack a non-voice frame!\n");
+ return -1;
+ }
+ if (!s->format) {
+ s->format = f->subclass;
+ s->samples=0;
+ } else if (s->format != f->subclass) {
+ ast_log(LOG_WARNING, "Packer was working on %d format frames, now trying to feed %d?\n", s->format, f->subclass);
+ return -1;
+ }
+ if (s->len + f->datalen > PACKER_SIZE) {
+ ast_log(LOG_WARNING, "Out of packer space\n");
+ return -1;
+ }
+ if (s->packet_index >= PACKER_QUEUE ){
+ ast_log(LOG_WARNING, "Out of packer queue space\n");
+ return -1;
+ }
+
+ memcpy(s->data + s->len, f->data, f->datalen);
+ /* If either side is empty, reset the delivery time */
+ if (!s->len || (!f->delivery.tv_sec && !f->delivery.tv_usec) ||
+ (!s->delivery.tv_sec && !s->delivery.tv_usec))
+ s->delivery = f->delivery;
+ s->len += f->datalen;
+//packer stuff
+ s->len_queue[s->packet_index] += f->datalen;
+ s->sample_queue[s->packet_index] += f->samples;
+ s->samples += f->samples;
+
+ if (s->samples > s->framesize )
+ ++s->packet_index;
+
+ return 0;
+}
+
+struct ast_frame *ast_packer_read(struct ast_packer *s)
+{
+ struct ast_frame *opt;
+ int len;
+ /* IF we have an optimization frame, send it */
+ if (s->opt) {
+ opt = s->opt;
+ s->opt = NULL;
+ return opt;
+ }
+
+ /* Make sure we have enough data */
+ if (s->samples < s->framesize ){
+ return NULL;
+ }
+ len = s->len_queue[0];
+ if (len > s->len)
+ len = s->len;
+ /* Make frame */
+ s->f.frametype = AST_FRAME_VOICE;
+ s->f.subclass = s->format;
+ s->f.data = s->framedata + AST_FRIENDLY_OFFSET;
+ s->f.offset = AST_FRIENDLY_OFFSET;
+ s->f.datalen = len;
+ s->f.samples = s->sample_queue[0];
+ s->f.delivery = s->delivery;
+ /* Fill Data */
+ memcpy(s->f.data, s->data, len);
+ s->len -= len;
+ /* Move remaining data to the front if applicable */
+ if (s->len) {
+ /* In principle this should all be fine because if we are sending
+ G.729 VAD, the next timestamp will take over anyawy */
+ memmove(s->data, s->data + len, s->len);
+ if (s->delivery.tv_sec || s->delivery.tv_usec) {
+ /* If we have delivery time, increment it, otherwise, leave it at 0 */
+ s->delivery.tv_sec += s->sample_queue[0] / 8000.0;
+ s->delivery.tv_usec += (((int)(s->sample_queue[0])) % 8000) * 125;
+ if (s->delivery.tv_usec > 1000000) {
+ s->delivery.tv_usec -= 1000000;
+ s->delivery.tv_sec += 1;
+ }
+ }
+ }
+ int j;
+ s->samples -= s->sample_queue[0];
+ if( s->packet_index > 0 ){
+ for (j=0; j<s->packet_index -1 ; j++){
+ s->len_queue[j]=s->len_queue[j+1];
+ s->sample_queue[j]=s->sample_queue[j+1];
+ }
+ s->len_queue[s->packet_index]=0;
+ s->sample_queue[s->packet_index]=0;
+ s->packet_index--;
+ } else {
+ s->len_queue[0]=0;
+ s->sample_queue[0]=0;
+ }
+
+
+ /* Return frame */
+ return &s->f;
+}
+
+void ast_packer_free(struct ast_packer *s)
+{
+ free(s);
+}
+
+int queue_frame_for_listener(
+ struct ast_conference* conf,
+ struct ast_conf_member* member,
+ conf_frame* frame
+)
+{
+ //
+ // check inputs
+ //
+
+ if ( conf == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to queue listener frame with null conference\n" ) ;
+ return -1 ;
+ }
+
+ if ( member == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to queue listener frame with null member\n" ) ;
+ return -1 ;
+ }
+
+ //
+ // loop over spoken frames looking for member's appropriate match
+ //
+
+ short found_flag = 0 ;
+ struct ast_frame* qf ;
+
+ for ( ; frame != NULL ; frame = frame->next )
+ {
+ // we're looking for a null or matching member
+ if ( frame->member != NULL && frame->member != member )
+ continue ;
+
+ if ( frame->fr == NULL )
+ {
+ ast_log( LOG_WARNING, "unknown error queueing frame for listener, frame->fr == NULL\n" ) ;
+ continue ;
+ }
+
+ // first, try for a pre-converted frame
+ qf = frame->converted[ member->write_format_index ] ;
+
+ // convert ( and store ) the frame
+ if ( qf == NULL )
+ {
+ // make a copy of the slinear version of the frame
+ qf = ast_frdup( frame->fr ) ;
+
+ if ( qf == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to duplicate frame\n" ) ;
+ continue ;
+ }
+
+ // convert using the conference's translation path
+ qf = convert_frame_from_slinear( conf->from_slinear_paths[ member->write_format_index ], qf ) ;
+
+ // store the converted frame
+ // ( the frame will be free'd next time through the loop )
+ frame->converted[ member->write_format_index ] = qf ;
+ }
+
+ if ( qf != NULL )
+ {
+ // duplicate the frame before queue'ing it
+ // ( since this member doesn't own this _shared_ frame )
+ // qf = ast_frdup( qf ) ;
+
+
+
+ if ( queue_outgoing_frame( member, qf, conf->delivery_time ) != 0 )
+ {
+ // free the new frame if it couldn't be queue'd
+ // XXX NEILS - WOULD BE FREED IN CLEANUPast_frfree( qf ) ;
+ //qf = NULL ;
+ }
+ }
+ else
+ {
+ ast_log( LOG_WARNING, "unable to translate outgoing listener frame, channel => %s\n", member->channel_name ) ;
+ }
+
+ // set found flag
+ found_flag = 1 ;
+
+ // break from for loop
+ break ;
+ }
+
+ // queue a silent frame
+ if ( found_flag == 0 )
+ queue_silent_frame( conf, member ) ;
+
+ return 0 ;
+}
+
+
+int queue_frame_for_speaker(
+ struct ast_conference* conf,
+ struct ast_conf_member* member,
+ conf_frame* frame
+)
+{
+ //
+ // check inputs
+ //
+
+ if ( conf == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to queue speaker frame with null conference\n" ) ;
+ return -1 ;
+ }
+
+ if ( member == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to queue speaker frame with null member\n" ) ;
+ return -1 ;
+ }
+
+ //
+ // loop over spoken frames looking for member's appropriate match
+ //
+
+ short found_flag = 0 ;
+ struct ast_frame* qf ;
+
+ for ( ; frame != NULL ; frame = frame->next )
+ {
+ if ( frame->member != member )
+ {
+ continue ;
+ }
+
+ if ( frame->fr == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to queue speaker frame with null data\n" ) ;
+ continue ;
+ }
+
+ //
+ // convert and queue frame
+ //
+
+ // short-cut pointer to the ast_frame
+ qf = frame->fr ;
+
+ if ( qf->subclass == member->write_format )
+ {
+ // frame is already in correct format, so just queue it
+
+ queue_outgoing_frame( member, qf, conf->delivery_time ) ;
+ }
+ else
+ {
+ //
+ // convert frame to member's write format
+ // ( calling ast_frdup() to make sure the translator's copy sticks around )
+ //
+ qf = convert_frame_from_slinear( member->from_slinear, ast_frdup( qf ) ) ;
+
+ if ( qf != NULL )
+ {
+ // queue frame
+ queue_outgoing_frame( member, qf, conf->delivery_time ) ;
+
+ // free frame ( the translator's copy )
+ ast_frfree( qf ) ;
+ }
+ else
+ {
+ ast_log( LOG_WARNING, "unable to translate outgoing speaker frame, channel => %s\n", member->channel_name ) ;
+ }
+ }
+
+ // set found flag
+ found_flag = 1 ;
+
+ // we found the frame, skip to the next member
+ break ;
+ }
+
+ // queue a silent frame
+ if ( found_flag == 0 )
+ queue_silent_frame( conf, member ) ;
+
+ return 0 ;
+}
+
+
+int queue_silent_frame(
+ struct ast_conference* conf,
+ struct ast_conf_member* member
+)
+{
+ int c;
+#ifdef APP_CONFERENCE_DEBUG
+ //
+ // check inputs
+ //
+
+ if ( conf == NULL )
+ {
+ ast_log( AST_CONF_DEBUG, "unable to queue silent frame for null conference\n" ) ;
+ return -1 ;
+ }
+
+ if ( member == NULL )
+ {
+ ast_log( AST_CONF_DEBUG, "unable to queue silent frame for null member\n" ) ;
+ return -1 ;
+ }
+#endif // APP_CONFERENCE_DEBUG
+
+ //
+ // initialize static variables
+ //
+
+ static conf_frame* silent_frame = NULL ;
+ static struct ast_frame* qf = NULL ;
+
+ if ( silent_frame == NULL )
+ {
+ if ( ( silent_frame = get_silent_frame() ) == NULL )
+ {
+ ast_log( LOG_WARNING, "unable to initialize static silent frame\n" ) ;
+ return -1 ;
+ }
+ }
+
+
+ // get the appropriate silent frame
+ qf = silent_frame->converted[ member->write_format_index ] ;
+
+ if ( qf == NULL )
+ {
+ //
+ // we need to do this to avoid echo on the speaker's line.
+ // translators seem to be single-purpose, i.e. they
+ // can't be used simultaneously for multiple audio streams
+ //
+
+ struct ast_trans_pvt* trans = ast_translator_build_path( member->write_format, AST_FORMAT_SLINEAR ) ;
+
+ if ( trans != NULL )
+ {
+ // attempt ( five times ) to get a silent frame
+ // to make sure we provice the translator with enough data
+ for ( c = 0 ; c < 5 ; ++c )
+ {
+ // translate the frame
+ qf = ast_translate( trans, silent_frame->fr, 0 ) ;
+
+ // break if we get a frame
+ if ( qf != NULL ) break ;
+ }
+
+ if ( qf != NULL )
+ {
+ // isolate the frame so we can keep it around after trans is free'd
+ qf = ast_frisolate( qf ) ;
+
+ // cache the new, isolated frame
+ silent_frame->converted[ member->write_format_index ] = qf ;
+ }
+
+ ast_translator_free_path( trans ) ;
+ }
+ }
+
+ //
+ // queue the frame, if it's not null,
+ // otherwise there was an error
+ //
+ if ( qf != NULL )
+ {
+ queue_outgoing_frame( member, qf, conf->delivery_time ) ;
+ }
+ else
+ {
+ ast_log( LOG_ERROR, "unable to translate outgoing silent frame, channel => %s\n", member->channel_name ) ;
+ }
+
+ return 0 ;
+}
+
+
+
+void member_process_outgoing_frames(struct ast_conference* conf,
+ struct ast_conf_member *member,
+ struct conf_frame *send_frames)
+{
+ ast_mutex_lock(&member->lock);
+
+ // skip members that are not ready
+ if ( member->ready_for_outgoing == 0 )
+ {
+ ast_mutex_unlock(&member->lock);
+ return ;
+ }
+
+ // skip no receive audio clients
+ if ( member->norecv_audio )
+ {
+ ast_mutex_unlock(&member->lock);
+ return;
+ }
+
+ if ( member->local_speaking_state == 0 )
+ {
+ // queue listener frame
+ queue_frame_for_listener( conf, member, send_frames ) ;
+ }
+ else
+ {
+ // queue speaker frame
+ queue_frame_for_speaker( conf, member, send_frames ) ;
+ }
+ ast_mutex_unlock(&member->lock);
+}
+
+// Functions that will increase and decrease speaker_count in a secure way, locking the member mutex if required
+// Will also set speaking_state flag.
+// Returns the previous speaking state
+int increment_speaker_count(struct ast_conf_member *member, int lock)
+{
+ int old_state;
+
+ if ( lock )
+ ast_mutex_lock(&member->lock);
+
+ old_state = member->speaking_state;
+ member->speaker_count++;
+ member->speaking_state = 1;
+
+ ast_log(AST_CONF_DEBUG, "Increment speaker count: id=%d, count=%d\n", member->id, member->speaker_count);
+
+ // If this is a state change, update the timestamp
+ if ( old_state == 0 )
+ {
+ member->speaking_state_notify = 1;
+ member->last_state_change = ast_tvnow();
+ }
+
+ if ( lock )
+ ast_mutex_unlock(&member->lock);
+
+ return old_state;
+}
+
+int decrement_speaker_count(struct ast_conf_member *member, int lock)
+{
+ int old_state;
+
+ if ( lock )
+ ast_mutex_lock(&member->lock);
+
+ old_state = member->speaking_state;
+ if ( member->speaker_count > 0 )
+ member->speaker_count--;
+ if ( member->speaker_count == 0 )
+ member->speaking_state = 0;
+
+ ast_log(AST_CONF_DEBUG, "Decrement speaker count: id=%d, count=%d\n", member->id, member->speaker_count);
+
+ // If this is a state change, update the timestamp
+ if ( old_state == 1 && member->speaking_state == 0 )
+ {
+ member->speaking_state_notify = 1;
+ member->last_state_change = ast_tvnow();
+ }
+
+ if ( lock )
+ ast_mutex_unlock(&member->lock);
+
+ return old_state;
+}
+
+void member_process_spoken_frames(struct ast_conference* conf,
+ struct ast_conf_member *member,
+ struct conf_frame **spoken_frames,
+ long time_diff,
+ int *listener_count,
+ int *speaker_count
+ )
+{
+ struct conf_frame *cfr;
+
+ // acquire member mutex
+ TIMELOG(ast_mutex_lock( &member->lock ),1,"conf thread member lock") ;
+
+ // check for dead members
+ if ( member->remove_flag == 1 )
+ {
+ // If this member is the default video source for the conference, then change the default to -1
+ if ( member->id == conf->default_video_source_id )
+ conf->default_video_source_id = -1;
+
+ if (conf->debug_flag)
+ {
+ ast_log( LOG_NOTICE, "found member slated for removal, channel => %s\n", member->channel_name ) ;
+ }
+ remove_member( member, conf ) ;
+ return;
+ }
+
+ // tell member the number of frames we're going to need ( used to help dropping algorithm )
+ member->inFramesNeeded = ( time_diff / AST_CONF_FRAME_INTERVAL ) - 1 ;
+
+ // !!! TESTING !!!
+ if (
+ conf->debug_flag == 1
+ && member->inFramesNeeded > 0
+ )
+ {
+ ast_log( AST_CONF_DEBUG, "channel => %s, inFramesNeeded => %d, inFramesCount => %d\n",
+ member->channel_name, member->inFramesNeeded, member->inFramesCount ) ;
+ }
+
+ // non-listener member should have frames,
+ // unless silence detection dropped them
+ cfr = get_incoming_frame( member ) ;
+
+ // handle retrieved frames
+ if ( cfr == NULL || cfr->fr == NULL )
+ {
+ // Decrement speaker count for us and for driven members
+ // This happens only for the first missed frame, since we want to
+ // decrement only on state transitions
+ if ( member->local_speaking_state == 1 )
+ {
+ decrement_speaker_count(member, 0);
+ member->local_speaking_state = 0;
+ // If we're driving another member, decrement its speaker count as well
+ if ( member->driven_member != NULL )
+ decrement_speaker_count(member->driven_member, 1);
+ }
+
+ // count the listeners
+ (*listener_count)++ ;
+ }
+ else
+ {
+ // append the frame to the list of spoken frames
+ if ( *spoken_frames != NULL )
+ {
+ // add new frame to end of list
+ cfr->next = *spoken_frames ;
+ (*spoken_frames)->prev = cfr ;
+ }
+
+ // point the list at the new frame
+ *spoken_frames = cfr ;
+
+ // Increment speaker count for us and for driven members
+ // This happens only on the first received frame, since we want to
+ // increment only on state transitions
+ if ( member->local_speaking_state == 0 )
+ {
+ increment_speaker_count(member, 0);
+ member->local_speaking_state = 1;
+
+ // If we're driving another member, increment its speaker count as well
+ if ( member->driven_member != NULL )
+ increment_speaker_count(member->driven_member, 1);
+ }
+
+ // count the speakers
+ (*speaker_count)++ ;
+ }
+
+ // release member mutex
+ ast_mutex_unlock( &member->lock ) ;
+
+ return;
+}
diff --git a/apps/conference/member.h b/apps/conference/member.h
new file mode 100644
index 0000000..f4cae3f
--- /dev/null
+++ b/apps/conference/member.h
@@ -0,0 +1,315 @@
+
+// $Id: member.h 872 2007-03-05 23:43:10Z sbalea $
+
+/*
+ * app_conference
+ *
+ * A channel independent conference application for Asterisk
+ *
+ * Copyright (C) 2002, 2003 Junghanns.NET GmbH
+ * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
+ * Copyright (C) 2005, 2006 HorizonWimba, Inc.
+ * Copyright (C) 2007 Wimba, Inc.
+ *
+ * Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
+ *
+ * Video Conferencing support added by
+ * Neil Stratford <neils@vipadia.com>
+ * Copyright (C) 2005, 2005 Vipadia Limited
+ *
+ * VAD driven video conferencing, text message support
+ * and miscellaneous enhancements added by
+ * Mihai Balea <mihai at hates dot ms>
+ *
+ * This program may be modified and distributed under the
+ * terms of the GNU General Public License. You should have received
+ * a copy of the GNU General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc.
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _APP_CONF_MEMBER_H
+#define _APP_CONF_MEMBER_H
+
+//
+// includes
+//
+
+#include "app_conference.h"
+#include "common.h"
+
+//
+// struct declarations
+//
+
+struct ast_conf_soundq
+{
+ char name[256];
+ struct ast_filestream *stream; // the stream
+ int muted; // should incoming audio be muted while we play?
+ struct ast_conf_soundq *next;
+};
+
+struct ast_conf_member
+{
+ ast_mutex_t lock ; // member data mutex
+
+ struct ast_channel* chan ; // member's channel
+ char* channel_name ; // member's channel name
+
+ // values passed to create_member () via *data
+ int priority ; // highest priority gets the channel
+ char* flags ; // raw member-type flags
+ char type ; // L = ListenOnly, M = Moderator, S = Standard (Listen/Talk)
+ char* conf_name ; // name of the conference that own this member
+
+ char *callerid;
+ char *callername;
+
+ // voice flags
+ int vad_flag;
+ int denoise_flag;
+ int agc_flag;
+ int via_telephone;
+
+ // video conference params
+ int id;
+ int initial_id;
+ int req_id;
+
+ // muting options - this member will not be heard/seen
+ int mute_audio;
+ int mute_video;
+
+ // this member will not hear/see
+ int norecv_audio;
+ int norecv_video;
+
+ // this member does not have a camera
+ int no_camera;
+
+ // is this person a moderator?
+ int ismoderator;
+
+ // determine by flags and channel name
+ char connection_type ; // T = telephone, X = iaxclient, S = sip
+
+ // vad voice probability thresholds
+ float vad_prob_start ;
+ float vad_prob_continue ;
+
+ // ready flag
+ short ready_for_outgoing ;
+
+ // input frame queue
+ conf_frame* inFrames ;
+ conf_frame* inFramesTail ;
+ unsigned int inFramesCount ;
+ conf_frame* inVideoFrames ;
+ conf_frame* inVideoFramesTail ;
+ unsigned int inVideoFramesCount ;
+ conf_frame* inDTMFFrames ;
+ conf_frame* inDTMFFramesTail ;
+ unsigned int inDTMFFramesCount ;
+ conf_frame* inTextFrames ;
+ conf_frame* inTextFramesTail ;
+ unsigned int inTextFramesCount ;
+
+
+ // input/output smoother
+ struct ast_smoother *inSmoother;
+ struct ast_packer *outPacker;
+ int smooth_size_in;
+ int smooth_size_out;
+ int smooth_multiple;
+
+ // frames needed by conference_exec
+ unsigned int inFramesNeeded ;
+ unsigned int inVideoFramesNeeded ;
+
+ // used when caching last frame
+ conf_frame* inFramesLast ;
+ unsigned int inFramesRepeatLast ;
+ unsigned short okayToCacheLast ;
+
+ // LL output frame queue
+ conf_frame* outFrames ;
+ conf_frame* outFramesTail ;
+ unsigned int outFramesCount ;
+ conf_frame* outVideoFrames ;
+ conf_frame* outVideoFramesTail ;
+ unsigned int outVideoFramesCount ;
+ conf_frame* outDTMFFrames ;
+ conf_frame* outDTMFFramesTail ;
+ unsigned int outDTMFFramesCount ;
+ conf_frame* outTextFrames ;
+ conf_frame* outTextFramesTail ;
+ unsigned int outTextFramesCount ;
+
+ // LL video switched flag
+ short conference;
+
+ // switch video by VAD?
+ short vad_switch;
+ // switch by dtmf?
+ short dtmf_switch;
+ // relay dtmf to manager?
+ short dtmf_relay;
+ // initial nat delay flag
+ short first_frame_received;
+ // does text messages?
+ short does_text;
+
+
+ // time we last dropped a frame
+ struct timeval last_in_dropped ;
+ struct timeval last_out_dropped ;
+
+ // ( not currently used )
+ // int samplesperframe ;
+
+ // used for determining need to mix frames
+ // and for management interface notification
+ // and for VAD based video switching
+ short speaking_state_notify ;
+ short speaking_state ; // This flag will be true if this member or any of its drivers is speaking
+ short local_speaking_state; // This flag will be true only if this member is speaking
+ struct timeval last_state_change;
+ int speaker_count; // Number of drivers (including this member) that are speaking
+
+ // pointer to next member in single-linked list
+ struct ast_conf_member* next ;
+
+ // accounting values
+ unsigned long frames_in ;
+ unsigned long frames_in_dropped ;
+ unsigned long frames_out ;
+ unsigned long frames_out_dropped ;
+
+ unsigned long video_frames_in ;
+ unsigned long video_frames_in_dropped ;
+ unsigned long video_frames_out ;
+ unsigned long video_frames_out_dropped ;
+
+ unsigned long dtmf_frames_in ;
+ unsigned long dtmf_frames_in_dropped ;
+ unsigned long dtmf_frames_out ;
+ unsigned long dtmf_frames_out_dropped ;
+
+ unsigned long text_frames_in ;
+ unsigned long text_frames_in_dropped ;
+ unsigned long text_frames_out ;
+ unsigned long text_frames_out_dropped ;
+
+ // for counting sequentially dropped frames
+ unsigned int sequential_drops ;
+ unsigned long since_dropped ;
+
+ // start time
+ struct timeval time_entered ;
+ struct timeval lastsent_timeval ;
+
+ // flag indicating we should remove this member
+ short remove_flag ;
+ short kick_flag ;
+
+#if ( SILDET == 2 )
+ // pointer to speex preprocessor dsp
+ SpeexPreprocessState *dsp ;
+ // number of frames to ignore speex_preprocess()
+ int ignore_speex_count;
+#else
+ // placeholder when preprocessing is not enabled
+ void* dsp ;
+#endif
+
+ // audio format this member is using
+ int write_format ;
+ int read_format ;
+
+ int write_format_index ;
+ int read_format_index ;
+
+ // member frame translators
+ struct ast_trans_pvt* to_slinear ;
+ struct ast_trans_pvt* from_slinear ;
+
+ // For playing sounds
+ struct ast_conf_soundq *soundq;
+ struct ast_conf_soundq *videoq;
+
+ // Enter/leave sounds
+ char * enter_snd;
+ char * leave_snd;
+
+ // Pointer to another member that will be driven from this member's audio
+ struct ast_conf_member *driven_member;
+} ;
+
+struct conf_member
+{
+ struct ast_conf_member* realmember ;
+ struct conf_member* next ;
+} ;
+
+//
+// function declarations
+//
+
+int member_exec( struct ast_channel* chan, void* data ) ;
+
+struct ast_conf_member* check_active_video( int id, struct ast_conference *conf );
+
+struct ast_conf_member* create_member( struct ast_channel* chan, const char* data ) ;
+struct ast_conf_member* delete_member( struct ast_conf_member* member ) ;
+
+// incoming queue
+int queue_incoming_frame( struct ast_conf_member* member, struct ast_frame* fr ) ;
+int queue_incoming_video_frame( struct ast_conf_member* member, const struct ast_frame* fr ) ;
+int queue_incoming_dtmf_frame( struct ast_conf_member* member, const struct ast_frame* fr ) ;
+conf_frame* get_incoming_frame( struct ast_conf_member* member ) ;
+conf_frame* get_incoming_video_frame( struct ast_conf_member* member ) ;
+conf_frame* get_incoming_dtmf_frame( struct ast_conf_member* member ) ;
+
+// outgoing queue
+int queue_outgoing_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery ) ;
+int __queue_outgoing_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery ) ;
+conf_frame* get_outgoing_frame( struct ast_conf_member* member ) ;
+
+int queue_outgoing_video_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery ) ;
+conf_frame* get_outgoing_video_frame( struct ast_conf_member* member ) ;
+int queue_outgoing_dtmf_frame( struct ast_conf_member* member, const struct ast_frame* fr ) ;
+int queue_outgoing_text_frame( struct ast_conf_member* member, const struct ast_frame* fr ) ;
+conf_frame* get_outgoing_dtmf_frame( struct ast_conf_member* member ) ;
+conf_frame* get_outgoing_text_frame( struct ast_conf_member* member ) ;
+
+void send_state_change_notifications( struct ast_conf_member* member ) ;
+
+int increment_speaker_count(struct ast_conf_member *member, int lock);
+int decrement_speaker_count(struct ast_conf_member *member, int lock);
+
+void member_process_spoken_frames(struct ast_conference* conf,
+ struct ast_conf_member *member,
+ struct conf_frame **spoken_frames,
+ long time_diff,
+ int *listener_count,
+ int *speaker_count);
+
+void member_process_outgoing_frames(struct ast_conference* conf,
+ struct ast_conf_member *member,
+ struct conf_frame *send_frames);
+
+//
+// packer functions
+//
+
+struct ast_packer;
+
+extern struct ast_packer *ast_packer_new(int bytes);
+extern void ast_packer_set_flags(struct ast_packer *packer, int flags);
+extern int ast_packer_get_flags(struct ast_packer *packer);
+extern void ast_packer_free(struct ast_packer *s);
+extern void ast_packer_reset(struct ast_packer *s, int bytes);
+extern int ast_packer_feed(struct ast_packer *s, const struct ast_frame *f);
+extern struct ast_frame *ast_packer_read(struct ast_packer *s);
+#endif
--
1.5.3.4