/**
* Copyright 2010 Voxeo Corporation Licensed under the Apache License, Version
* 2.0 (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
* or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package com.voxeo.moho;
import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import javax.media.mscontrol.EventType;
import javax.media.mscontrol.MediaEventListener;
import javax.media.mscontrol.MediaObject;
import javax.media.mscontrol.MediaSession;
import javax.media.mscontrol.MsControlException;
import javax.media.mscontrol.MsControlFactory;
import javax.media.mscontrol.Parameters;
import javax.media.mscontrol.join.Joinable;
import javax.media.mscontrol.join.Joinable.Direction;
import javax.media.mscontrol.join.JoinableStream;
import javax.media.mscontrol.join.JoinableStream.StreamType;
import javax.media.mscontrol.mediagroup.MediaGroup;
import javax.media.mscontrol.mixer.MediaMixer;
import javax.media.mscontrol.mixer.MixerAdapter;
import javax.media.mscontrol.mixer.MixerEvent;
import javax.media.mscontrol.spi.Driver;
import javax.media.mscontrol.spi.DriverManager;
import org.apache.log4j.Logger;
import org.dom4j.Element;
import com.voxeo.moho.common.event.DispatchableEventSource;
import com.voxeo.moho.common.event.MohoActiveSpeakerEvent;
import com.voxeo.moho.common.event.MohoJoinCompleteEvent;
import com.voxeo.moho.common.event.MohoMediaResourceDisconnectEvent;
import com.voxeo.moho.common.event.MohoUnjoinCompleteEvent;
import com.voxeo.moho.event.Event;
import com.voxeo.moho.event.EventSource;
import com.voxeo.moho.event.JoinCompleteEvent;
import com.voxeo.moho.event.JoinCompleteEvent.Cause;
import com.voxeo.moho.event.Observer;
import com.voxeo.moho.event.UnjoinCompleteEvent;
import com.voxeo.moho.media.GenericMediaService;
import com.voxeo.moho.media.Input;
import com.voxeo.moho.media.Output;
import com.voxeo.moho.media.Prompt;
import com.voxeo.moho.media.Recording;
import com.voxeo.moho.media.SIPRecordingImpl;
import com.voxeo.moho.media.dialect.MediaDialect;
import com.voxeo.moho.media.input.InputCommand;
import com.voxeo.moho.media.output.OutputCommand;
import com.voxeo.moho.media.record.RecordCommand;
import com.voxeo.moho.media.record.SIPRecordCommand;
import com.voxeo.moho.media.siprecord.metadata.CommunicationSession;
import com.voxeo.moho.media.siprecord.metadata.MediaStream;
import com.voxeo.moho.media.siprecord.metadata.MetadataUtils;
import com.voxeo.moho.media.siprecord.metadata.ParticipantMetadata;
import com.voxeo.moho.media.siprecord.metadata.RecordingSession;
import com.voxeo.moho.remotejoin.RemoteParticipant;
import com.voxeo.moho.sip.JoinDelegate;
import com.voxeo.moho.sip.SIPEndpoint;
import com.voxeo.moho.sip.SipRecordingCall;
import com.voxeo.moho.spi.ExecutionContext;
import com.voxeo.moho.util.IDGenerator;
public class MixerImpl extends DispatchableEventSource implements Mixer, ParticipantContainer {
private static final Logger LOG = Logger.getLogger(MixerImpl.class);
protected MixerEndpoint _address;
protected MediaService<Mixer> _service;
protected MediaSession _media;
protected MediaMixer _mixer;
protected boolean _clampDtmf;
protected JoineeData _joinees = new JoineeData();
protected Map<Object, Participant> activeInputParticipant = new HashMap<Object, Participant>();
protected String _name;
protected MediaDialect _mediaDialect;
protected SIPRecordingImpl<Mixer> _sipRecording;
protected MixerImpl(final ExecutionContext context, final MixerEndpoint address, String name,
final Map<Object, Object> params, Parameters parameters) {
super(context);
_name = name;
_id = IDGenerator.generateId(_context, RemoteParticipant.RemoteParticipant_TYPE_CONFERENCE);
_mediaDialect = ((ApplicationContextImpl) _context).getDialect();
try {
MsControlFactory mf = null;
if ((params == null || params.size() == 0 || params.get(MsControlFactory.MEDIA_SERVER_URI) == null)
&& (address == null || address.getName().equalsIgnoreCase(MixerEndpoint.DEFAULT_MIXER_ENDPOINT))) {
mf = context.getMSFactory();
}
else {
final Driver driver = DriverManager.getDrivers().next();
final Properties props = new Properties();
for (final Map.Entry<Object, Object> entry : params.entrySet()) {
final String key = String.valueOf(entry.getKey());
final String value = entry.getValue() == null ? "" : entry.getValue().toString();
props.setProperty(key, value);
}
if (props.getProperty(MsControlFactory.MEDIA_SERVER_URI) == null && address != null
&& !address.getName().equalsIgnoreCase(MixerEndpoint.DEFAULT_MIXER_ENDPOINT)) {
props.setProperty(MsControlFactory.MEDIA_SERVER_URI, address.getURI().toString());
}
mf = driver.getFactory(props);
}
_media = mf.createMediaSession();
if (name != null) {
if (parameters == null) {
parameters = mf.createParameters();
}
_mediaDialect.setMixerName(parameters, name);
}
if ((parameters != null && parameters.get(MediaMixer.ENABLED_EVENTS) != null)
|| (params != null && params.get(MediaMixer.ENABLED_EVENTS) != null)) {
if (params != null && params.get(MediaMixer.ENABLED_EVENTS) != null
&& params.get(MediaMixer.ENABLED_EVENTS) instanceof EventType[]) {
if (parameters == null) {
parameters = mf.createParameters();
}
parameters.put(MediaMixer.ENABLED_EVENTS, params.get(MediaMixer.ENABLED_EVENTS));
}
_mixer = _media.createMediaMixer(MediaMixer.AUDIO_EVENTS, parameters);
}
else {
_mixer = _media.createMediaMixer(MediaMixer.AUDIO, parameters);
}
_address = address;
if ((address.getProperty("playTones") != null && !Boolean.valueOf(address.getProperty("playTones")))
|| (params != null && !Boolean.valueOf((String) params.get("playTones")))) {
_clampDtmf = true;
}
_mixer.addListener(new MixerEventListener());
if (_context != null && getId() != null) {
((ApplicationContextImpl) _context).addParticipant(this);
}
}
catch (final Exception e) {
throw new MediaException(e);
}
}
@Override
public int hashCode() {
return _mixer.hashCode();
}
@Override
public boolean equals(final Object o) {
if (!(o instanceof MixerImpl)) {
return false;
}
if (this == o) {
return true;
}
return _mixer.equals(((MixerImpl) o).getMediaObject());
}
@Override
public String toString() {
return String.format("%s[id=%s mediamixer=%s]", getClass().getSimpleName(), _id, _mixer);
}
public synchronized MediaService<Mixer> getMediaService(boolean create) throws MediaException, IllegalStateException {
checkState();
if (_service == null && create) {
try {
_service = (MediaService<Mixer>) _context.getMediaServiceFactory().create((Mixer) this, _media, null);
JoinDelegate.bridgeJoin(this, _service.getMediaGroup());
return _service;
}
catch (final Exception e) {
throw new MediaException(e);
}
}
return _service;
}
@Override
public synchronized MediaService<Mixer> getMediaService() throws MediaException, IllegalStateException {
return this.getMediaService(true);
}
@Override
public synchronized void disconnect() {
LOG.debug("Disconnecting mixer:" + this);
((ApplicationContextImpl) _context).removeParticipant(getId());
if (_service != null) {
try {
((GenericMediaService) _service).release(true);
}
catch (final Exception e) {
LOG.warn("Exception when release media service", e);
}
}
if (_sipRecording != null) {
try {
_sipRecording.stop();
}
catch (final Exception e) {
LOG.warn("Exception when stopping SIPRecording.", e);
}
}
try {
_mixer.release();
}
catch (final Exception e) {
LOG.warn("Exception when release mixer", e);
}
if (_media != null) {
try {
_media.release();
}
catch (final Exception e) {
LOG.warn("Exception when release mediaSession", e);
}
_media = null;
}
Participant[] _joineesArray = _joinees.getJoinees();
for (Participant participant : _joineesArray) {
if (participant instanceof ParticipantContainer) {
try {
((ParticipantContainer) participant).doUnjoin(this, false);
}
catch (Exception e) {
LOG.error("", e);
}
MohoUnjoinCompleteEvent event = new MohoUnjoinCompleteEvent(participant, MixerImpl.this,
UnjoinCompleteEvent.Cause.DISCONNECT, false);
participant.dispatch(event);
dispatch(new MohoUnjoinCompleteEvent(this, participant, UnjoinCompleteEvent.Cause.DISCONNECT, true));
}
}
_joinees.clear();
activeInputParticipant.clear();
joinDelegates.clear();
this.dispatch(new MohoMediaResourceDisconnectEvent<Mixer>(this));
LOG.debug("Disconnected mixer.");
}
@Override
public Endpoint getAddress() {
return _address;
}
@Override
public Participant[] getParticipants() {
return _joinees.getJoinees();
}
@Override
public Participant[] getParticipants(final Direction direction) {
return _joinees.getJoinees(direction);
}
@Override
public void addParticipant(final Participant p, final JoinType type, final Direction direction, Participant realJoined) {
_joinees.add(p, type, direction, realJoined);
if (realJoined == null) {
activeInputParticipant.put(p.getMediaObject(), p);
}
else {
activeInputParticipant.put(realJoined.getMediaObject(), p);
}
}
public JoinData removeParticipant(final Participant p) {
JoinData joinData = _joinees.remove(p);
if (joinData != null) {
if (joinData.getRealJoined() == null) {
activeInputParticipant.remove(p.getMediaObject());
}
else {
activeInputParticipant.remove(joinData.getRealJoined().getMediaObject());
}
}
return joinData;
}
@Override
public Joint join(final Participant other, final JoinType type, final Direction direction)
throws IllegalStateException {
return this.join(other, type, false, direction);
}
@Override
public Joint join(final Participant other, final JoinType type, final boolean force, final Direction direction)
throws IllegalStateException {
synchronized (this) {
checkState();
if (_joinees.contains(other)) {
return new JointImpl(_context.getExecutor(), new JointImpl.DummyJoinWorker(MixerImpl.this, other));
}
}
// join strategy check on either side
try {
final ExecutionException e = JoinDelegate.checkJoinStrategy(this, other, type, force);
if (e != null) {
// dispatch BUSY event
final MohoJoinCompleteEvent event = new MohoJoinCompleteEvent(this, other, Cause.BUSY, e, true);
dispatch(event);
final MohoJoinCompleteEvent event2 = new MohoJoinCompleteEvent(other, this, Cause.BUSY, e, false);
other.dispatch(event2);
throw e;
}
}
catch (Exception e) {
// TODO
throw new RuntimeException(e);
}
if (other instanceof CallImpl) {
Joint joint = null;
if (isClampDtmf(null)) {
try {
joint = other.join(new ClampDtmfMixerAdapter(), type, force, JoinDelegate.reserve(direction));
}
catch (MsControlException ex) {
LOG.warn("can't clamp DTMF", ex);
joint = other.join(this, type, force, JoinDelegate.reserve(direction));
}
}
else {
joint = other.join(this, type, force, JoinDelegate.reserve(direction));
}
return joint;
}
else if (other instanceof RemoteParticipant) {
return other.join(this, type, force, JoinDelegate.reserve(direction));
}
else {
if (!(other.getMediaObject() instanceof Joinable)) {
throw new IllegalArgumentException("MediaObject is't joinable.");
}
return new JointImpl(_context.getExecutor(), new JoinWorker() {
@Override
public JoinCompleteEvent call() throws Exception {
JoinCompleteEvent event = null;
try {
synchronized (MixerImpl.this) {
if (MixerImpl.this.isClampDtmf(null)) {
try {
ClampDtmfMixerAdapter clampMixerAdapter = new ClampDtmfMixerAdapter();
JoinDelegate.bridgeJoin(clampMixerAdapter, other, direction);
_joinees.add(other, type, direction, clampMixerAdapter);
((ParticipantContainer) other).addParticipant(MixerImpl.this, type, direction, clampMixerAdapter);
}
catch (MsControlException ex) {
LOG.warn("can't clamp DTMF", ex);
JoinDelegate.bridgeJoin(MixerImpl.this, other, direction);
_joinees.add(other, type, direction);
((ParticipantContainer) other).addParticipant(MixerImpl.this, type, direction, null);
}
}
else {
JoinDelegate.bridgeJoin(MixerImpl.this, other, direction);
_joinees.add(other, type, direction);
((ParticipantContainer) other).addParticipant(MixerImpl.this, type, direction, null);
}
event = new MohoJoinCompleteEvent(MixerImpl.this, other, Cause.JOINED, true);
}
}
catch (final Exception e) {
event = new MohoJoinCompleteEvent(MixerImpl.this, other, Cause.ERROR, e, true);
throw new MediaException(e);
}
finally {
MixerImpl.this.dispatch(event);
MohoJoinCompleteEvent event2 = new MohoJoinCompleteEvent(other, MixerImpl.this, event.getCause(), false);
other.dispatch(event2);
}
return event;
}
@Override
public boolean cancel() {
return false;
}
});
}
}
public synchronized MohoUnjoinCompleteEvent doUnjoin(final Participant p, boolean callOtherUnjoin) throws Exception {
MohoUnjoinCompleteEvent event = null;
if (!_joinees.contains(p)) {
event = new MohoUnjoinCompleteEvent(MixerImpl.this, p, UnjoinCompleteEvent.Cause.NOT_JOINED, true);
MixerImpl.this.dispatch(event);
return event;
}
try {
JoinData joinData = removeParticipant(p);
if (p.getMediaObject() instanceof Joinable) {
if (joinData.getRealJoined() != null) {
if (callOtherUnjoin) {
JoinDelegate.bridgeUnjoin(joinData.getRealJoined(), p);
}
((ClampDtmfMixerAdapter) joinData.getRealJoined())._mixerAdapter.release();
}
else {
if (callOtherUnjoin) {
JoinDelegate.bridgeUnjoin(this, p);
}
}
}
if (callOtherUnjoin) {
((ParticipantContainer) p).doUnjoin(this, false);
}
event = new MohoUnjoinCompleteEvent(MixerImpl.this, p, UnjoinCompleteEvent.Cause.SUCCESS_UNJOIN, true);
}
catch (final Exception e) {
LOG.error("", e);
event = new MohoUnjoinCompleteEvent(MixerImpl.this, p, UnjoinCompleteEvent.Cause.FAIL_UNJOIN, e, true);
throw e;
}
finally {
if (event == null) {
event = new MohoUnjoinCompleteEvent(MixerImpl.this, p, UnjoinCompleteEvent.Cause.FAIL_UNJOIN, true);
}
MixerImpl.this.dispatch(event);
}
return event;
}
public Unjoint unjoin(final Participant other) {
Unjoint task = new UnjointImpl(_context.getExecutor(), new Callable<UnjoinCompleteEvent>() {
@Override
public UnjoinCompleteEvent call() throws Exception {
return doUnjoin(other, true);
}
});
return task;
}
public Unjoint unjoin(final Participant other, final boolean isInitiator) {
Unjoint task = new UnjointImpl(_context.getExecutor(), new Callable<UnjoinCompleteEvent>() {
@Override
public UnjoinCompleteEvent call() throws Exception {
return doUnjoin(other, isInitiator);
}
});
return task;
}
@Override
public MediaObject getMediaObject() {
return _mixer;
}
@Override
public JoinableStream getJoinableStream(final StreamType arg0) throws MediaException, IllegalStateException {
checkState();
try {
return _mixer.getJoinableStream(arg0);
}
catch (final MsControlException e) {
throw new MediaException(e);
}
}
@Override
public JoinableStream[] getJoinableStreams() throws MediaException, IllegalStateException {
checkState();
try {
return _mixer.getJoinableStreams();
}
catch (final MsControlException e) {
throw new MediaException(e);
}
}
protected void checkState() {
if (_media == null) {
throw new IllegalStateException();
}
}
protected boolean isClampDtmf(Properties props) {
boolean clampDTMF = _clampDtmf;
if (props != null && props.get("playTones") != null) {
if (Boolean.valueOf(props.getProperty("playTones"))) {
clampDTMF = false;
}
else {
clampDTMF = true;
}
}
return clampDTMF;
}
@Override
public Joint join(final Participant other, final JoinType type, final Direction direction, final Properties props) {
return join(other, type, false, direction, props);
}
@Override
public Joint join(final Participant other, final JoinType type, final boolean force, final Direction direction,
final Properties props) {
return join(other, type, false, direction, !isClampDtmf(props));
}
@Override
public Joint join(final Participant other, final JoinType type, final boolean force, final Direction direction,
final boolean dtmfPassThough) {
synchronized (this) {
checkState();
if (_joinees.contains(other)) {
return new JointImpl(_context.getExecutor(), new JointImpl.DummyJoinWorker(MixerImpl.this, other));
}
}
// join strategy check on either side
try {
final ExecutionException e = JoinDelegate.checkJoinStrategy(this, other, type, force);
if (e != null) {
// dispatch BUSY event
final MohoJoinCompleteEvent event = new MohoJoinCompleteEvent(this, other, Cause.BUSY, e, true);
dispatch(event);
final MohoJoinCompleteEvent event2 = new MohoJoinCompleteEvent(other, this, Cause.BUSY, e, false);
other.dispatch(event2);
throw e;
}
}
catch (Exception e) {
// TODO
throw new RuntimeException(e);
}
if (other instanceof CallImpl) {
Joint joint = null;
if (!dtmfPassThough) {
try {
joint = other.join(new ClampDtmfMixerAdapter(), type, direction);
}
catch (MsControlException ex) {
LOG.warn("can't clamp DTMF", ex);
joint = other.join(this, type, direction);
}
}
else {
joint = other.join(this, type, direction);
}
return joint;
}
else if (other instanceof RemoteParticipant) {
return other.join(this, type, force, JoinDelegate.reserve(direction));
}
else {
if (!(other.getMediaObject() instanceof Joinable)) {
throw new IllegalArgumentException("MediaObject is't joinable.");
}
return new JointImpl(_context.getExecutor(), new JoinWorker() {
@Override
public JoinCompleteEvent call() throws Exception {
JoinCompleteEvent event = null;
try {
synchronized (MixerImpl.this) {
if (!dtmfPassThough) {
try {
ClampDtmfMixerAdapter clampMixerAdapter = new ClampDtmfMixerAdapter();
JoinDelegate.bridgeJoin(clampMixerAdapter, other, direction);
_joinees.add(other, type, direction, clampMixerAdapter);
((ParticipantContainer) other).addParticipant(MixerImpl.this, type, direction, clampMixerAdapter);
}
catch (MsControlException ex) {
LOG.warn("can't clamp DTMF", ex);
JoinDelegate.bridgeJoin(MixerImpl.this, other, direction);
_joinees.add(other, type, direction);
((ParticipantContainer) other).addParticipant(MixerImpl.this, type, direction, null);
}
}
else {
JoinDelegate.bridgeJoin(MixerImpl.this, other, direction);
_joinees.add(other, type, direction);
((ParticipantContainer) other).addParticipant(MixerImpl.this, type, direction, null);
}
event = new MohoJoinCompleteEvent(MixerImpl.this, other, Cause.JOINED, true);
}
}
catch (final Exception e) {
event = new MohoJoinCompleteEvent(MixerImpl.this, other, Cause.ERROR, e, true);
throw new MediaException(e);
}
finally {
MixerImpl.this.dispatch(event);
MohoJoinCompleteEvent event2 = new MohoJoinCompleteEvent(other, MixerImpl.this, event.getCause(), false);
other.dispatch(event2);
}
return event;
}
@Override
public boolean cancel() {
return false;
}
});
}
}
public class ClampDtmfMixerAdapter implements Mixer, ParticipantContainer {
protected MixerAdapter _mixerAdapter;
public ClampDtmfMixerAdapter() throws MsControlException {
super();
_mixerAdapter = MixerImpl.this._mixer.createMixerAdapter(MixerAdapter.DTMF_CLAMP);
}
@Override
public MediaService<Mixer> getMediaService() {
return MixerImpl.this.getMediaService();
}
public MediaService<Mixer> getMediaService(boolean create) {
return MixerImpl.this.getMediaService(create);
}
@Override
public Joint join(Participant other, JoinType type, Direction direction, Properties props) {
return MixerImpl.this.join(other, type, direction, props);
}
@Override
public Joint join(Participant other, JoinType type, boolean force, Direction direction, Properties props) {
return MixerImpl.this.join(other, type, force, direction, props);
}
@Override
public Joint join(Participant other, JoinType type, boolean force, Direction direction, boolean dtmfPassThough) {
return MixerImpl.this.join(other, type, force, direction, dtmfPassThough);
}
@Override
public JoinableStream getJoinableStream(StreamType value) {
JoinableStream result = null;
try {
result = _mixerAdapter.getJoinableStream(value);
}
catch (final MsControlException e) {
throw new MediaException(e);
}
return result;
}
@Override
public JoinableStream[] getJoinableStreams() {
JoinableStream[] result = null;
try {
result = _mixerAdapter.getJoinableStreams();
}
catch (final MsControlException e) {
throw new MediaException(e);
}
return result;
}
@Override
public void disconnect() {
MixerImpl.this.disconnect();
}
@Override
public Endpoint getAddress() {
return MixerImpl.this.getAddress();
}
@Override
public MediaObject getMediaObject() {
return _mixerAdapter;
}
@Override
public Participant[] getParticipants() {
return MixerImpl.this.getParticipants();
}
@Override
public Participant[] getParticipants(Direction direction) {
return MixerImpl.this.getParticipants(direction);
}
@Override
public Joint join(Participant other, JoinType type, Direction direction) {
return MixerImpl.this.join(other, type, direction);
}
@Override
public Unjoint unjoin(Participant other) {
return MixerImpl.this.unjoin(other);
}
// private void addListener(EventListener<?> listener) {
// MixerImpl.this.addListener(listener);
// }
//
// private <E extends Event<?>, T extends EventListener<E>> void
// addListener(Class<E> type, T listener) {
// MixerImpl.this.addListener(type, listener);
// }
//
// private void addListeners(EventListener<?>... listeners) {
// MixerImpl.this.addListeners(listeners);
// }
//
// private <E extends Event<?>, T extends EventListener<E>> void
// addListeners(Class<E> type, T... listener) {
// MixerImpl.this.addListeners(type, listener);
// }
//
// private void addObserver(Observer observer) {
// MixerImpl.this.addObserver(observer);
// }
@Override
public void addObserver(Observer... observers) {
MixerImpl.this.addObserver(observers);
}
@Override
public <S extends EventSource, T extends Event<S>> Future<T> dispatch(T event) {
return MixerImpl.this.dispatch(event);
}
@Override
public <S extends EventSource, T extends Event<S>> Future<T> dispatch(T event, Runnable afterExec) {
return MixerImpl.this.dispatch(event, afterExec);
}
@Override
public ApplicationContext getApplicationContext() {
return MixerImpl.this.getApplicationContext();
}
@Override
public String getApplicationState() {
return MixerImpl.this.getApplicationState();
}
@Override
public String getApplicationState(String FSM) {
return MixerImpl.this.getApplicationState(FSM);
}
// private void removeListener(EventListener<?> listener) {
// MixerImpl.this.removeListener(listener);
// }
@Override
public void removeObserver(Observer listener) {
MixerImpl.this.removeObserver(listener);
}
@Override
public void setApplicationState(String state) {
MixerImpl.this.setApplicationState(state);
}
@Override
public void setApplicationState(String FSM, String state) {
MixerImpl.this.setApplicationState(FSM, state);
}
@Override
public String getId() {
return MixerImpl.this.getId();
}
@Override
@SuppressWarnings("unchecked")
public <T> T getAttribute(String name) {
return (T) MixerImpl.this.getAttribute(name);
}
@Override
public Map<String, Object> getAttributeMap() {
return MixerImpl.this.getAttributeMap();
}
@Override
public void setAttribute(String name, Object value) {
MixerImpl.this.setAttribute(name, value);
}
@Override
public void addParticipant(Participant p, JoinType type, Direction direction, Participant realJoined) {
MixerImpl.this.addParticipant(p, type, direction, realJoined);
}
public MixerImpl getMixer() {
return MixerImpl.this;
}
@Override
public Output<Mixer> output(String text) throws MediaException {
return getMediaService().output(text);
}
@Override
public Output<Mixer> output(URI media) throws MediaException {
return getMediaService().output(media);
}
@Override
public Output<Mixer> output(OutputCommand output) throws MediaException {
return getMediaService().output(output);
}
@Override
public Prompt<Mixer> prompt(String text, String grammar, int repeat) throws MediaException {
return getMediaService().prompt(text, grammar, repeat);
}
@Override
public Prompt<Mixer> prompt(URI media, String grammar, int repeat) throws MediaException {
return getMediaService().prompt(media, grammar, repeat);
}
@Override
public Prompt<Mixer> prompt(OutputCommand output, InputCommand input, int repeat) throws MediaException {
return getMediaService().prompt(output, input, repeat);
}
@Override
public Input<Mixer> input(String grammar) throws MediaException {
return getMediaService().input(grammar);
}
@Override
public Input<Mixer> input(InputCommand input) throws MediaException {
return getMediaService().input(input);
}
@Override
public Recording<Mixer> record(URI recording) throws MediaException {
return getMediaService().record(recording);
}
@Override
public Recording<Mixer> record(RecordCommand command) throws MediaException {
return getMediaService().record(command);
}
@Override
public MediaGroup getMediaGroup(boolean create) {
if (this.getMediaService(create) != null) {
return getMediaService(false).getMediaGroup();
}
return null;
}
@Override
public MediaGroup getMediaGroup() {
return getMediaGroup(true);
}
@Override
public String getRemoteAddress() {
return MixerImpl.this.getRemoteAddress();
}
@Override
public MohoUnjoinCompleteEvent doUnjoin(Participant other, boolean callPeerUnjoin) throws Exception {
return MixerImpl.this.doUnjoin(other, callPeerUnjoin);
}
@Override
public void startJoin(Participant participant, JoinDelegate delegate) {
joinDelegates.put(participant.getId(), delegate);
}
@Override
public void joinDone(Participant participant, JoinDelegate delegate) {
joinDelegates.remove(participant.getId());
}
public JoinDelegate getJoinDelegate(String id) {
return joinDelegates.get(id);
}
@Override
public Direction getDirection(Participant participant) {
return _joinees.getDirection(participant);
}
@Override
public Joint join(Participant other, JoinType type, boolean force, Direction direction) {
return MixerImpl.this.join(other, type, force, direction);
}
@Override
public byte[] getJoinSDP() {
throw new UnsupportedOperationException("");
}
@Override
public void processSDPAnswer(byte[] sdp) throws IOException {
throw new UnsupportedOperationException("");
}
@Override
public byte[] processSDPOffer(byte[] sdp) throws IOException {
throw new UnsupportedOperationException("");
}
@Override
public JoinType getJoinType(Participant participant) {
return MixerImpl.this.getJoinType(participant);
}
@Override
public Unjoint unjoin(Participant other, boolean callPeerUnjoin) throws Exception {
return MixerImpl.this.unjoin(other, callPeerUnjoin);
}
@Override
public String getName() {
return MixerImpl.this.getName();
}
}
// listener for Active speaker event.
public class MixerEventListener implements MediaEventListener<MixerEvent> {
@Override
public void onEvent(MixerEvent event) {
if (event.getEventType() == MixerEvent.ACTIVE_INPUTS_CHANGED) {
Joinable[] joinables = event.getActiveInputs();
List<Participant> activeSpeakers = new LinkedList<Participant>();
if (joinables != null) {
for (Joinable joinalbe : joinables) {
Participant participant = activeInputParticipant.get(joinalbe);
if (participant != null) {
activeSpeakers.add(participant);
}
}
MixerImpl.this.dispatch(new MohoActiveSpeakerEvent(MixerImpl.this, activeSpeakers
.toArray(new Participant[] {})));
}
}
}
}
@Override
public Output<Mixer> output(String text) throws MediaException {
return getMediaService().output(text);
}
@Override
public Output<Mixer> output(URI media) throws MediaException {
return getMediaService().output(media);
}
@Override
public Output<Mixer> output(OutputCommand output) throws MediaException {
return getMediaService().output(output);
}
@Override
public Prompt<Mixer> prompt(String text, String grammar, int repeat) throws MediaException {
return getMediaService().prompt(text, grammar, repeat);
}
@Override
public Prompt<Mixer> prompt(URI media, String grammar, int repeat) throws MediaException {
return getMediaService().prompt(media, grammar, repeat);
}
@Override
public Prompt<Mixer> prompt(OutputCommand output, InputCommand input, int repeat) throws MediaException {
return getMediaService().prompt(output, input, repeat);
}
@Override
public Input<Mixer> input(String grammar) throws MediaException {
return getMediaService().input(grammar);
}
@Override
public Input<Mixer> input(InputCommand input) throws MediaException {
return getMediaService().input(input);
}
@Override
public Recording<Mixer> record(URI recording) throws MediaException {
return getMediaService().record(recording);
}
@Override
public Recording<Mixer> record(RecordCommand command) throws MediaException {
if (command instanceof SIPRecordCommand) {
SIPRecordCommand siprecCommand = (SIPRecordCommand) command;
LOG.debug(this + " starting SIPRecording, SRS:" + siprecCommand.getSiprecServer());
// This is SIPREC, only support prompt parameter. all the other parameters
// will be ignored
if (command.getPrompt() != null) {
try {
getMediaService().output(command.getPrompt()).get();
}
catch (Exception e) {
LOG.error("Exception when playing prompt.", e);
}
}
// create metadata
Participant[] participants = _joinees.getJoinees();
Map<String, List<Element>> extendData = siprecCommand.getParticipantExtendedMetadata();
String labelValue = MetadataUtils.generateLabelValue();
RecordingSession rs = new RecordingSession();
CommunicationSession cs = new CommunicationSession();
rs.assotiateCommunicationSession(cs);
cs.setExtendedDatas(siprecCommand.getExtendedDatat());
MediaStream stream = new MediaStream();
stream.setLabel(labelValue);
cs.associateMediaStream(stream);
for (Participant participant : participants) {
ParticipantMetadata participantMetadata = new ParticipantMetadata();
participantMetadata.associateSendStream(stream);
participantMetadata.associateReceiveStream(stream);
Map<URI, String> nameAors = new HashMap<URI, String>();
nameAors.put(participant.getAddress().getURI(), participant.getAddress().getName());
participantMetadata.setNameAors(nameAors);
if (extendData != null && extendData.get(participant.getId()) != null) {
participantMetadata.setExtendedDatas(extendData.get(participant.getId()));
}
cs.associateParticipant(participantMetadata);
}
// create sipRecordingCall and set label for SDP
Endpoint recServerEndpoint = getApplicationContext().createEndpoint(siprecCommand.getSiprecServer().toString());
Endpoint srcEndpoint = getApplicationContext().createEndpoint(siprecCommand.getSiprecSrcURI().toString());
SipRecordingCall recordingCall = new SipRecordingCall((ExecutionContext) getApplicationContext(),
(SIPEndpoint) srcEndpoint, (SIPEndpoint) recServerEndpoint);
recordingCall.setLabel(labelValue);
recordingCall.setRSMetadata(rs);
// create SIPRecording
_sipRecording = new SIPRecordingImpl<Mixer>(recordingCall, this);
// start record
_sipRecording.start();
return _sipRecording;
}
else {
return getMediaService().record(command);
}
}
@Override
public MediaGroup getMediaGroup(boolean create) {
MediaService<Mixer> service = this.getMediaService(create);
if (service != null) {
return service.getMediaGroup(false);
}
return null;
}
@Override
public MediaGroup getMediaGroup() {
return this.getMediaGroup(true);
}
@Override
public String getRemoteAddress() {
return _id;
}
private Map<String, JoinDelegate> joinDelegates = new ConcurrentHashMap<String, JoinDelegate>();
@Override
public void startJoin(Participant participant, JoinDelegate delegate) {
joinDelegates.put(participant.getId(), delegate);
}
@Override
public void joinDone(Participant participant, JoinDelegate delegate) {
joinDelegates.remove(participant.getId());
}
public JoinDelegate getJoinDelegate(String id) {
return joinDelegates.get(id);
}
@Override
public Direction getDirection(Participant participant) {
return _joinees.getDirection(participant);
}
@Override
public byte[] getJoinSDP() {
throw new UnsupportedOperationException("");
}
@Override
public void processSDPAnswer(byte[] sdp) throws IOException {
throw new UnsupportedOperationException("");
}
@Override
public byte[] processSDPOffer(byte[] sdp) throws IOException {
throw new UnsupportedOperationException("");
}
@Override
public JoinType getJoinType(Participant participant) {
return _joinees.getJoinType(participant);
}
@Override
public String getName() {
return _name;
}
public void setSipRecording(SIPRecordingImpl<Mixer> sipRecording) {
this._sipRecording = sipRecording;
}
}