/*
* @(#)Handler.java 1.51 02/08/21
*
* Copyright 1996-1998 by Sun Microsystems, Inc.,
* 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
* All rights reserved.
*
* This software is the confidential and proprietary information
* of Sun Microsystems, Inc. ("Confidential Information"). You
* shall not disclose such Confidential Information and shall use
* it only in accordance with the terms of the license agreement
* you entered into with Sun.
*/
package com.sun.media.content.rtp;
import java.io.*;
import java.awt.*;
import java.net.*;
import java.awt.event.*;
import java.util.Vector;
import javax.media.*;
import javax.media.rtp.*;
import javax.media.rtp.event.*;
import javax.media.rtp.rtcp.*;
import javax.media.protocol.*;
import javax.media.format.AudioFormat;
import javax.media.format.VideoFormat;
import javax.media.Format;
import javax.media.format.FormatChangeEvent;
import javax.media.control.BufferControl;
import com.sun.media.util.*;
import com.sun.media.*;
import com.sun.media.ui.*;
import com.sun.media.controls.*;
import com.sun.media.rtp.*;
import com.sun.media.protocol.BufferListener;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
import com.ms.security.PermissionID;
import com.ms.security.PolicyEngine;
/**
* MediaPlayer extends BasicPlayer and uses MediaEngine to play media.
*/
public class Handler extends BasicPlayer
implements ReceiveStreamListener, BufferListener
{
RTPSessionMgr mgrs[] = null;
DataSource sources[] = null;
Player players[] = null;
Format formats[] = null;
Format formatChanged[] = null;
boolean realized[] = null;
boolean dataReady[] = null;
Vector locators = null; // Media Locators.
ControllerListener listener = new PlayerListener(this);
boolean playersRealized = false;
Object realizedSync = new Object();
Object closeSync = new Object();
Object dataSync = new Object();
Object stateLock = new Object();
private boolean closed = false;
private boolean audioEnabled = false;
private boolean videoEnabled = false;
private boolean prebuffer = false;
private boolean dataAllReady = false;
private static JMFSecurity jmfSecurity = null;
private static boolean securityPrivelege=false;
private Method m[] = new Method[1];
private Class cl[] = new Class[1];
private Object args[][] = new Object[1][0];
public Handler() {
framePositioning = false;
bufferControl = new BC();
stopThreadEnabled = true;
}
String sessionError = "cannot create and initialize the RTP session.";
protected boolean doRealize() {
super.doRealize();
try {
if (source instanceof RTPSocket) {
// this constructor will take care of initliasing and
// starting the session as well as updating the encodings
// from the RTPControl
mgrs = new RTPSessionMgr[1];
mgrs[1] = new RTPSessionMgr((RTPSocket)source);
mgrs[1].addReceiveStreamListener(this);
sources = new DataSource[1];
players = new Player[1];
formats = new Format[1];
realized = new boolean[1];
dataReady = new boolean[1];
formatChanged = new Format[1];
sources[0] = source;
dataReady[0] = false;
} else {
RTPMediaLocator rml;
InetAddress ipAddr;
SessionAddress localAddr = new SessionAddress();
SessionAddress destAddr;
mgrs = new RTPSessionMgr[locators.size()];
sources = new DataSource[locators.size()];
players = new Player[locators.size()];
formats = new Format[locators.size()];
realized = new boolean[locators.size()];
dataReady = new boolean[locators.size()];
formatChanged = new Format[locators.size()];
for (int i = 0; i < locators.size(); i++) {
rml = (RTPMediaLocator)locators.elementAt(i);
realized[i] = false;
mgrs[i] = (RTPSessionMgr) RTPManager.newInstance();
mgrs[i].addReceiveStreamListener(this);
ipAddr = InetAddress.getByName(rml.getSessionAddress());
if( ipAddr.isMulticastAddress()) {
// local and remote address pairs are identical:
localAddr= new SessionAddress( ipAddr,
rml.getSessionPort(),
rml.getTTL());
destAddr = new SessionAddress( ipAddr,
rml.getSessionPort(),
rml.getTTL());
} else {
localAddr= new SessionAddress(InetAddress.getLocalHost(),
rml.getSessionPort());
destAddr = new SessionAddress(ipAddr,
rml.getSessionPort());
}
mgrs[ i].initialize( localAddr);
if (prebuffer) {
BufferControl bc = (BufferControl)mgrs[i].getControl("javax.media.control.BufferControl");
bc.setBufferLength(bufferControl.getBufferLength());
bc.setMinimumThreshold(bufferControl.getMinimumThreshold());
}
mgrs[i].addTarget(destAddr);
}
}
} catch (Exception e){
Log.error("Cannot create the RTP Session: " + e.getMessage());
processError = sessionError;
return false;
}
// dont realize this meta player until our player is realized
try{
synchronized (realizedSync) {
while (!playersRealized && !isInterrupted() && !closed)
realizedSync.wait();
}
} catch (Exception e) {}
// If realize is being interrupted, return failure from realize.
if (closed || isInterrupted()) {
resetInterrupt();
processError = "no RTP data was received.";
return false;
}
return true;
}
protected void completeRealize() {
state = Realized;
super.completeRealize();
}
protected void doStart() {
super.doStart();
synchronized (dataSync) {
if (prebuffer) {
dataAllReady = false;
for (int i = 0; i < dataReady.length; i++) {
dataReady[i] = false;
((com.sun.media.protocol.rtp.DataSource)sources[i]).flush();
((com.sun.media.protocol.rtp.DataSource)sources[i]).prebuffer();
}
// Wait atmost 3 secs for data prebuffering.
if (!dataAllReady && !closed) {
try {
dataSync.wait(3000);
} catch (Exception e) {}
}
//System.err.println("data prebuffered: " + dataAllReady);
}
}
// Start the component players.
for (int i = 0; i < players.length; i++) {
try {
if (players[i] != null)
waitForStart(players[i]);
//players[i].start();
} catch (Exception e) {}
}
}
protected void doStop() {
super.doStop();
synchronized (dataSync) {
if (prebuffer) {
dataSync.notify();
}
}
// Stop the component players.
for (int i = 0; i < players.length; i++) {
try {
if (players[i] != null)
waitForStop(players[i]);
//players[i].stop();
} catch (Exception e) {}
}
}
protected void doDeallocate() {
for (int i = 0; i < players.length; i++) {
try {
if (players[i] != null)
players[i].deallocate();
} catch (Exception e) {}
}
synchronized (realizedSync) {
realizedSync.notify();
}
}
protected void doFailedRealize() {
synchronized (closeSync) {
for (int i = 0; i < mgrs.length; i++) {
if (mgrs[i] != null) {
mgrs[i].removeTargets( "Closing session from the RTP Handler");
mgrs[i].dispose();
mgrs[i] = null;
}
}
}
super.doFailedRealize();
}
protected void doClose() {
closed = true;
synchronized (realizedSync) {
realizedSync.notify();
}
synchronized (dataSync) {
dataSync.notifyAll();
}
stop();
for (int i = 0; i < players.length; i++) {
try {
if (players[i] != null)
players[i].close();
} catch (Exception e) {}
}
// close the RTP session.
synchronized (closeSync) {
for (int i = 0; i < mgrs.length; i++) {
if (mgrs[i] != null) {
mgrs[i].removeTargets( "Closing session from the RTP Handler");
mgrs[i].dispose();
mgrs[i] = null;
}
}
}
super.doClose();
}
public void setTimeBase(TimeBase tb) throws IncompatibleTimeBaseException {
}
protected TimeBase getMasterTimeBase() {
return new SystemTimeBase();
}
public float setRate(float rate) {
if (getState() < Realized) {
throwError(new NotRealizedError("Cannot set rate on an unrealized Player."));
}
// Cannot play any rate other than 1.0.
return 1.0f;
}
public void setStopTime(Time t) {
controllerSetStopTime(t);
}
protected void stopAtTime() {
controllerStopAtTime();
}
public synchronized void addController(Controller newController)
throws IncompatibleTimeBaseException {
int playerState = getState();
if (playerState == Started) {
throwError(new ClockStartedError("Cannot add controller to a started player"));
}
if ( (playerState == Unrealized) || (playerState == Realizing) ) {
throwError(new NotRealizedError("A Controller cannot be added to an Unrealized Player"));
}
throw new IncompatibleTimeBaseException();
}
protected boolean audioEnabled() {
return audioEnabled;
}
protected boolean videoEnabled() {
return videoEnabled;
}
private void sendMyEvent(ControllerEvent e) {
super.sendEvent(e);
}
public void update( ReceiveStreamEvent event) {
RTPSessionMgr mgr = (RTPSessionMgr)event.getSource();
int idx;
for (idx = 0; idx < mgrs.length; idx++) {
if (mgrs[idx] == mgr)
break;
}
if (idx >= mgrs.length) {
// Something's wrong.
System.err.println("Unknown manager: " + mgr);
return;
}
if( event instanceof RemotePayloadChangeEvent) {
Log.comment("Received an RTP PayloadChangeEvent");
RTPControl ctl = (RTPControl)sources[idx].getControl("javax.media.rtp.RTPControl");
if (ctl != null)
formatChanged[idx] = ctl.getFormat();
// payload has changed. we need to close the old player and
// create a new player
if (players[idx] != null) {
// stop player
stop();
// Now, close the rtp player.
// Wait till the old player is closed
waitForClose(players[idx]);
}
try {
// when the player was closed, its datasource was
// disconnected. Now we must reconnect the datasource before
// a player can be created for it.
sources[idx].connect();
players[idx] = javax.media.Manager.createPlayer(sources[idx]);
if (players[idx] == null) {
Log.error("Could not create player for the new RTP payload.");
return;
}
players[idx].addControllerListener(listener);
players[idx].realize();
}catch (Exception e){
Log.error("Could not create player for the new payload.");
}
} // payload change event
if (event instanceof NewReceiveStreamEvent){
if (players[idx] != null) {
// We've already created a player for this. So this must
// be the second stream in the same session. We won't
// deal with it.
return;
}
ReceiveStream stream = null;
try {
stream =((NewReceiveStreamEvent)event).getReceiveStream();
sources[idx] = stream.getDataSource();
RTPControl ctl = (RTPControl)sources[idx].getControl("javax.media.rtp.RTPControl");
if (ctl != null){
formats[idx] = ctl.getFormat();
if (formats[idx] instanceof AudioFormat)
audioEnabled = true;
else if (formats[idx] instanceof VideoFormat)
videoEnabled = true;
}
if (source instanceof RTPSocket)
((RTPSocket)source).setChild(sources[idx]);
else
((com.sun.media.protocol.rtp.DataSource)source).
setChild((com.sun.media.protocol.rtp.DataSource)sources[idx]);
// create a player by passing datasource to the Media Manager
players[idx] = javax.media.Manager.createPlayer(sources[idx]);
if (players[idx] == null)
return;
players[idx].addControllerListener(listener);
players[idx].realize();
if (prebuffer)
((com.sun.media.protocol.rtp.DataSource)sources[idx]).setBufferListener(this);
} catch (Exception e) {
Log.error("NewReceiveStreamEvent exception " + e.getMessage());
return;
}
}// instanceof newReceiveStreamEvent
}
private void waitForStart(Player p) {
(new StateWaiter()).waitForStart(p, true);
}
private void waitForStop(Player p) {
(new StateWaiter()).waitForStart(p, false);
}
private void waitForClose(Player p) {
(new StateWaiter()).waitForClose(p);
}
class StateWaiter implements ControllerListener {
boolean closeDown = false;
Object stateLock = new Object();
public void waitForStart(Player p, boolean startOn) {
p.addControllerListener(this);
if (startOn)
p.start();
else
p.stop();
synchronized (stateLock) {
while (((startOn && p.getState() != Started) ||
(!startOn && p.getState() == Started)) &&
!closeDown) {
try {
stateLock.wait(1000);
} catch (InterruptedException ie) {
break;
}
}
}
p.removeControllerListener(this);
}
public void waitForClose(Player p) {
p.addControllerListener(this);
p.close();
synchronized (stateLock) {
while (!closeDown) {
try {
stateLock.wait(1000);
} catch (InterruptedException ie) {
break;
}
}
}
p.removeControllerListener(this);
}
public void controllerUpdate(ControllerEvent ce) {
if (ce instanceof ControllerClosedEvent ||
ce instanceof ControllerErrorEvent) {
closeDown = true;
}
synchronized (stateLock) {
stateLock.notify();
}
}
}
public void setSource(javax.media.protocol.DataSource source)
throws IOException, IncompatibleSourceException
{
super.setSource(source);
if (source instanceof com.sun.media.protocol.rtp.DataSource){
MediaLocator ml = source.getLocator();
String mlStr = ml.getRemainder();
String str;
int start, idx;
start = 0;
while (mlStr.charAt(start) == '/')
start++;
locators = new Vector();
RTPMediaLocator rml;
try {
while (start < mlStr.length() &&
(idx = mlStr.indexOf("&", start)) != -1) {
str = mlStr.substring(start, idx);
rml = new RTPMediaLocator("rtp://" + str);
locators.addElement(rml);
start = idx+1;
}
if (start != 0)
str = mlStr.substring(start);
else
str = mlStr;
rml = new RTPMediaLocator("rtp://" + str);
locators.addElement(rml);
} catch (Exception e) {
throw new IncompatibleSourceException();
}
if (locators.size() > 1)
prebuffer = true;
} else if (!(source instanceof RTPSocket)) {
throw new IncompatibleSourceException();
}
// we will set the dynamic encoding for the one
// encoding we dont use from static RTP payloads
RTPControl ctl = (RTPControl)
source.getControl("javax.media.rtp.RTPControl");
if (ctl != null)
ctl.addFormat(new AudioFormat(AudioFormat.DVI_RTP,
44100,
4,
1),
18);
}
private void invalidateComp(){
controlComp = null;
controls = null;
}
/**
* Obtain the visiual component from the media engine.
*/
public Component getVisualComponent() {
/**
* Call the superclass method to ensure that restrictions
* on player methods are enforced
*/
super.getVisualComponent();
for (int i = 0; i < players.length; i++) {
if (players[i] != null && players[i].getVisualComponent() != null)
return players[i].getVisualComponent();
}
return null;
}
/**
* Return the list of controls from its slave controllers plus the
* ones that this player supports.
* @return the list of controls supported by this player.
*/
public Control [] getControls() {
if (controls != null)
return controls;
// build the list of controls. It is the total of all the
// controls from each controllers plus the ones that are maintained
// by the player itself (e.g. playbackControl).
Vector cv = new Vector();
if (cachingControl != null)
cv.addElement(cachingControl);
if (bufferControl != null)
cv.addElement(bufferControl);
Control c;
Object cs[];
Controller ctrller;
int i, size = players.length;
for (i = 0; i < size; i++) {
ctrller = (Controller)players[i];
cs = ctrller.getControls();
if (cs == null) continue;
for (int j = 0; j < cs.length; j++) {
cv.addElement(cs[j]);
}
}
Control ctrls[];
size = cv.size();
ctrls = new Control[size];
for (i = 0; i < size; i++) {
ctrls[i] = (Control)cv.elementAt(i);
}
// If the player has already been realized, we'll save what
// we've collected this time. Then next time, we won't need
// to go through this expensive search again.
if (getState() >= Realized)
controls = ctrls;
return ctrls;
}
public void updateStats() {
for (int i = 0; i < players.length; i++) {
if (players[i] != null)
((BasicPlayer)players[i]).updateStats();
}
}
public void minThresholdReached(DataSource ds) {
boolean ready = true;
synchronized (dataSync) {
for (int i = 0; i < sources.length; i++) {
if (sources[i] == ds)
dataReady[i] = true;
else if (!dataReady[i])
ready = false;
}
if (!ready)
return;
dataAllReady = true;
dataSync.notify();
}
}
class PlayerListener implements ControllerListener {
Handler handler;
public PlayerListener(Handler handler) {
this.handler = handler;
}
public synchronized void controllerUpdate(ControllerEvent ce) {
Player p = (Player)ce.getSourceController();
int idx;
if (p == null)
return;
for (idx = 0; idx < players.length; idx++) {
if (players[idx] == p)
break;
}
if (idx >= players.length) {
// Something's wrong.
System.err.println("Unknown player: " + p);
return;
}
if (ce instanceof RealizeCompleteEvent) {
// if this is a payload change, add this player as
// controller and start the meta player
if (formatChanged[idx] != null){
try {
// now send a FCE
// invalidate control component
invalidateComp();
FormatChangeEvent f = new FormatChangeEvent(
(Controller)handler,
formats[idx],
formatChanged[idx]);
((com.sun.media.content.rtp.Handler)handler).sendMyEvent(f);
formats[idx] = formatChanged[idx];
formatChanged[idx] = null;
// now start the meta player
//start();
}catch (Exception e){
e.getMessage();
}
}
realized[idx] = true;
// Check to see if all the players are realized.
for (int i = 0; i < realized.length; i++) {
if (!realized[i])
return;
}
// The meta player is considered to be realized if all
// the component players are all realized.
synchronized (realizedSync){
playersRealized = true;
realizedSync.notifyAll();
}
}
if (ce instanceof ControllerErrorEvent) {
players[idx].removeControllerListener( this );
Log.error("RTP Handler internal error: " + ce);
players[idx] = null;
}
}// end of controllerUpdate
} // end of PlayerListener;
/**
* BufferControl for the renderer.
*/
class BC implements BufferControl, Owned {
long len = -1;
long min = -1;
public long getBufferLength() {
if (len < 0)
return (prebuffer ? 750L : 125L);
return len;
}
public long setBufferLength(long time) {
len = time;
Log.comment("RTP Handler buffer length set: " + len);
return len;
}
public long getMinimumThreshold() {
if (min < 0)
return (prebuffer ? 125L : 0);
return min;
}
public long setMinimumThreshold(long time) {
min = time;
Log.comment("RTP Handler buffer minimum threshold: " + min);
return min;
}
public void setEnabledThreshold(boolean b) {
}
public boolean getEnabledThreshold() {
return (getMinimumThreshold() > 0);
}
public java.awt.Component getControlComponent() {
return null;
}
public Object getOwner() {
return Handler.this;
}
}
}// end of Handler
|