Java Media Control: Using MediaMixer
Monday, May 9th, 2011Besides using MediaGroup to provide multimedia functions, another common use of media server is multi-party conference calls. MediaMixer is the object in Java Media Control that provides sound mixing capability.
Overview
Similar to MediaGroup, a MediaMixer is a Joinable MediaObject. To connect an endpoint to MediaMixer, simply joining the endpoint’s NetworkConnection to the MediaMixer object, as illustrated in the following diagram from JSR 309 specification.
NC1.join(DUPLEX, theMediaMixer); NC2.join(DUPLEX, theMediaMixer); NC3.join(DUPLEX, theMediaMixer);
Sample Application
Here is a simple conference application. When a call comes in, the application asks the caller to input a conference number (max 9 digits). Based on the conference number, the application either create or locate a MediaMixer. Then join the caller’s NetworkConnection to the MediaMixer.
The following is a simplified sequence diagram that shows the interaction among different objects.
Participant Object
The Participant is an abstraction that capture all the signaling and media states and operations on the per caller (conference participant) basis. This is a design pattern I often use in SIP Servlet and Java Media Control based applications.
When an initial SIP INVITE message comes, the MainServlet creates a new Participant object. During the construction, Participant initializes a MediaSession and create a NetworkConnection and a MediaGroup from the MediaSession. Then Participant uses SdpPortManager of the NetworkConnection to negotiate the SDP with the client. See SDP Negotiation for more information about the SdpPortManager.
Collect Conference ID
Once the call is setup, Participant asks the caller to input the conference ID. Instead of using Player to ask the question and use SignalDetector to collect input, I use the PROMPT in the SignalDetector to ask and collect in a single receiveSignal call, as shown below.
SignalDetector detector = _mg.getSignalDetector();
Parameters options = _factory.createParameters();
options.put(SignalDetector.INTER_SIG_TIMEOUT, 5000);
options.put(SignalDetector.PROMPT, URI.create("data:" + ANNOUCEMENT));
detector.receiveSignals(9, SignalDetector.NO_PATTERN, RTC.NO_RTC, options);
I also set INTER_SIG_TIMEOUT to 5 seconds. This allows SignalDetector to collect any conference ID that is less 9 digits long when the user stops inputting for 5 seconds. Otherwise, SignalDetector will keep waiting until the user inputs 9 digits.
Join the Conference
Once the conference ID is collected, Participant calls MixerManager to create a MediaMixer based on the ID. The MixerManager maintains a mapping between ID and MediaMixer and only creates a MediaMixer when needed.
public synchronized MediaMixer createMixer(Configuration config, Parameters options, String key) throws MsControlException {
MediaMixer mixer = _mixers.get(key);
if (mixer == null) {
MediaSession session = _factory.createMediaSession();
mixer = session.createMediaMixer(config, options);
_mixers.put(key, mixer);
}
return mixer;
}
Once Participant obtains the MediaMixer, it will join its NetworkConnection to the MediaMixer.
public void join(String id) throws MsControlException {
_mixer = _mgr.createMixer(MediaMixer.AUDIO, Parameters.NO_PARAMETER, id);
_id = id;
_nc.join(Direction.DUPLEX, _mixer);
}
Leave the Conference
When the caller hangs up the call, SIP BYE is received by the MainServlet. Participant, retrieved from the SipSession, unjoin its NetworkConnection from the MediaMixer.
public void unjoin() throws MsControlException {
if (_mixer != null) {
_mixer.unjoin(_nc);
_ms.release();
_mgr.removeMixer(_id);
}
}
The MixerManager only removes the MediaMixer when no Participant is joined.
public synchronized MediaMixer removeMixer(String key) throws MsControlException {
MediaMixer mixer = getMixer(key);
if (mixer.getJoinees().length == 0) {
_mixers.remove(key);
mixer.release();
mixer.getMediaSession().release();
return mixer;
}
return null;
}
SipListener
I implemented a SipListener as both SipSessionListener and SipErrorListener to handle the following events.
SipErrorListener.noAckRecieved. Because MediaSession and related resources are created when INVITE is created, we need clean it up when call setup is failed because of no ACK.SipSessionListener.sessionDestroyed. Similar, we need clean up the MediaSession when the SipSession is destroyed.
Test Sample
As other samples, simply download the WAR and deployed it under <prism>/apps directory. You can use two softphones dialing into the server to form a two party conference call. Please note Voxeo Prism free trial only comes with 2 media ports. Please contact Voxeo Support to request additional licenses.
Want to learn how Voxeo can help unlock your communications and deliver a better customer experience? Please contact us!
If you found this post interesting or helpful, please consider either subscribing via RSS, becoming a fan on Facebook, or following us on Twitter.













Given that we are a Mac-based company and that we release multiple Java-based products on Windows, Linux and MacOS X, I was pleased to see 
RSS Feed



