org.mobicents.javax.media.mscontrol.mediagroup.PlayerImpl Maven / Gradle / Ivy
package org.mobicents.javax.media.mscontrol.mediagroup;
import jain.protocol.ip.mgcp.JainMgcpCommandEvent;
import jain.protocol.ip.mgcp.JainMgcpListener;
import jain.protocol.ip.mgcp.JainMgcpResponseEvent;
import jain.protocol.ip.mgcp.message.Constants;
import jain.protocol.ip.mgcp.message.NotificationRequest;
import jain.protocol.ip.mgcp.message.Notify;
import jain.protocol.ip.mgcp.message.parms.EventName;
import jain.protocol.ip.mgcp.message.parms.RequestIdentifier;
import jain.protocol.ip.mgcp.message.parms.RequestedAction;
import jain.protocol.ip.mgcp.message.parms.RequestedEvent;
import jain.protocol.ip.mgcp.message.parms.ReturnCode;
import jain.protocol.ip.mgcp.pkg.MgcpEvent;
import java.net.URI;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import javax.media.mscontrol.MediaErr;
import javax.media.mscontrol.MediaEventListener;
import javax.media.mscontrol.MediaSession;
import javax.media.mscontrol.MsControlException;
import javax.media.mscontrol.Parameters;
import javax.media.mscontrol.Qualifier;
import javax.media.mscontrol.UnsupportedException;
import javax.media.mscontrol.Value;
import javax.media.mscontrol.mediagroup.MediaGroup;
import javax.media.mscontrol.mediagroup.Player;
import javax.media.mscontrol.mediagroup.PlayerEvent;
import javax.media.mscontrol.resource.RTC;
import org.mobicents.javax.media.mscontrol.spi.DriverImpl;
import org.mobicents.fsm.FSM;
import org.mobicents.fsm.Logger;
import org.mobicents.fsm.State;
import org.mobicents.fsm.StateEventHandler;
import org.mobicents.fsm.TransitionHandler;
import org.mobicents.fsm.UnknownTransitionException;
import org.mobicents.jsr309.mgcp.PackageAU;
/**
* b
* @author amit bhayani
*
*/
public class PlayerImpl implements Player, JainMgcpListener, Logger {
public final static String STATE_NULL = "NULL";
public final static String STATE_IDLE = "IDLE";
public final static String STATE_ACTIVE = "ACTIVE";
public final static String STATE_ACTIVATING = "ACTIVATING";
public final static String STATE_PAUSED = "PAUSED";
public final static String STATE_INVALID = "INVALID";
public final static String SIGNAL_CREATE = "CREATE";
public final static String SIGNAL_PLAY = "PLAY";
public final static String SIGNAL_STARTED = "STARTED";
public final static String SIGNAL_FAILED = "FAILED";
public final static String SIGNAL_START_PAUSED = "START_PAUSED";
public final static String SIGNAL_STOP = "STOP";
public final static String SIGNAL_PAUSE = "PAUSE";
public final static String SIGNAL_RESUME = "RESUME";
public final static String SIGNAL_PLAY_COMPLETED = "PLAY_COMPLETED";
public final static String SIGNAL_RELEASE = "RELEASE";
protected MediaGroupImpl parent = null;
private FSM fsm;
protected CopyOnWriteArrayList> listeners = new CopyOnWriteArrayList>();
protected String uri;
private String params;
private String returnCode;
private Qualifier qualifier;
private ReentrantLock lock = new ReentrantLock();
private Condition started = lock.newCondition();
private long startTime;
private long timeError = 0;
private long stopTime;
private long reqDuration;
private ConcurrentLinkedQueue playList = new ConcurrentLinkedQueue();
private MgcpSender mgcpSender;
//-------------------------------------------------------------------------------------------/
protected PlayerImpl(MediaGroupImpl parent) throws MsControlException {
this.parent = parent;
mgcpSender=new MgcpSender();
this.initFSM();
}
private void initFSM() {
fsm = new FSM(parent.getMediaSession().getDriver().getScheduler());
fsm.setLogger(this);
fsm.createState(STATE_NULL);
fsm.createState(STATE_IDLE);
fsm.createState(STATE_ACTIVATING);
fsm.createState(STATE_ACTIVE).setOnEnter(new OnStart());
fsm.createState(STATE_PAUSED);
fsm.createState(STATE_INVALID);
fsm.setStart(STATE_NULL);
fsm.setEnd(STATE_INVALID);
//state NULL
fsm.createTransition(SIGNAL_CREATE, STATE_NULL, STATE_IDLE);
fsm.createTransition(SIGNAL_RELEASE, STATE_NULL, STATE_INVALID);
//state IDLE
//sending request to server and waiting response
fsm.createTransition(SIGNAL_PLAY, STATE_IDLE, STATE_ACTIVATING).setHandler(new PlayRequest());
//player started in suspended mode, nothing is sent to server in this case
fsm.createTransition(SIGNAL_START_PAUSED, STATE_IDLE, STATE_PAUSED);
//player was stopped during activation, ask to stop it now
//TODO: Handle this transition!
//fsm.createTransition(SIGNAL_STARTED, STATE_IDLE, STATE_IDLE).setHandler(new StopRequest());
//Player release, silently go to INVALID state
fsm.createTransition(SIGNAL_RELEASE, STATE_IDLE, STATE_INVALID);
//state ACTIVATNG
//server said - ok
fsm.createTransition(SIGNAL_STARTED, STATE_ACTIVATING, STATE_ACTIVE);
//server said follow to the hand.
fsm.createTransition(SIGNAL_FAILED, STATE_ACTIVATING, STATE_IDLE);
//user asks to stop player
fsm.createTransition(SIGNAL_STOP, STATE_ACTIVATING, STATE_IDLE);
//playback failure
fsm.createTransition("FAILURE", STATE_ACTIVE, STATE_IDLE).setHandler(new PlayFailure(this));
//Player has been released. Do not forget to notify server
fsm.createTransition(SIGNAL_RELEASE, STATE_ACTIVATING, STATE_INVALID).setHandler(new StopRequest(this));
//state ACTIVE
//play finishes normaly
fsm.createTransition(SIGNAL_PLAY_COMPLETED, STATE_ACTIVE, STATE_IDLE).setHandler(new PlayCompleted(this));
//user asks to stop player
fsm.createTransition(SIGNAL_STOP, STATE_ACTIVE, STATE_IDLE).setHandler(new StopRequest(this));
//Player has been released. Do not forget to notify server
fsm.createTransition(SIGNAL_RELEASE, STATE_ACTIVE, STATE_INVALID).setHandler(new StopRequest(this));
//state PAUSED
//user ask to stop player but it was in puased mode so no need to notify server
fsm.createTransition(SIGNAL_STOP, STATE_PAUSED, STATE_IDLE);
//this transition means that some trigger activated. do not send anything more to server
fsm.createTransition(SIGNAL_RESUME, STATE_PAUSED, STATE_ACTIVE);
//Player has been released.
fsm.createTransition(SIGNAL_RELEASE, STATE_INVALID, STATE_INVALID);
try {
fsm.signal(SIGNAL_CREATE);
} catch (UnknownTransitionException e) {
}
}
// Player methods
@SuppressWarnings("static-access")
public void play(URI[] uris, RTC[] rtc, Parameters params) throws MsControlException {
if (rtc != null) {
verifyRTC(rtc);
}
this.checkURI(uris);
PlayTask task = new PlayTask(uris, rtc, params);
//user calls play during the current work?
if (fsm.getState().getName().equals(STATE_IDLE)) {
playList.offer(task);
} else {
//no specific behaviour?
if (params == null) {
throw new MsControlException("Busy now: state=" + fsm.getState());
}
Value behaviour = (Value) params.get(Player.BEHAVIOUR_IF_BUSY);
playList.offer(task);
if (Player.FAIL_IF_BUSY.equals(behaviour)) {
//queue task and send stop
throw new MsControlException("Busy now");
}
if (Player.STOP_IF_BUSY.equals(behaviour)) {
//queue task and send stop
try {
fsm.signal(SIGNAL_STOP);
} catch (UnknownTransitionException e) {
}
}
}
new Thread(new Starter()).start();
lock.lock();
try {
try {
started.await();
} catch (InterruptedException e) {
}
} finally {
lock.unlock();
}
}
@SuppressWarnings("static-access")
public void play(URI uri, RTC[] rtc, Parameters params) throws MsControlException {
play(new URI[]{uri}, rtc, params);
}
// Resource Methods
public MediaGroup getContainer() {
return this.parent;
}
// MediaEventNotifier methods
public void addListener(MediaEventListener listener) {
this.listeners.add(listener);
}
public void removeListener(MediaEventListener listener) {
this.listeners.remove(listener);
}
public MediaSession getMediaSession() {
return this.parent.getMediaSession();
}
protected void update(PlayerEvent anEvent) {
}
private void checkURI(URI[] uris) throws MsControlException {
if (uris == null) {
throw new MsControlException("URI[] cannot be null");
}
for (URI uri : uris) {
if (uri == null) {
throw new MsControlException("URI cannot be null");
}
if (uri.getScheme().equalsIgnoreCase("data")) {
continue;
}
}
}
public void stop(boolean stopAll) {
try {
fsm.signal(SIGNAL_STOP);
} catch (UnknownTransitionException e) {
}
}
private void verifyRTC(RTC[] rtc) throws UnsupportedException {
for (RTC r: rtc ) {
if (r.getTrigger() == Player.PLAY_START && r.getAction() == Player.STOP) {
throw new UnsupportedException("Invalid RTC");
}
}
}
protected void fireEvent(PlayerEvent event) {
new Thread(new EventSender(event)).start();
}
private String createParams(URI[] uris, Parameters params) {
StringBuilder buff = new StringBuilder();
//add URI list of the player
if (uris.length == 1) {
buff.append("an=").append(uris[0].toString());
} else {
buff.append("an=").append(uris[0].toString());
for (int i = 1; i < uris.length; i++) {
buff.append(";");
buff.append(uris[i].toString());
}
}
this.qualifier = PlayerEvent.END_OF_PLAY_LIST;
//max duraion if specified
if (params != null && params.containsKey(Player.MAX_DURATION)) {
long du = ((Integer)params.get(Player.MAX_DURATION));
buff.append(" ");
buff.append("du=").append(du);
if (du > 0) {
this.qualifier = PlayerEvent.DURATION_EXCEEDED;
}
this.reqDuration = du;
}
//start offset if specified
if (params != null && params.containsKey(Player.START_OFFSET)) {
buff.append(" ");
buff.append("of=").append(params.get(Player.START_OFFSET));
}
//iterations if specified
if (params != null && params.containsKey(Player.REPEAT_COUNT)) {
int it = 0;
if (params.get(Player.REPEAT_COUNT) instanceof Integer) {
try {
it = ((Integer)params.get(Player.REPEAT_COUNT));
} catch (Exception e) {
}
}
if (it < 0) it = 0;
buff.append(" ");
buff.append("it=").append(it);
}
//intervals if specified
if (params != null && params.containsKey(Player.INTERVAL)) {
buff.append(" ");
buff.append("iv=").append(params.get(Player.INTERVAL));
}
return buff.toString();
}
private void requestAnnouncement() {
//generate request identifier and transaction ID
RequestIdentifier reqID = parent.nextRequestID();
int txID = parent.getMediaSession().getDriver().getNextTxID();
//constructs request
NotificationRequest req = new NotificationRequest(this, parent.getEndpoint().getIdentifier(), reqID);
RequestedAction[] actions = new RequestedAction[] { RequestedAction.NotifyImmediately };
ArrayList signalList = new ArrayList();
ArrayList eventList = new ArrayList();
//player signals
signalList.add(new EventName(PackageAU.Name, PackageAU.pa.withParm(params)));
//player events
eventList.add(new RequestedEvent(new EventName(PackageAU.Name, MgcpEvent.oc), actions));
eventList.add(new RequestedEvent(new EventName(PackageAU.Name, MgcpEvent.of), actions));
EventName[] signals = new EventName[signalList.size()];
signalList.toArray(signals);
RequestedEvent[] events = new RequestedEvent[eventList.size()];
eventList.toArray(events);
//new EventName(auPackageName, MgcpEvent.oc, connId)
req.setRequestedEvents(events);
req.setSignalRequests(signals);
req.setTransactionHandle(txID);
DriverImpl driver=parent.getMediaSession().getDriver();
req.setNotifiedEntity(driver.getCallAgent());
driver.attach(txID, this);
driver.attach(reqID, this);
if(this.parent.isStopping())
mgcpSender.init(driver, req);
else
driver.send(req);
}
private void requestStop() {
//generate request identifier and transaction ID
RequestIdentifier reqID = parent.nextRequestID();
int txID = parent.getMediaSession().getDriver().getNextTxID();
//constructs request
NotificationRequest req = new NotificationRequest(this, parent.getEndpoint().getIdentifier(), reqID);
req.setTransactionHandle(txID);
DriverImpl driver=parent.getMediaSession().getDriver();
req.setNotifiedEntity(driver.getCallAgent());
driver.attach(txID, this);
driver.attach(reqID, this);
driver.send(req);
}
/**
* Fires event.
*
* @param evt the event to be fired
*/
private void fireEvent(EventName eventName) {
this.returnCode = eventName.getEventIdentifier().getParms();
switch (eventName.getEventIdentifier().intValue()) {
case MgcpEvent.REPORT_ON_COMPLETION :
try {
fsm.signal(SIGNAL_PLAY_COMPLETED);
} catch (UnknownTransitionException e) {
e.printStackTrace();
}
break;
case MgcpEvent.REPORT_FAILURE :
try {
fsm.signal("FAILURE");
} catch (UnknownTransitionException e) {
e.printStackTrace();
}
break;
}
}
public void stopCompleted()
{
if(mgcpSender.waiting)
mgcpSender.run();
}
private class PlayRequest implements TransitionHandler {
public void process(State state) {
requestAnnouncement();
}
}
private class PlayCompleted implements TransitionHandler {
private PlayerImpl player;
protected PlayCompleted(PlayerImpl player) {
this.player = player;
}
public void process(State state) {
stopTime = System.currentTimeMillis();
if (Math.abs(stopTime - startTime - reqDuration) > 1000) {
qualifier = PlayerEvent.END_OF_PLAY_LIST;
}
PlayerEventImpl evt = new PlayerEventImpl(player, PlayerEvent.PLAY_COMPLETED, true, qualifier, null);
evt.setOffset((int)(stopTime - startTime));
fireEvent(evt);
if (!playList.isEmpty()) {
new Thread(new Starter()).start();
}
}
}
private class PlayFailure implements TransitionHandler {
private PlayerImpl player;
protected PlayFailure(PlayerImpl player) {
this.player = player;
}
public void process(State state) {
MediaErr error = null;
if (returnCode.equals("rc=301")) {
error = MediaErr.BAD_ARG;
} else if (returnCode.equals("rc=312")) {
error = MediaErr.NOT_FOUND;
}
stopTime = System.currentTimeMillis();
PlayerEventImpl evt = new PlayerEventImpl(player, PlayerEvent.PLAY_COMPLETED, false, PlayerEvent.NO_QUALIFIER, null, error, "");
evt.setOffset((int)(stopTime - startTime));
fireEvent(evt);
if (!playList.isEmpty()) {
new Thread(new Starter()).start();
}
}
}
private class StopRequest implements TransitionHandler {
private PlayerImpl player;
protected StopRequest(PlayerImpl player) {
this.player = player;
}
public void process(State state) {
requestStop();
PlayerEventImpl evt = new PlayerEventImpl(player, PlayerEvent.PLAY_COMPLETED, true, PlayerEvent.STOPPED, null);
evt.setOffset((int)(stopTime - startTime));
fireEvent(evt);
}
}
private class OnStart implements StateEventHandler {
public void onEvent(State state) {
startTime = System.currentTimeMillis() + timeError;
//notify that player started
lock.lock();
try {
started.signal();
} finally {
lock.unlock();
}
}
}
public void processMgcpCommandEvent(JainMgcpCommandEvent event) {
switch (event.getObjectIdentifier()) {
case Constants.CMD_NOTIFY :
Notify notify = (Notify) event;
EventName[] events = notify.getObservedEvents();
for (EventName evt: events) {
fireEvent(evt);
}
break;
default :
return;
}
}
public void processMgcpResponseEvent(JainMgcpResponseEvent event) {
switch (event.getReturnCode().getValue()) {
case ReturnCode.TRANSACTION_BEING_EXECUTED :
break;
case ReturnCode.TRANSACTION_EXECUTED_NORMALLY :
try {
fsm.signal(SIGNAL_STARTED);
} catch (UnknownTransitionException e) {
}
break;
default :
try {
fsm.signal(SIGNAL_FAILED);
} catch (UnknownTransitionException e) {
}
}
}
private class PlayTask {
private URI[] uris;
private RTC[] rtc;
private Parameters params;
protected PlayTask(URI[] uris, RTC[] rtc, Parameters params) {
this.uris = uris;
this.rtc = rtc;
this.params = params;
}
}
private class Starter implements Runnable {
public void run() {
PlayTask task = playList.poll();
params = createParams(task.uris, task.params);
try {
fsm.signal(SIGNAL_PLAY);
} catch (UnknownTransitionException e) {
}
}
}
private class EventSender implements Runnable {
private PlayerEvent evt;
public EventSender(PlayerEvent evt) {
this.evt = evt;
}
public void run() {
//needed to disable Busy Now Errors.
try{
Thread.sleep(2);
} catch( InterruptedException e ) {
}
for (MediaEventListener l : listeners) {
l.onEvent(evt);
}
}
}
private class MgcpSender {
private DriverImpl driver;
private NotificationRequest req;
private Boolean waiting=false;
public MgcpSender()
{
}
public void init(DriverImpl driver,NotificationRequest req)
{
this.driver=driver;
this.req=req;
waiting=true;
}
public void run() {
driver.send(req);
waiting=false;
}
}
public void info(String s) {
parent.info(s);
}
public void debug(String s) {
parent.debug(s);
}
public void warn(String s) {
parent.warn(s);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy