Skip Headers
Oracle® Communications Converged Application Server Developer's Guide
Release 5.1

Part Number E27707-01
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
PDF · Mobi · ePub

23 Conferencing With Media Control

This chapter describes how to implement conferencing applications with media server interactions using Service Foundation Toolkit (SFT).

Conferencing with Media Control

A common use of a media server is to provide multi-user conferences that incorporate audio and video conferencing features. A conference is a unique instance of a multi-party conversation, and consists of a Focus and a Mixer.

The Focus identifies a conference. It acts as a SIP UA and is addressed by SIP URI. The Focus maintains a SIP signaling relationship with each participant in the conference, and is responsible for ensuring that each participant receives the media that makes up the conference. The Focus implements conference policies, such as determining who can join a conference, and is responsible for interpreting the media policies.

The Mixer is responsible for mixing all members' data streams and sending them back to all conference members via an RTP channel. Data streams may be text, audio, and video. A Mixer is always under the control of the Focus. The Mixer enforces the appropriate media policies. The Mixer represents a JSR 309 compliant media server.

About the Conferencing and Media Control Interfaces

The Focus interface let's you create a voice conference with a Mixer, such that you can use the JSR 309 APIs to create a Mixer, and encapsulate the Mixer within the Focus interface to create a conference.

Conferencing and Media Control makes use of the following classes in the com.oracle.sft.api package. For more information on these interfaces and their usage, refer to the Converged Application Server API Reference.

Table 23-1 Focus Interfaces

Class Description

Focus

Focus extends the Participant class, and represents a participant in a conference. Applications use the Focus interface to initialize the media server using JSR309 API, and create a Mixer.


Table 23-2 lists methods accessed from the CommunicationSession interface that you can use to create a conference.

Table 23-2 Methods Defined by the CommunicationSession Interface

Method Description

createConference(Focus f)

Creates a conference without a specified name.

createConference(String name, Focus f)

Creates a conference with a specified name and focus instance.

createConference(String name, Conversation c, Focus f)

Create a conference from a Conversation instance using the focus instance. If no name is specified (null), the conference name will use the name assigned to the focus.


Creating a Conference With the Focus Interface

Example 23-0 creates a JSR 309-compliant Mixer which is encapsulated by the Focus interface. The Mixer is created using the javax.media.mscontrol.mixer.MediaMixer class, which serves as the base for a conferencing service. When a conference is instantiated, JSR 309 objects are created using MsControlFactory. The Focus interface encapsulates the Mixer, and connects and mixes multiple participants in a conference.

Example 23-1 Creating a Mixer And Encapsulating It Within A Focus

...
String msJndiName = "mediaServerJNDIName";
MsControlFactory msFactory = (MsControlFactory)(new InitialContext()).lookup(msJndiName);
MediaSession mSession = msFactory.createMediaSession();
MediaMixer mixer = (MediaMixer) mSession.createMediaMixer(MediaMixer.AUDIO_VIDEO);
Focus focus = sess.createParticipant(Focus.class, "FOCUS_AUDIO_VIDEO", mixer);
Conference conf = sess.createConference(null, call, focus);
...

Example 23-2 creates the Java class ConfFocusBean, which is a JSR 309 conference application. ConfFocusBean encapsulates the Mixer within the Focus interface, and illustrates the use of the @CommunicationEvent annotation in creating and establishing communication within the conference, and @ParticpantEvent annotation in joining SIP dialogs within the conference.

Example 23-2 Creating A JSR 309 Conference Using The Focus Interface

package com.oracle.sft.testapp;
 
import javax.media.mscontrol.MediaSession;
import javax.media.mscontrol.MsControlException;
import javax.media.mscontrol.MsControlFactory;
import javax.media.mscontrol.join.Joinable;
import javax.media.mscontrol.mixer.MediaMixer;
import javax.naming.InitialContext;
import javax.naming.NamingException;
 
import com.oracle.sft.api.Communication;
import com.oracle.sft.api.CommunicationContext;
import com.oracle.sft.api.CommunicationService;
import com.oracle.sft.api.CommunicationSession;
import com.oracle.sft.api.Conference;
import com.oracle.sft.api.Context;
import com.oracle.sft.api.Conversation;
import com.oracle.sft.api.Focus;
import com.oracle.sft.api.bean.CommunicationBean;
import com.oracle.sft.api.bean.CommunicationEvent;
import com.oracle.sft.api.bean.ParticipantEvent;
 
@ServiceAttributes(mscontrolJndiName = "mscontrol.OCMP")
@ServiceAttributes(mscontrolJndiName = "mscontrol.dlg309")

@CommunicationBean
public class ConfFocusBean {
 
  @Context CommunicationSession sess;
  @Context CommunicationContext ctx;
  @Context CommunicationService service;
 
  @CommunicationEvent(type = CommunicationEvent.Type.INITIALIZATION)
  void handleInit() {
    
    Conversation call = (Conversation) ctx.getCommunication();
    String calleeName = call.getCallee().getName();
 
    if(calleeName.equals("conf1@example.com")) {
      MediaMixer mixer = this.createMixer();
      Focus focus = sess.createParticipant(Focus.class, "FOCUS_AUDIO_VIDEO", mixer);
      // The Conference name is set to null, and uses the Focus's name.
      Conference conf = sess.createConference(null, call, focus);
    }
  }
 
  @CommunicationEvent(type = CommunicationEvent.Type.STARTED)
  void handleStarted() {
    Joinable j = ctx.getParticipant().getJoinable();
    Communication c = ctx.getCommunication();
  }
  
  @CommunicationEvent(type = CommunicationEvent.Type.ESTABLISHED)
  void handleEstablised() {
    Joinable j = ctx.getParticipant().getJoinable();
    Communication c = ctx.getCommunication();
  }
  
  @ParticipantEvent(type = ParticipantEvent.Type.JOINED)
  void handleJoin() {
    Communication c = ctx.getCommunication();
  }
  
  private MediaMixer createMixer() {
    String msJndiName = "__SYSTEM.resource.jvb-ra#javax.media.mscontrol.MsControlFactory";
    MsControlFactory msFactory;
    try {
      msFactory = (MsControlFactory)(new InitialContext()).lookup(msJndiName);
      MediaSession mSession = msFactory.createMediaSession();
      MediaMixer mixer = (MediaMixer) mSession.createMediaMixer(MediaMixer.AUDIO_VIDEO);
      return mixer;
    } catch (NamingException e) {
      e.printStackTrace();
    } catch (MsControlException e) {
      e.printStackTrace();
    }
    return null;
  }
}

Creating Conferences Using Resource-Contained Lists

In certain types of multimedia communications, a SIP request is distributed to a group of SIP User Agents (UAs). The sender sends a single SIP request to a server which further distributes the request to the group. This SIP request contains a list of Uniform Resource Identifiers (URIs). The URI list is expressed as an XML document that allows the sender of the request to qualify a recipient using a copy control level similar to the copy control level of existing e-mail systems.

The XML resource list (defined in RFC 4826) provides a mechanism for describing a list of resources, however, there is a need for a copy control attribute to determine whether a resource is receiving a SIP request as a primary recipient, a carbon copy, or a blind carbon copy. This is similar to e-mail systems where the sender can categorize each recipient as “to”, “cc”, or “bcc.” RFC 5366 defines the copy control XML extension to the XML resource list format. Example 23-3 shows a list that follows the XML resource list document extended with format extension for representing copy control attributes in resource lists.

Example 23-3 URI List for Conference Establishment Contained in the SIP Header

<?xml version="1.0" encoding="UTF-8"?>
<resource-lists xmlns="urn:ietf:params:xml:ns:resource-lists"
          xmlns:cp="urn:ietf:params:xml:ns:copycontrol">
   <list>
     <entry uri="sip:bill@example.com" cp:copyControl="to"/>
     <entry uri="sip:joe@example.com" cp:copyControl="cc" />
     <entry uri="sip:ted@example.net" cp:copyControl="bcc"/>
   </list>
</resource-lists>

A conference server supporting RFC 5366, in which a received INVITE triggers the conference focus UAS to initiate multiple INVITEs as a UAC, operates as a media termination B2BUA when performing that function.

When an incoming INVITE request with the SIP Content-Type header containing either of:

  • Content-Type: application/resource-lists+xml

  • Content-Type: multipart/mixed;boundary="boundary1" with sub Content-Type: application/resource-lists+xml

is received the com.oracle.sft.api.ResourceListsMessage interface creates the message, and sets it to the current CommunicationContext.

The ResourceListsMessage interface represents the ProtocolMessage (the actual SIP Message) interface. Like other types of Message interfaces, ProtocolMessage is available to the application from the CommunicationContext.getMessage() method. Note that ProtocolMessage is expected to be used only by advanced users. Also, changes made to the actual protocol objects may impact the behavior of the SFT runtime. Typically an SFT application adds or removes custom headers or parameters needed by a specific application.

com.oracle.sft.api.UserParticipant contains recipient list history information.

