You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sems/AmConference.cpp

465 lines
14 KiB

/*
* $Id: AmConference.cpp,v 1.1.2.4 2005/08/31 13:54:29 rco Exp $
*
* Copyright (C) 2002-2003 Fhg Fokus
*
* This file is part of sems, a free SIP media server.
*
* sems 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
*
* For a license to use the ser software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* sems 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
*/
#include "AmConference.h"
#include "AmConferenceStatus.h"
#include "AmSession.h"
#include "AmMultiPartyMixerSocket.h"
#include <utility>
using std::make_pair;
/* ----- audio mixer----------------------- */
AmConference* AmConference::_instance=0;
AmConference* AmConference::instance()
{
if(!_instance)
_instance = new AmConference();
return _instance;
}
AmConference::AmConference()
: runcond(true)
{
}
AmConference::~AmConference() {
}
void AmConference::run() {
while (runcond.get()) {
// FIXME check sockets/conferences for inactive/single user ???
sleep(5);
}
}
void AmConference::on_stop() {
}
AmConference::socketpair* AmConference::find_socketpair(string c_id) {
map<string, socketpair >::iterator it = mixer_sockets.find(c_id);
if (it != mixer_sockets.end())
return &(it->second);
else
return 0;
}
int AmConference::conference_connect_sock(AmSession* session, string conf_id, bool send, bool receive, int options,
AmMultiPartyMixerSocket** play_sock, AmMultiPartyMixerSocket** rec_sock) {
*play_sock = *rec_sock = 0;
AmConferenceStatus* conf_status = AmConferenceStatusContainer::instance()->get_status(conf_id);
mixer_sockets_mut.lock();
// check if already connected to that conference
if (find_socketpair(make_id(session, conf_id))) {
mixer_sockets_mut.unlock();
ERROR("already connected to conference '%s'\n", conf_id.c_str());
return CONFERENCE_ERR_ALREADY_CONNECTED;
}
if (!conf_status) {
if (!(options & CONFERENCE_OPTIONS_CREATE_CONF)) {
mixer_sockets_mut.unlock();
DBG("conference '%s' does not exist and shall not be created.\n", conf_id.c_str());
return CONFERENCE_ERR_NOTEXIST;
}
conf_status = AmConferenceStatusContainer::instance()->add_status(conf_id);
if (!conf_status) {
mixer_sockets_mut.unlock();
ERROR("could not create new conference status\n");
return CONFERENCE_ERR_CREATESTATUS;
}
}
unsigned int channel = conf_status->add_channel(session);
if (send)
*play_sock = new AmMultiPartyMixerSocket(conf_status->get_mixer(), AmMultiPartyMixerSocket::Play, channel);
if (receive)
*rec_sock = new AmMultiPartyMixerSocket(conf_status->get_mixer(), AmMultiPartyMixerSocket::Record, channel);
mixer_sockets[make_id(session, conf_id)] = make_pair(*play_sock, *rec_sock);
mixer_sockets_mut.unlock();
return 0; // ok
}
// bool send : send to conference
// bool send : receive from conference (not rcv RTP!)
int AmConference::conference_connect(AmSession* session, string conf_id, bool send, bool receive, int options)
{
if (!send && !receive) {
ERROR("connect must either send or receive or both.\n");
return CONFERENCE_ERR_CONNTYPE;
}
AmMultiPartyMixerSocket* play_sock = 0;
AmMultiPartyMixerSocket* rec_sock = 0;
if (int errcode = conference_connect_sock(session, conf_id, send, receive, options, &play_sock, &rec_sock))
return errcode; // error
if (send)
session->setOutput(play_sock);
if (receive)
session->setInput(rec_sock);
return 0;
}
void AmConference::conference_addToPlaylist(AmSession* session, string conf_id,
AmAudio* src, AmMultiPartyMixerSocket::Type type,
AmMultiPartyMixerSocket::PlayMode mode,
AmMultiPartyMixerSocket::Priority priority) {
if (!src) {
ERROR("called with empty src!\n");
return;
}
mixer_sockets_mut.lock();
map<string, socketpair >::iterator it = mixer_sockets.find(make_id(session, conf_id));
if (it == mixer_sockets.end()) {
mixer_sockets_mut.unlock();
ERROR("session/conf %s not found in mixer socket list.\n", make_id(session, conf_id).c_str());
return;
}
socketpair sockets = it->second;
AmMultiPartyMixerSocket* dst_sock;
if (type == AmMultiPartyMixerSocket::Play)
dst_sock = get_play_sock(sockets);
else
dst_sock = get_rec_sock(sockets);
dst_sock->addToPlaylist(src, mode, priority);
mixer_sockets_mut.unlock();
}
void AmConference::conference_clearPlaylist(AmSession* session, string conf_id,
AmMultiPartyMixerSocket::Type type,
AmMultiPartyMixerSocket::PlayMode mode) {
mixer_sockets_mut.lock();
map<string, socketpair >::iterator it = mixer_sockets.find(make_id(session, conf_id));
if (it == mixer_sockets.end()) {
mixer_sockets_mut.unlock();
ERROR("session/conf %s not found in mixer socket list.\n", make_id(session, conf_id).c_str());
return;
}
socketpair sockets = it->second;
AmMultiPartyMixerSocket* dst_sock;
if (type == AmMultiPartyMixerSocket::Play)
dst_sock = get_play_sock(sockets);
else
dst_sock = get_rec_sock(sockets);
dst_sock->clearPlaylist(mode);
mixer_sockets_mut.unlock();
}
int AmConference::conference_disconnect(AmSession* session, string conf_id) {
mixer_sockets_mut.lock();
map<string, socketpair>::iterator it = mixer_sockets.find(make_id(session, conf_id));
if (it == mixer_sockets.end()) {
mixer_sockets_mut.unlock();
ERROR("session/conf %s not found in mixer socket list.\n", make_id(session, conf_id).c_str());
return CONFERENCE_ERR_NOTEXIST;
}
socketpair sockets = it->second;
if (get_play_sock(sockets))
session->setOutput(0);
if (get_rec_sock(sockets))
session->setInput(0);
AmConferenceStatus* conf_status = AmConferenceStatusContainer::instance()->get_status(conf_id);
// unsigned int channel;
// if (AmMultiPartyMixerSocket* s = get_play_sock(sockets)) {
// channel = s->getChannel();
// } else if (AmMultiPartyMixerSocket* s = get_rec_sock(sockets)) {
// channel = s->getChannel();
// }
conf_status->remove_channel(session);
if (get_play_sock(sockets))
delete get_play_sock(sockets);
if (get_rec_sock(sockets))
delete get_rec_sock(sockets);
mixer_sockets.erase(it); // erase it!
mixer_sockets_mut.unlock();
DBG("session 0x%lx disconnected from conference '%s'\n", (unsigned long) session, conf_id.c_str());
return 0;
}
// disconnect sockets and destroy only, don't set session.input and session.output
int AmConference::conference_disconnect_sock(AmSession* session, string conf_id) {
mixer_sockets_mut.lock();
map<string, socketpair>::iterator it = mixer_sockets.find(make_id(session, conf_id));
if (it == mixer_sockets.end()) {
mixer_sockets_mut.unlock();
ERROR("session/conf %s not found in mixer socket list.\n", make_id(session, conf_id).c_str());
return CONFERENCE_ERR_NOTEXIST;
}
socketpair sockets = it->second;
AmConferenceStatus* conf_status = AmConferenceStatusContainer::instance()->get_status(conf_id);
conf_status->remove_channel(session);
if (get_play_sock(sockets))
delete get_play_sock(sockets);
if (get_rec_sock(sockets))
delete get_rec_sock(sockets);
mixer_sockets.erase(it); // erase it!
mixer_sockets_mut.unlock();
DBG("session 0x%lx socket-disconnected from conference '%s'\n", (unsigned long) session, conf_id.c_str());
return 0;
}
AmConference::socketpair AmConference::conference_get_sockets(AmSession* session, string conf_id) {
mixer_sockets_mut.lock();
map<string, socketpair>::iterator it = mixer_sockets.find(make_id(session, conf_id));
if (it == mixer_sockets.end()) {
mixer_sockets_mut.unlock();
ERROR("session/conf %s not found in mixer socket list.\n", make_id(session, conf_id).c_str());
return make_pair((AmMultiPartyMixerSocket*)0,(AmMultiPartyMixerSocket*)0);
}
mixer_sockets_mut.unlock();
return it->second;
}
string AmConference::make_id(const AmSession* session, const string& conf_id) {
char sptr_chr[20];
sprintf(sptr_chr, "%li", (unsigned long) session);
return string(sptr_chr) + "/" + conf_id;
}
AmAudioQueue* AmConference::find_queue(string q_id) {
map<string, AmAudioQueue*>::iterator it = audio_queues.find(q_id);
if (it != audio_queues.end())
return it->second;
else
return 0;
}
int AmConference::queue_connect(AmSession* session, string queue_id, bool send, bool receive) {
if (!send && !receive) {
ERROR("connect must either send or receive or both.\n");
return CONFERENCE_ERR_CONNTYPE;
}
string q_id = make_id(session, queue_id);
audio_queues_mut.lock();
AmAudioQueue* q = find_queue(q_id);
if (q) {
audio_queues_mut.unlock();
ERROR("already connected to queue with id '%s'\n", queue_id.c_str());
return CONFERENCE_ERR_ALREADY_CONNECTED;
}
q = new AmAudioQueue();
audio_queues[q_id] = q;
audio_queue_connected[q_id] = make_pair(send, receive);
if (send)
session->setOutput(q);
if (receive)
session->setInput(q);
audio_queues_mut.unlock();
return 0;
}
int AmConference::queue_disconnect(AmSession* session, string queue_id) {
string q_id = make_id(session, queue_id);
audio_queues_mut.lock();
AmAudioQueue* q = find_queue(q_id);
if (!q) {
audio_queues_mut.unlock();
ERROR("queue with id '%s' not found\n", queue_id.c_str());
return CONFERENCE_ERR_NOTEXIST;
}
if (audio_queue_connected[q_id].first)
session->setOutput(0);
if (audio_queue_connected[q_id].second)
session->setInput(0);
audio_queues.erase(audio_queues.find(q_id));
audio_queue_connected.erase(audio_queue_connected.find(q_id));
audio_queues_mut.unlock();
delete q; // delete the queue itself
return 0;
}
int AmConference::queue_pushAudio(AmSession* session, string queue_id, AmAudio* audio, AmAudioQueue::QueueType type,
AmAudioQueue::Pos pos, bool write, bool read) {
if (!audio) {
ERROR("not a valid audio source\n");
return CONFERENCE_ERR_INVALID_AUDIO;
}
string q_id = make_id(session, queue_id);
audio_queues_mut.lock();
AmAudioQueue* q = find_queue(q_id);
if (!q) {
audio_queues_mut.unlock();
ERROR("queue with id '%s' not found\n", queue_id.c_str());
return CONFERENCE_ERR_NOTEXIST;
}
q->pushAudio(audio, type, pos, write, read);
audio_queues_mut.unlock();
return 0;
}
int AmConference::queue_popAudio(AmSession* session, string queue_id, AmAudioQueue::QueueType type,
AmAudioQueue::Pos pos) {
string q_id = make_id(session, queue_id);
audio_queues_mut.lock();
AmAudioQueue* q = find_queue(q_id);
if (!q) {
audio_queues_mut.unlock();
ERROR("queue with id '%s' not found\n", queue_id.c_str());
return CONFERENCE_ERR_NOTEXIST;
}
int res = q->popAudio(type, pos);
audio_queues_mut.unlock();
return res;
}
AmAudio* AmConference::queue_popAndGetAudio(AmSession* session, string queue_id, AmAudioQueue::QueueType type,
AmAudioQueue::Pos pos) {
string q_id = make_id(session, queue_id);
audio_queues_mut.lock();
AmAudioQueue* q = find_queue(q_id);
if (!q) {
audio_queues_mut.unlock();
ERROR("queue with id '%s' not found\n", queue_id.c_str());
return 0; //CONFERENCE_ERR_NOTEXIST;
}
AmAudio* res = q->popAndGetAudio(type, pos);
audio_queues_mut.unlock();
return res;
}
int AmConference::queue_pushConference(AmSession* session, string queue_id, string conf_id,
bool write, bool read,
AmAudioQueue::Pos input_queue_pos, AmAudioQueue::Pos output_queue_pos,
int options) {
string q_id = make_id(session, queue_id);
audio_queues_mut.lock();
AmAudioQueue* q = find_queue(q_id);
if (!q) {
audio_queues_mut.unlock();
ERROR("queue with id '%s' not found\n", queue_id.c_str());
return CONFERENCE_ERR_NOTEXIST;
}
AmMultiPartyMixerSocket* play_sock;
AmMultiPartyMixerSocket* rec_sock;
int res = conference_connect_sock(session, conf_id, write, read, options,
&play_sock, &rec_sock);
if (res) {
audio_queues_mut.unlock();
return res;
}
if (write) {
// we should have the play_sock now
q->pushAudio(play_sock, AmAudioQueue::InputQueue, input_queue_pos, true, false);
}
if (read) {
// we should have the rec_sock
q->pushAudio(rec_sock, AmAudioQueue::OutputQueue, output_queue_pos, false, true);
}
audio_queues_mut.unlock();
return 0;
}
int AmConference::queue_removeConference(AmSession* session, string queue_id, string conf_id) {
string q_id = make_id(session, queue_id);
audio_queues_mut.lock();
AmAudioQueue* q = find_queue(q_id);
if (!q) {
audio_queues_mut.unlock();
ERROR("queue with id '%s' not found\n", queue_id.c_str());
return CONFERENCE_ERR_NOTEXIST;
}
socketpair conf_sockets = conference_get_sockets(session, conf_id);
if ((!get_play_sock(conf_sockets))&&(!get_rec_sock(conf_sockets))) {
audio_queues_mut.unlock();
ERROR("conference with id '%s' is not connected here\n", conf_id.c_str());
return CONFERENCE_ERR_NOTEXIST;
}
q->removeAudio(get_play_sock(conf_sockets));
q->removeAudio(get_rec_sock(conf_sockets));
int res = conference_disconnect_sock(session, conf_id);
audio_queues_mut.unlock();
DBG("removed conference from queue\n");
return res;
}
int AmConference::queue_pushBackConference(AmSession* session, string queue_id, string conf_id) {
return queue_pushConference(session, queue_id, conf_id,
true, true, AmAudioQueue::Back, AmAudioQueue::Front,
CONFERENCE_OPTIONS_CREATE_CONF);
}