/*
* @(#)Handler.java 1.74 02/08/21
*
* Copyright (c) 1996-2002 Sun Microsystems, Inc. All rights reserved.
*/
package com.sun.media.content.rtsp;
import java.io.*;
import java.awt.*;
import java.net.*;
import java.util.Vector;
import javax.media.*;
import javax.media.format.*;
import javax.media.protocol.*;
import javax.media.rtp.*;
import javax.media.rtp.event.*;
import javax.media.rtp.rtcp.*;
import javax.media.control.BufferControl;
import javax.media.renderer.*;
import com.sun.media.*;
import com.sun.media.controls.RtspAdapter;
import com.sun.media.protocol.DelegateDataSource;
import com.sun.media.rtsp.*;
import com.sun.media.protocol.rtp.DataSource;
import com.sun.media.protocol.BufferListener;
/**
* MediaPlayer extends BasicPlayer and uses MediaEngine to play media.
*/
public class Handler extends BasicPlayer implements ReceiveStreamListener,
TimerListener, BufferListener {
// states:
private final int INITIALIZED = 0;
private final int REALIZED = 1;
private final int PLAYING = 2;
private final int PAUSING = 3;
private RtspUtil rtspUtil;
private Player players[];
private Vector playerList;
private boolean dataReceived;
private DataSource data_sources[];
private boolean track_ready[];
private String url;
private Object readySync = new Object();
// synchronization variable used in
// waitForState().
private Object stateSync = new Object();
// used to wait for a state until the state is
// reached or something failed. In this case
// the wait loop will be aborted.
private boolean waitFailed;
private int state;
private boolean first_pass = true;
public Handler() {
rtspUtil = new RtspUtil( this);
framePositioning = false;
playerList = new Vector();
state = INITIALIZED;
stopThreadEnabled = true;
}
protected synchronized boolean doRealize() {
boolean realized = super.doRealize();
if (realized) {
realized = initRtspSession();
if( !realized) {
processError= rtspUtil.getProcessError();
} else {
long duration= rtspUtil.getDuration();
if( duration > 0) {
sendEvent(new DurationUpdateEvent(this, new Time(duration)));
}
}
}
return realized;
}
private boolean initRtspSession() {
boolean realized = false;
rtspUtil.setUrl( url);
String ipAddress = rtspUtil.getServerIpAddress();
if (ipAddress == null) {
Log.error( "Invalid server address");
rtspUtil.setProcessError( "Invalid server address");
realized = false;
} else {
realized = rtspUtil.createConnection();
if( realized) {
realized = rtspUtil.rtspSetup();
try {
InetAddress destaddr = InetAddress.getByName(ipAddress);
int numberOfTracks= rtspUtil.getNumberOfTracks();
int server_ports[]= rtspUtil.getServerPorts();
for( int i = 0; i < numberOfTracks; i++) {
SessionAddress remoteAddress =
new SessionAddress(destaddr, server_ports[ i]);
RTPManager mgr= rtspUtil.getRTPManager( i);
mgr.addTarget( remoteAddress);
// Set 3/4 sec worth of buffering.
BufferControl bc = (BufferControl)mgr.getControl("javax.media.control.BufferControl");
String mediaType= rtspUtil.getMediaType( i);
if( mediaType.equals( "audio")) {
bc.setBufferLength(250);
bc.setMinimumThreshold(125);
} else if( mediaType.equals( "video")) {
bc.setBufferLength(1500);
bc.setMinimumThreshold(250);
}
}
} catch (Exception e) {
Log.error(e.getMessage());
return realized;
}
}
}
if (realized) {
state = REALIZED;
int size= rtspUtil.getNumberOfTracks();
players= new Player[ size];
data_sources = new DataSource[ size];
track_ready = new boolean[ size];
dataReceived= false;
// Start the server.
if (!rtspUtil.rtspStart()) {
if( first_pass &&
rtspUtil.getStatusCode() ==
com.sun.media.rtsp.protocol.StatusCode.SESSION_NOT_FOUND) {
first_pass= false;
playerList= new Vector();
return initRtspSession();
}
return false;
}
// Wait for the initial filling of the data buffers and
// the RTP players to fully realized.
waitForData();
if( playerList.size() > 0) {
// Now the players are all started.
// Stop the server momentarily. Rewind the media
// so the next start will start from the beginning again.
// This is sort of inefficient but will get by the initial
// buffering problem.
rtspStop();
rtspUtil.setStartPos( 0);
// Now we need to flush the datasources to make sure
// the rewind starts from the very beginning.
for( int i = 0; i < rtspUtil.getNumberOfTracks(); i++) {
data_sources[ i].flush();
}
} else {
rtspUtil.setProcessError( "Media tracks not supported");
realized= false;
}
}
return realized;
}
// This method should not be called with RTSP:
public boolean doPrefetch() {
boolean prefetched;
prefetched = super.doPrefetch();
return prefetched;
}
public void doStart() {
if (state >= REALIZED && state != PLAYING) {
for( int i = 0; i < rtspUtil.getNumberOfTracks(); i++) {
track_ready[ i] = (rtspUtil.getRTPManager( i) == null ? true : false);
data_sources[ i].prebuffer();
}
boolean success = rtspUtil.rtspStart();
// We are waiting for audio and video to be prebuffered
// before actually starting the playback. We'll wait for
// at most 3 seconds.
synchronized (readySync) {
boolean ready = true;
for( int i = 0; i < rtspUtil.getNumberOfTracks(); i++) {
if( !track_ready[ i]) {
ready = false;
break;
}
}
if (!ready) {
try {
readySync.wait(3000);
} catch (Exception e) { }
}
}
if (success) {
super.doStart();
startPlayers();
state = PLAYING;
// this timer controls the end of media event which is
// required for looping.
long duration= rtspUtil.getDuration();
if( duration > 0) {
timer = new Timer(this,
duration + 500000000 - getMediaTime().getNanoseconds());
timer.start();
}
}
}
}
public void doSetMediaTime(Time now) {
super.doSetMediaTime(now);
rtspUtil.setStartPos( now.getNanoseconds());
// Now we need to flush the datasources.
for( int i = 0; i < rtspUtil.getNumberOfTracks(); i++) {
data_sources[ i].flush();
}
}
private Timer timer;
public Time getMediaTime() {
Time time = super.getMediaTime();
return time;
}
public void timerExpired() {
timer = null;
processEndOfMedia();
}
public void doStop() {
if( state == PLAYING) {
super.doStop();
if (timer != null) {
timer.stopTimer();
timer.removeListener(this);
timer = null;
}
stopPlayers();
rtspStop();
state = PAUSING;
}
}
public void rtspStop() {
rtspUtil.setStartPos( getMediaTime().getNanoseconds());
rtspUtil.rtspStop();
}
public void doClose() {
stopPlayers();
closePlayers();
if (timer != null) {
timer.stopTimer();
timer.removeListener(this);
timer = null;
}
if( state == PLAYING) {
rtspUtil.rtspTeardown();
}
state = INITIALIZED;
rtspUtil.closeConnection();
for( int i = 0; i < rtspUtil.getNumberOfTracks(); i++) {
RTPManager mgr= rtspUtil.getRTPManager( i);
mgr.removeTargets( "server down.");
mgr.dispose();
}
super.doClose();
}
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();
}
public boolean audioEnabled() {
boolean enabled = true;
return enabled;
}
public boolean videoEnabled() {
boolean enabled = true;
return enabled;
}
public void updateStats() {
}
protected TimeBase getMasterTimeBase() {
return new SystemTimeBase();
}
public synchronized void update(ReceiveStreamEvent event) {
// find the sourceRTPSM for this event
RTPManager source = (RTPManager) event.getSource();
// create a new player if a new recvstream is detected
if (event instanceof NewReceiveStreamEvent) {
// get a handle over the ReceiveStream
ReceiveStream stream = ((NewReceiveStreamEvent) event).getReceiveStream();
Participant part = stream.getParticipant();
int numberOfTracks= rtspUtil.getNumberOfTracks();
for( int i = 0; i < numberOfTracks; i++) {
if( source == rtspUtil.getRTPManager( i)) {
DataSource ds = (com.sun.media.protocol.rtp.DataSource)stream.getDataSource();
try {
players[ i] = javax.media.Manager.createPlayer(ds);
} catch (Exception e) {
System.err.println("Failed to create a player " +
"from the given Data Source: " + e);
}
try {
waitFailed= false;
players[ i].addControllerListener(new StateListener());
players[ i].realize();
waitForState(players[ i], Player.Realized);
} catch( Exception e) {
// System.out.println( failed to realize.");
}
if( players[ i].getState() == Player.Realized) {
playerList.addElement(players[ i]);
ds.setBufferListener(this);
data_sources[ i]= ds;
} else {
players[ i].close();
players[ i]= null;
rtspUtil.removeTrack( i);
}
break;
}
}
// Notify when all the players are realized.
if (playerList.size() == rtspUtil.getNumberOfTracks()) {
dataReceived = true;
synchronized (this) {
notifyAll();
}
}
} else if (event instanceof ByeEvent) {
}
}
public void minThresholdReached(javax.media.protocol.DataSource ds) {
synchronized (readySync) {
for( int i = 0; i < rtspUtil.getNumberOfTracks(); i++) {
if (ds == data_sources[ i]) {
track_ready[ i] = true;
break;
}
}
boolean all_ready = true;
for( int i = 0; i < rtspUtil.getNumberOfTracks(); i++) {
if (!track_ready[ i]) {
all_ready = false;
break;
}
}
if( all_ready) {
readySync.notifyAll();
}
}
}
public long getMediaNanoseconds() {
long value= super.getMediaNanoseconds();
return value;
}
public Time getDuration() {
long t = rtspUtil.getDuration();
if (t <= 0)
return Duration.DURATION_UNKNOWN;
return new Time(t);
}
private synchronized void waitForState(Player p, int state) {
while (p.getState() < state && !waitFailed) {
synchronized (stateSync) {
try {
stateSync.wait();
} catch (InterruptedException ie) {
}
}
}
}
class StateListener implements ControllerListener {
public void controllerUpdate(ControllerEvent ce) {
if (ce instanceof ControllerClosedEvent) {
}
if (ce instanceof ResourceUnavailableEvent) {
waitFailed = true;
synchronized (stateSync) {
stateSync.notify();
}
}
if (ce instanceof RealizeCompleteEvent) {
synchronized (stateSync) {
stateSync.notify();
}
}
if (ce instanceof ControllerEvent) {
synchronized (stateSync) {
stateSync.notify();
}
}
if (ce instanceof EndOfMediaEvent) {
}
}
}
private synchronized boolean waitForData() {
try {
synchronized (this) {
while (!dataReceived) {
wait();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return dataReceived;
}
private Container container = null;
public Component getVisualComponent() {
Vector visuals = new Vector(1);
for( int i = 0; i < rtspUtil.getNumberOfTracks(); i++) {
if( players[ i] != null) {
Component comp= players[ i].getVisualComponent();
if( comp != null) {
visuals.addElement( comp);
}
}
}
if( visuals.size() == 0) {
return null;
} else if( visuals.size() == 1) {
return (Component) visuals.elementAt( 0);
} else {
return createVisualContainer( visuals);
}
}
protected Component createVisualContainer(Vector visuals) {
Boolean hint = (Boolean) Manager.getHint(Manager.LIGHTWEIGHT_RENDERER);
if (container == null) {
if (hint == null || hint.booleanValue() == false) {
container = new HeavyPanel(visuals);
} else {
container = new LightPanel(visuals);
}
container.setLayout( new FlowLayout() );
container.setBackground(Color.black);
for (int i = 0; i < visuals.size(); i++) {
Component c = (Component)visuals.elementAt(i);
container.add(c);
c.setSize(c.getPreferredSize());
}
}
return container;
}
class HeavyPanel extends java.awt.Panel implements VisualContainer {
public HeavyPanel(Vector visuals) {
}
}
class LightPanel extends java.awt.Container implements VisualContainer {
public LightPanel(Vector visuals) {
}
}
public GainControl getGainControl() {
GainControl gainControl = null;
for (int i = 0; i < playerList.size(); i++) {
Player player = (Player) playerList.elementAt(i);
gainControl = player.getGainControl();
if (gainControl != null) {
break;
}
}
return gainControl;
}
public Control[] getControls() {
int size = 0;
for (int i = 0; i < playerList.size(); i++) {
Control[] controls = ((Player)(playerList.elementAt(i))).getControls();
size += controls.length;
}
size++;
Control rtspControls[] = new Control[size];
RtspAdapter rtspAdapter= new RtspAdapter();
rtspAdapter.setRTPManagers( rtspUtil.getRTPManagers());
rtspAdapter.setMediaTypes( rtspUtil.getMediaTypes());
int counter = 0;
rtspControls[ counter++] = rtspAdapter;
for (int i = 0; i < playerList.size(); i++) {
Control[] controls = ((Player)(playerList.elementAt(i))).getControls();
for (int k = 0; k < controls.length; k++) {
rtspControls[counter++] = controls[k];
}
}
return rtspControls;
}
private void startPlayers() {
for (int i = 0; i < playerList.size(); i++) {
Player player = (Player) playerList.elementAt(i);
player.start();
}
}
private void stopPlayers() {
for (int i = 0; i < playerList.size(); i++) {
Player player = (Player) playerList.elementAt(i);
player.stop();
}
}
private void closePlayers() {
for (int i = 0; i < playerList.size(); i++) {
Player player = (Player) playerList.elementAt(i);
player.close();
}
}
public void setSource(javax.media.protocol.DataSource source)
throws IOException, IncompatibleSourceException {
if (source instanceof com.sun.media.protocol.rtsp.DataSource) {
MediaLocator ml = source.getLocator();
try {
url = ml.toString();
} catch (Exception e) {
throw new IncompatibleSourceException();
}
} else {
throw new IncompatibleSourceException();
}
}
}
|