The interface defines the methods setRecipientListHistory(ResourceLists recipientLists) and getRecipientListHistory() that you can use to set and retrieve recipient list history information contained by the URI list in the INVITE to the user participant.

When an INVITE is sent to the user participant, the SIP Content-Type is multipart/mixed.

Table 23-3 lists the interfaces defined in the com.oracle.sft.api.rls package to handle the XML resource list carried in the INVITE request. For more information on these interfaces and their usage, refer to the Converged Application Server API Reference.

Table 23-3 Interfaces for Handling XML Resource Lists

Interfaces Description

DisplayName

Represents the display-nameType complex type.

Entry

Represents the entryType complex type

EntryRef

Represents the entry-refType complex type.

External

Represents the externalType complex type.

ResourceList

Represents the resource-list contained in a resource-lists.

ResourceListData

Represents the data carried in the ResourceList.

ResourceLists

Represents the resource-lists defined in RFC 4826 and RFC 5364.

ResourceListsFactory

Factory interface for creating resource lists elements.


Example 23-4 illustrates how to create a conference using a resource list provided in the SIP message from the conference initiator.

Example 23-4 Using the ResourceLists Interface

...
Message msg = ctx.getMessage(); 
if (msg instanceof ResourceListsMessage) {
    ResourceLists rlss = ((ResourceListsMessage) msg).getResourceLists();
}
...

ResourceListsFactory rlssFactory = service.createResourceListsFactory();
ResourceLists recipientLists = rlssFactory.createRecipientLists(rlss);
ResourceLists recipientHistoryLists = rlssFactory.createRecipientHistoryLists(rlss);
 
 List<ResourceList> dataList = recipientLists.getResourceList();
    for (ResourceList rls : dataList) {
      List<Entry> entryList = rls.getResourceListData();
      for (Entry entry: entryList) {
        String userName = entry.getName();
        UserParticipant up = sess.createParticipant(UserParticipant.class, userName);
      up.setRecipientHistoryList(recipientHistLists);
        comm.addParticipant(up);
      }
    }

Example 23-5 creates a conference using a resource-contained list. A CommunicationBean named ConfRLSBean is created, which creates a conference and distributes the request to a group using a resource-contained list to specify copy control to the individual UAS receiving the INVITE. This SIP request contains a list of Uniform Resource Identifier (URI) requests— expressed as an XML document—that allows the sender of the request to qualify a recipient using a copy control level similar to the copy control level of existing e-mail systems.

Example 23-5 Creating a Conference using a Resource-Contained List

package com.oracle.sft.testapp;
 
import java.util.List;
import java.util.logging.Logger;
 
import com.oracle.sft.api.Agent;
import com.oracle.sft.api.Communication;
import com.oracle.sft.api.CommunicationContext;
import com.oracle.sft.api.CommunicationService;
import com.oracle.sft.api.CommunicationSession;
import com.oracle.sft.api.Conference;
import com.oracle.sft.api.Context;
import com.oracle.sft.api.Conversation;
import com.oracle.sft.api.Message;
import com.oracle.sft.api.ResourceListsMessage;
import com.oracle.sft.api.UserParticipant;
import com.oracle.sft.api.bean.CommunicationBean;
import com.oracle.sft.api.bean.CommunicationEvent;
import com.oracle.sft.api.bean.ParticipantEvent;
import com.oracle.sft.api.rls.Entry;
import com.oracle.sft.api.rls.ResourceList;
import com.oracle.sft.api.rls.ResourceLists;
import com.oracle.sft.api.rls.ResourceListsFactory;
 
// @ServiceAttributes(mscontrolJndiName = "mscontrol.OCMP")
// @ServiceAttributes(mscontrolJndiName = "mscontrol.dlg309")
 
@CommunicationBean
public class ConfRLSBean {
 
  @Context CommunicationSession sess;
  @Context CommunicationContext<> ctx;
  @Context CommunicationService service;
  private Logger logger = Logger.getLogger("sft.test");
 
  @CommunicationEvent(type = CommunicationEvent.Type.INITIALIZATION)
  void handleInit() {
    Conversation call = (Conversation) ctx.getCommunication();
    String calleeName = call.getCallee().getName();
    logger.info(call.getCaller().getName() + " call " + calleeName);
 
    if(calleeName.equals("conf1@example.com")) {
      Conference conf = sess.createConference(calleeName, call);
      Message msg = ctx.getMessage();
      ResourceLists rlss = handleMsg(msg);
      if (conf.getAgent("test") == null) {
        conf.addAgent("test", new TestAgent(rlss));
      }
    }
  }
 
