package com.rayo.server; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.media.mscontrol.EventType; import javax.media.mscontrol.mixer.MediaMixer; import javax.media.mscontrol.mixer.MixerEvent; import com.voxeo.logging.Loggerf; import com.voxeo.moho.ApplicationContext; import com.voxeo.moho.Mixer; import com.voxeo.moho.MixerEndpoint; import com.voxeo.moho.Participant; /** * <p>Manages mixers. Encapsulates all the logic related with mixers and mixer actors * life cycle.</p> * * @author martin * */ public class MixerManager { private static Loggerf log = Loggerf.getLogger(MixerManager.class); private MixerActorFactory mixerActorFactory; private MixerRegistry mixerRegistry; private MixerStatistics mixerStatistics; private CallManager callManager; private boolean gatewayHandlingMixers = false; private Map<String, ReentrantReadWriteLock> locks = new ConcurrentHashMap<String, ReentrantReadWriteLock>(); private ReentrantReadWriteLock globalLock = new ReentrantReadWriteLock(); public Mixer create(ApplicationContext ctx, String mixerName) { return create(ctx, mixerName, 0); } public Mixer create(ApplicationContext ctx, String mixerName, Integer minParticipants) { return create(ctx, mixerName, minParticipants, true); } public Mixer create(ApplicationContext ctx, String mixerName, Integer minParticipants, boolean enableActiveSpeakerEvents) { Lock lock = globalLock.writeLock(); lock.lock(); try { Mixer mixer = getMixer(mixerName); if (mixer != null) { log.debug("Mixer with name %s already exists", mixerName); return mixer; } log.info("Creating mixer %s", mixerName); // mixer creation MixerEndpoint endpoint = (MixerEndpoint)ctx .createEndpoint(MixerEndpoint.DEFAULT_MIXER_ENDPOINT); Map<Object, Object> parameters = new HashMap<Object, Object>(); if (enableActiveSpeakerEvents) { parameters.put(MediaMixer.ENABLED_EVENTS, new EventType[]{MixerEvent.ACTIVE_INPUTS_CHANGED}); } mixer = endpoint.create(mixerName, parameters); mixer.setAttribute("minParticipants", minParticipants); MixerActor actor = mixerActorFactory.create(mixer, mixerName); actor.setupMohoListeners(mixer); // Wire up default call handlers for (EventHandler handler : callManager.getEventHandlers()) { actor.addEventHandler(handler); } locks.put(mixerName, new ReentrantReadWriteLock()); actor.start(); mixerRegistry.add(actor); mixerStatistics.mixerCreated(); log.info("Mixer %s created successfully", mixerName); return mixer; } finally { lock.unlock(); } } public Mixer getMixer(String mixerName) { Lock lock = getReadLock(mixerName); lock.lock(); try { MixerActor actor = mixerRegistry.get(mixerName); if (actor != null) { return actor.getMixer(); } return null; } finally { lock.unlock(); } } public void removeMixer(Mixer mixer) { Lock lock = getWriteLock(mixer.getName()); lock.lock(); try { if (getMixer(mixer.getName()) == null) { log.error("Mixer %s does not exist", mixer.getName()); return; } log.info("Removing mixer: %s", mixer); MixerActor actor = mixerRegistry.remove(mixer.getName()); if (actor != null) { for (EventHandler handler : callManager.getEventHandlers()) { actor.removeEventHandler(handler); } actor.dispose(); } } finally { removeLock(mixer.getName()); lock.unlock(); } } public void setMixerActorFactory(MixerActorFactory mixerActorFactory) { this.mixerActorFactory = mixerActorFactory; } public void setMixerRegistry(MixerRegistry mixerRegistry) { this.mixerRegistry = mixerRegistry; } public void setMixerStatistics(MixerStatistics mixerStatistics) { this.mixerStatistics = mixerStatistics; } public void setCallManager(CallManager callManager) { this.callManager = callManager; } public boolean isGatewayHandlingMixers() { return gatewayHandlingMixers; } public void setGatewayHandlingMixers(boolean gatewayHandlingMixers) { this.gatewayHandlingMixers = gatewayHandlingMixers; } public void handleCallDisconnect(Mixer mixer, Participant participant) { log.info("Participant %s is disconnecting from mixer %s", participant, mixer); Lock lock = getWriteLock(mixer.getName()); lock.lock(); try { if (getMixer(mixer.getName()) == null) { log.error("Mixer %s has already been disposed.", mixer.getName()); return; } log.debug("Unjoining mixer %s which has %s participants", mixer, mixer.getParticipants().length); Integer minParticipants = mixer.getAttribute("minParticipants"); if (mixer.getParticipants().length <= minParticipants) { log.debug("Mixer %s has less than %s participants. Disposing it", mixer, minParticipants); for(Participant p: mixer.getParticipants()) { p.disconnect(); } removeMixer((Mixer)mixer); } } finally { lock.unlock(); } } private Lock getWriteLock(String mixerName) { ReentrantReadWriteLock lock = locks.get(mixerName); if (lock == null) { lock = globalLock; } return lock.writeLock(); } private void removeLock(String mixerName) { locks.remove(mixerName); } private Lock getReadLock(String mixerName) { ReentrantReadWriteLock lock = locks.get(mixerName); if (lock == null) { lock = globalLock; } return lock.readLock(); } }