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/core/AmMediaProcessor.h

247 lines
8.5 KiB

/*
* 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. This program is released under
* the GPL with the additional exemption that compiling, linking,
* and/or using OpenSSL is allowed.
*
* For a license to use the SEMS 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
*/
/** @file AmMediaProcessor.h */
#ifndef _AmMediaProcessor_h_
#define _AmMediaProcessor_h_
#include "AmEventQueue.h"
#include "amci/amci.h" // AUDIO_BUFFER_SIZE
#include <set>
using std::set;
#include <map>
struct SchedRequest;
/** Interface for basic media session processing.
*
* Media processor stores set of objects implementing this interface and
* periodically triggers media processing on each of them.
*
* First it calls readStreams() method on all managed media sessions to read
* from all streams first and then calls writeStreams() to send data out in all
* these sessions.
*
* Once audio processing of all media sessions is done, media processor walks
* through them once more and calls processDtmfEvents() on them to handle DTMF
* events detected when reading data from media streams.
*/
class AmMediaSession
{
private:
AmCondition processing_media;
public:
AmMediaSession(): processing_media(false) { }
virtual ~AmMediaSession() { }
/** Read from all media streams.
*
* To preserve current media processing scheme it is needed to read from all
* streams first and then write to them. This can be important for example
* in case of conferences where we need to have media from all streams ready
* for mixing them.
*
* So the AmMediaProcessorThread first calls readStreams on all sessions and
* then writeStreams on all sessions.
*
* \param ts timestamp for which the processing is currently running
*
* \param buffer multi-purpose space given from outside (AmMediaProcessorThread)
*
* Buffer given as parametr is usable for anything, originally was intended for data
* read from one stream before putting to another stream.
*
* The reason for having this buffer as parameter is that buffer size for
* audio processing is quite large (2K here) and thus allocating it on stack
* on some architectures may be problematic.
*
* On other hand having the buffer dynamically allocated for each media
* session would significantly increase memory consumption per call.
*
* So for now it seems to be the simplest way just to give the buffer as
* parameter from AmMediaProcessorThread and reuse it in all sessions handled
* by this thread (processing is done sequentially one session after another). */
virtual int readStreams(unsigned long long ts, unsigned char *buffer) = 0;
/** Write to all media streams.
*
* For the meaning of parameters see description of readStreams() method. */
virtual int writeStreams(unsigned long long ts, unsigned char *buffer) = 0;
/** Handle events in DTMF event queue.
*
* DTMF events should be detected from RTP stream when reading data (see
* readStreams()) and put into an event queue for later processing to avoid
* blocking of audio processing for too long time.
*
* This DTMF event queue should be processed then, within this method, which
* is triggered by AmMediaProcessorThread once reading/writing from media
* streams is finished. */
virtual void processDtmfEvents() = 0;
/** Reset all media processing elements.
*
* Called as part of cleanup when removing session from media processor upon
* an processing error (either readStreams() or writeStreams() returning
* error). */
virtual void clearAudio() = 0;
/** Reset timeouts of all RTP streams related to this media session.
*
* Called during initialization when session starts to be processed by media
* processor. */
virtual void clearRTPTimeout() = 0;
/** Callback function called when a session is added to media processor.
*
* Default implementation sets internal variable usable for detection if the
* object is in use by AmMediaProcessorThread. */
virtual void onMediaProcessingStarted() { processing_media.set(true); }
/* Callback function called when a session is removed from media processor.
*
* Default implementation sets internal variable usable for detection if the
* object is in use by AmMediaProcessorThread. */
virtual void onMediaProcessingTerminated() { processing_media.set(false); }
/** Indicates if the object is used by media processor.
*
* Returns value of internal variable for distinguishing if the object is
* already added into media processor. It should be avoided to insert one
* session into media processor multiple times.
*
* Note that using default implementation of onMediaProcessingStarted and
* onMediaProcessingTerminated is required for proper function. */
virtual bool isProcessingMedia() { return processing_media.get(); }
/** Indicates if the object is used by media processor.
*
* Seems to be duplicate to isProcessingMedia(). It was kept to reduce
* number of changes in existing code. */
virtual bool isDetached() { return !isProcessingMedia(); }
};
/**
* \brief Media processing thread
*
* This class implements a media processing thread.
* It processes the media and triggers the sending of RTP
* of all sessions added to it.
*/
class AmMediaProcessorThread :
public AmThread,
public AmEventHandler
{
AmEventQueue events;
unsigned char buffer[AUDIO_BUFFER_SIZE];
set<AmMediaSession*> sessions;
void processAudio(unsigned long long ts);
/**
* Process pending DTMF events
*/
void processDtmfEvents();
// AmThread interface
void run();
void on_stop();
const char *identify() { return "media processor"; }
// AmEventHandler interface
void process(AmEvent* e);
public:
AmMediaProcessorThread();
~AmMediaProcessorThread();
inline void postRequest(SchedRequest* sr);
unsigned int getLoad();
};
/**
* \brief Media processing thread manager
*
* This class implements the manager that assigns and removes
* the Sessions to the various \ref MediaProcessorThreads,
* according to their call group. This class contains the API
* for the MediaProcessor.
*/
class AmMediaProcessor
{
static AmMediaProcessor* _instance;
unsigned int num_threads;
AmMediaProcessorThread** threads;
std::map<string, unsigned int> callgroup2thread;
std::multimap<string, AmMediaSession*> callgroupmembers;
std::map<AmMediaSession*, string> session2callgroup;
AmMutex group_mut;
AmMediaProcessor();
~AmMediaProcessor();
void removeFromProcessor(AmMediaSession* s, unsigned int r_type);
public:
/**
* InsertSession : inserts the session to the processor
* RemoveSession : remove the session from the processor
* SoftRemoveSession : remove the session from the processor but leave it attached
* ClearSession : remove the session from processor and clear audio
*/
enum { InsertSession, RemoveSession, SoftRemoveSession, ClearSession };
static AmMediaProcessor* instance();
void init();
/** Add session s to processor */
void addSession(AmMediaSession* s, const string& callgroup);
/** Remove session s from processor */
void removeSession(AmMediaSession* s);
/** Remove session s from processor and clear its audio */
void clearSession(AmMediaSession* s);
/** Remove session s from processor but don't signal that to the session */
void softRemoveSession(AmMediaSession* s);
/** Change the callgroup of a session (use with caution) */
void changeCallgroup(AmMediaSession* s,
const string& new_callgroup);
void stop();
static void dispose();
};
#endif