  @CommunicationEvent(type = CommunicationEvent.Type.STARTED)
  void handleStart() {
    String confName = ctx.getCommunication().getName();
    logger.info(confName + " is started.");
  }
 
  @CommunicationEvent(type = CommunicationEvent.Type.ESTABLISHED)
  void handleEstablished() {
    Communication comm = ctx.getCommunication();
    logger.info(comm.getName() + " established");
 
    Agent<ResourceLists> testAgent = comm.getAgent("test");
    ResourceLists incomingRlss = testAgent.get();
 
    System.out.println("incomingRlss size : ");
    List<ResourceList> resourceList = incomingRlss.getResourceList();
    for (ResourceList rls: resourceList) {
      System.out.println(rls.getResourceListData().size());
    }
    System.out.println(incomingRlss.toString());
 
    ResourceListsFactory rlssFactory = service.createResourceListsFactory();
 
    ResourceLists recipientLists = rlssFactory.createRecipientLists(incomingRlss);
    resourceList = recipientLists.getResourceList();
    System.out.println("recipientLists size : ");
    for (ResourceList rls: resourceList) {
      System.out.println(rls.getResourceListData().size());
    }
    System.out.println(recipientLists.toString());
 
    ResourceLists recipientHistLists = rlssFactory.createRecipientHistoryLists(incomingRlss);
    System.out.println("recipientHistLists size : ");
    resourceList = recipientHistLists.getResourceList();
    for (ResourceList rls: resourceList) {
      System.out.println(rls.getResourceListData().size());
    }
    System.out.println(recipientHistLists.toString());
 
    List<ResourceList> dataList = recipientLists.getResourceList();
 
    for (ResourceList rls : dataList) {
      List<Entry> entryList = rls.getResourceListData();
      for (Entry entry: entryList) {
        String userName = entry.getName();
        UserParticipant up = sess.createParticipant(UserParticipant.class, userName);
//        up.setRecipientHistoryList(recipientHistLists);
        comm.addParticipant(up);
      }
    }
  }
 
  @CommunicationEvent(type = CommunicationEvent.Type.FINISHED)
  void handleEnd() {
    String confName = ctx.getCommunication().getName();
    logger.info(confName + " is finished.");
  }
 
  // Handle ParticipantEvent
 
  @ParticipantEvent(type = ParticipantEvent.Type.JOINED)
  void handleJoin() {
    logger.info(ctx.getParticipant().getName() + " joined");
  }
 
  private ResourceLists handleMsg(Message msg) {
    if (msg instanceof ResourceListsMessage) {
      ResourceLists rlss = ((ResourceListsMessage) msg).getResourceLists();
      return rlss;
    }
    return null;
  }
}
 
class TestAgent extends Agent<ResourceLists> {
  private static final long serialVersionUID = 1L;
 
  TestAgent(ResourceLists rls) {
    super(false);
    super.set(rls);
  }
}

Ad-Hoc Conferencing

RFC 4575 defines an event package for conferencing. The conference event package allows a user to subscribe to information relating to a conference.

The conference event package allows a user to subscribe to information relating to a conference. Within the SIP protocol, conferences are represented by URIs. These URIs identify the Focus, a SIP user agent (UA), that is responsible for ensuring that all users in the conference can communicate with each other. The Focus has sufficient information about the state of the conference to inform subscribers about it.

The following is supported for event notifications via the conference event package:

Configuring the Conference Event Package

You can configure a conference with:

  • Miminum expiration time

  • Maximum expiration time

  • Default expiration time

  • Maximum number of participants

The configuration is done using:

  • The minExpirationTime, defaultExpirationTime, maxExpirationTime, and maxNumOfSubscriptions elements of the sft.xml deployment descriptor.

  • The @ServiceAttributes annotation.

The times are given in seconds.

Example 23-6 illustrates event notification expiration times and the maximum number of subscribers to a conference using the conferenceEventConfig element of the sft.xml deployment descriptor.

Example 23-6 Conference Event Expiration in SFT.XML Deployment Descriptor

<service-attributes>
  <conferenceEventConfig>
      <minExpirationTime>100</minExpirationTime>
      <defaultExpirationTime>1800</defaultExpirationTime>
      <maxExpirationTime>3600</maxExpirationTime>
      <maxNumOfSubscriptions>100</maxNumOfSubscriptions>
  </conferenceEventConfig>
</service-attributes>

Example 23-7 specifies event notification expiration times and the maximum number of subscribers using the @ServiceAttributes annotation. Use this annotation in the CommunicationBean Java class you create to implement the conferencing application.

Example 23-7 Specifying Bandwidth Using the @ServiceAttributes Annotation

