/* * @(#)Handler.java 1.22 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.processor.rtsp; import java.io.*; import java.awt.*; import java.util.Vector; import javax.media.*; import com.sun.media.*; import com.sun.media.ui.*; import com.sun.media.controls.*; import com.sun.media.rtp.*; import javax.media.rtp.*; import javax.media.rtp.event.*; import javax.media.rtp.rtcp.*; import java.net.*; import javax.media.protocol.*; import java.awt.event.*; import com.sun.media.util.*; import com.sun.media.rtsp.*; import javax.media.format.AudioFormat; import javax.media.format.VideoFormat; import javax.media.Format; import javax.media.format.FormatChangeEvent; import javax.media.control.TrackControl; import javax.media.control.BufferControl; import java.lang.reflect.Method; import java.lang.reflect.Constructor; import com.ms.security.PermissionID; import com.ms.security.PolicyEngine; public class Handler extends BasicProcessor implements ReceiveStreamListener { // states: private final int INITIALIZED = 0; private final int REALIZED = 1; private final int PLAYING = 2; private final int PAUSING = 3; private DataSource data_sources[]; Processor processor = null; Format formats[] = null; Vector locators = null; Object dataLock = new Object(); boolean dataReady = false; private boolean closed = false; private boolean audioEnabled = false; private boolean videoEnabled = 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]; private boolean first_pass = true; com.sun.media.content.rtsp.RtspUtil rtspUtil; public Handler() { framePositioning = false; rtspUtil = new com.sun.media.content.rtsp.RtspUtil( this); locators = new Vector(); } String sessionError = "cannot create and initialize the RTP Session."; protected synchronized boolean doConfigure() { boolean configured = super.doConfigure(); if (configured) { configured = initRtspSession(); } return configured; } private boolean initRtspSession() { boolean realized = false; MediaLocator ml= (MediaLocator) locators.elementAt( 0); rtspUtil.setUrl( ml.toString()); String ipAddress = rtspUtil.getServerIpAddress(); if (ipAddress == null) { System.out.println( "Invalid server address."); realized = false; } else { rtspUtil.setUrl( ml.toString()); realized= rtspUtil.createConnection(); if( realized) { realized = rtspUtil.rtspSetup(); try { InetAddress destaddr = InetAddress.getByName(ipAddress); int server_ports[]= rtspUtil.getServerPorts(); for( int i = 0; i < rtspUtil.getNumberOfTracks(); i++) { SessionAddress remoteAddress = new SessionAddress(destaddr, server_ports[ i]); rtspUtil.getRTPManager( i).addTarget( remoteAddress); // Set 3/4 sec worth of buffering. BufferControl bc = (BufferControl)rtspUtil.getRTPManager( i).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(); data_sources = new DataSource[ size]; formats= new Format[ size]; // Start the server. if (!rtspUtil.rtspStart()) { if( first_pass && rtspUtil.getStatusCode() == com.sun.media.rtsp.protocol.StatusCode.SESSION_NOT_FOUND) { first_pass= false; return initRtspSession(); } return false; } // Wait for the initial filling of the data buffers and // the RTP players to fully realized. waitForData(); // 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(); // startPos = 0; // Now we need to flush the datasources to make sure // the rewind starts from the very beginning. // for( int i = 0; i < numberOfTracks; i++) { // data_sources[ i].flush(); // } } return realized; } private synchronized boolean waitForData() { try { synchronized (dataLock) { while (!dataReady) { dataLock.wait(); } } } catch (InterruptedException e) { e.printStackTrace(); } return dataReady; } // is this code 'getProperty()' used at all? - Marc /* private String getProperty(String prop) { String value = null; if ( (jmfSecurity != null) ) { try { if (jmfSecurity.getName().startsWith("jmf-security")) { jmfSecurity.requestPermission(m, cl, args, JMFSecurity.READ_PROPERTY); m[0].invoke(cl[0], args[0]); } else if (jmfSecurity.getName().startsWith("internet")) { PolicyEngine.checkPermission(PermissionID.PROPERTY); PolicyEngine.assertPermission(PermissionID.PROPERTY); } } catch (Throwable e) { if (JMFSecurityManager.DEBUG) { System.err.println("Unable to get read property " + " privilege " + e); } jmfSecurity.permissionFailureNotification(JMFSecurity.READ_PROPERTY); // securityPrivelege = false; } } try { if ( (jmfSecurity != null) && (jmfSecurity.getName().startsWith("jdk12"))) { Constructor cons = jdk12PropertyAction.cons; value = (String) jdk12.doPrivM.invoke( jdk12.ac, new Object[] { cons.newInstance( new Object[] { prop })}); } else { value = System.getProperty(prop); } } catch (Throwable e) { } return value; } */ protected void completeConfigure() { state = javax.media.Processor.Configured; super.completeConfigure(); } protected void doFailedConfigure() { closeSessions(); super.doFailedConfigure(); } private void closeSessions() { RTPManager mgrs[]= rtspUtil.getRTPManagers(); 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; } } protected boolean doRealize() { return waitForRealize(processor); } protected void completeRealize() { state = Realized; super.completeRealize(); } protected void doFailedRealize() { closeSessions(); super.doFailedRealize(); } protected void doStart() { super.doStart(); waitForStart(processor); } protected void doStop() { super.doStop(); waitForStop(processor); } protected void doDeallocate() { processor.deallocate(); synchronized (dataLock) { dataLock.notifyAll(); } } protected void doClose() { closed = true; synchronized (dataLock) { dataLock.notify(); } stop(); processor.close(); closeSessions(); super.doClose(); } public void setTimeBase(TimeBase tb) throws IncompatibleTimeBaseException { } protected TimeBase getMasterTimeBase() { return new SystemTimeBase(); } protected boolean audioEnabled(){ return audioEnabled; } protected boolean videoEnabled(){ return videoEnabled; } private void sendMyEvent(ControllerEvent e) { super.sendEvent(e); } public void update( ReceiveStreamEvent event) { RTPManager mgr = (RTPManager)event.getSource(); int idx; // return if the data sources have not been allocated yet. // this may happen if data is coming in from a different // source before the PLAY message has been issued. if( data_sources == null) { return; } RTPManager mgrs[]= rtspUtil.getRTPManagers(); 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"); Log.error("The RTP processor cannot handle mid-stream payload change.\n"); sendEvent(new ControllerErrorEvent(this, "Cannot handle mid-stream payload change.")); close(); } if (event instanceof NewReceiveStreamEvent) { if (data_sources[idx] != null) { // We've already gotten a source from this session. return; } ReceiveStream stream = null; try { // get a handle over the ReceiveStream stream =((NewReceiveStreamEvent)event).getReceiveStream(); data_sources[idx] = stream.getDataSource(); RTPControl ctl = (RTPControl)data_sources[idx].getControl("javax.media.rtp.RTPControl"); if (ctl != null) { formats[idx] = ctl.getFormat(); if (formats[idx] instanceof AudioFormat) { audioEnabled = true; } if (formats[idx] instanceof VideoFormat) { videoEnabled = true; } } /* this can't be done if (source instanceof RTPSocket) { ((RTPSocket)source).setChild(data_sources[idx]); } else { ((com.sun.media.protocol.rtp.DataSource)source). setChild((com.sun.media.protocol.rtp.DataSource)data_sources[idx]); } */ for (int i = 0; i < data_sources.length; i++) { // Return if not all sessions had yielded a source. if (data_sources[i] == null) { return; } } // We've received all the sources, let create the processor. DataSource mixDS; try { mixDS = javax.media.Manager.createMergingDataSource(data_sources); } catch (Exception e) { System.err.println("Cannot merge data sources."); return; } try { processor = javax.media.Manager.createProcessor(mixDS); } catch (Exception e) { System.err.println("Cannot create the mix processor."); return; } if (!waitForConfigure(processor)) { return; } // We are done generating the internal processor. synchronized(dataLock) { dataReady = true; dataLock.notifyAll(); } } catch (Exception e){ System.err.println("NewReceiveStreamEvent exception " + e.getMessage()); return; } } } public void setSource(javax.media.protocol.DataSource source) throws IOException, IncompatibleSourceException { super.setSource(source); if (source instanceof com.sun.media.protocol.rtsp.DataSource){ MediaLocator ml = source.getLocator(); locators.addElement(ml); } else { throw new IncompatibleSourceException(); } } 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(); return processor.getVisualComponent(); } /** * 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() { return processor.getControls(); } public void updateStats() { if ( processor != null) { ((BasicProcessor) processor).updateStats(); } } /** */ public TrackControl[] getTrackControls() throws NotConfiguredError { super.getTrackControls(); return processor.getTrackControls(); } public ContentDescriptor[] getSupportedContentDescriptors() throws NotConfiguredError { super.getSupportedContentDescriptors(); return processor.getSupportedContentDescriptors(); } public ContentDescriptor setContentDescriptor(ContentDescriptor ocd) throws NotConfiguredError { super.setContentDescriptor(ocd); return processor.setContentDescriptor(ocd); } public ContentDescriptor getContentDescriptor() throws NotConfiguredError { super.getContentDescriptor(); return processor.getContentDescriptor(); } public DataSource getDataOutput() throws NotRealizedError { super.getDataOutput(); return processor.getDataOutput(); } private boolean waitForConfigure(Processor p) { return (new StateWaiter()).waitForConfigure(p); } private boolean waitForRealize(Processor p) { return (new StateWaiter()).waitForRealize(p); } 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 boolean waitForConfigure(Processor p) { p.addControllerListener(this); p.configure(); synchronized (stateLock) { while (p.getState() != javax.media.Processor.Configured && !closeDown) { try { stateLock.wait(1000); } catch (InterruptedException ie) { break; } } } p.removeControllerListener(this); return !closeDown; } public boolean waitForRealize(Processor p) { p.addControllerListener(this); p.realize(); synchronized (stateLock) { while (p.getState() != Realized && !closeDown) { try { stateLock.wait(1000); } catch (InterruptedException ie) { break; } } } p.removeControllerListener(this); return !closeDown; } 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(); } } } }