@ServiceAttributes (conferenceEventConfig = {100, 3600, 3600, 100})
@CommunicationBean
public class ConferenceBean
...

To learn more about configuring event notification for conferences with the @ServiceAttributes annotation, see the Converged Application Server API Reference.

Handling Subscription and Notification Events

The conference notification service allows conference-aware participants to subscribe to it, and receive notifications that contain a list of participants. Subscribers are notified when a participant joins or leaves a conference. The conference notification service also allows a user to obtain a list of current subscribers.

The conference notification service uses the @CommunicationEvent annotation's SUBSCRIPTION and NOTIFICATION event types.

Handling Conference Subscription Events

Example 23-8 illustrates the use of the SUBSCRIPTION event type in conjunction with the SubscriptionPolicy interface to perform authorization. SubscriptionPolicy gets the subscription information from @CommunicationContext using the ContextElement interface.

  • If the application sets the Policy to SubscriptionPolicy.ACCEPTED, the subscription will be created successfully, and a SIP 200 OK response sent to the subscriber.

  • If the application sets the Policy to SubscriptionPolicy.FORBIDDEN, a SIP 403 - Forbidden response is sent to the subscriber.

  • If the application does not change the Policy, the default value is set to SubscriptionPolicy.NONE. In this case, if the subscriber is authenticated by Converged Application Server, the SFT application accepts the request.

Example 23-8 Handling SUBSCRIPTION Type

... 
@CommunicationEvent(type = CommunicationEvent.Type.SUBSCRIPTION)
  void handleSub() {
    // The communication name is the same as the resourceId
    String confName = ctx.getCommunication().getName();
    //Get Subscription information from CommunicationContext.
    ContextElement SubsElement =
    ctx.getContextElement(SubscriptionPolicy.class);
    
    if (SubsElement != null) {
      SubscriptionPolicy subsPolicy = (SubscriptionPolicy)SubsElement;
      // Get information about the subscription for authorization.
      List<String> accepList = subsPolicy.getAccept();
      String contentType = subsPolicy.getContentType();
      Map<String, String> parameters = subsPolicy.getEventHeaderParameters();
      String resourceId = subsPolicy.getResourceId();
      String subscriber = subsPolicy.getSubscriber();
      String eventName = subsPolicy.getEventName();
      
      /* Perform authorization and reject alice@example.com 
       * subscription to conf1@example.com
      */ 
      if (subscriber.equals("alice@example.com")) {
        if (resourceId.equals("conf1@example.com")) {
          subsPolicy.reject();
        }
      }
    }
  }
...

Handling Conference Notification Events

Indicates a new notification has been created. The conference application can change the notification content in this event.

Example 23-9 illustrates the use of the NOTIFICATION event type. The subscriber Alice is removed from the list of subscribers that get notifications about the conference. Alice's info is explicitly removed from the notification details sent to Bob.

Example 23-9 Handling the NOTIFICATION Type

...
  @CommunicationEvent(type = CommunicationEvent.Type.NOTIFICATION)
  void handleNotify() {
    // The communication name is the same as the resourceId
    String confName = ctx.getCommunication().getName();
    // Get ConferenceResource from CommunicationContext.
    ContextElement element = ctx.getContextElement(ConferenceResource.class);
    if (element != null) {
      ConferenceResource confrenceRes = (ConferenceResource) element;
      // Get all the information about the Resource. Application can 
      // use them to do the authorization for notifications.
      ConferenceInfo defaultConferenceInfo =
                     confrenceRes.getDefaultConferenceInfo();
      Collection<String> subscribers = confrenceRes.getSubscribers();
      String resourceId = confrenceRes.getResourceId();
      // ******************************* 
      // An example of authorization * 
      // *******************************
      if (resourceId.equals("aConference@example.com")) {
        // Alice can not get notification.
        if (subscribers.contains("alice@example.com")) {
          confrenceRes.removeSubscriber("alice@example.com");
        }
        // Bob can not get Alice's state from conference.
        if (subscribers.contains("alice@example.com")) {
          ConferenceInfo confInfoToBob =
                         defaultConferenceInfo.clone();
          Users users = confInfoToBob.getUsers();
          if (users != null) {
            List<User> userList = users.getUserList();
            ListIterator<User> it = userList.listIterator();
            while (it.hasNext()) {
              User user = it.next();
              if (user.getEntity().contains("alice@example.com")) {
                it.remove();
                confrenceRes.setDistinctConferenceInfo(
                             "sipp2@example.com", confInfoToBob);
              }
            }
          }
        }
      }
    }
  }
...