FileDocCategorySizeDatePackage
AMController.javaAPI DocJMF 2.1.1e26097Mon May 12 12:21:20 BST 2003com.sun.media.amovie

AMController

public class AMController extends BasicController
Controller for Active Movie based MPEG player.

Fields Summary
final int
MINVOLUME
Constants
ActiveMovie
amovie
Variables
protected static final int
TRYSET_DONE
protected static final int
TRYSET_CANT
protected static final int
TRYSET_PASTEOM
private Integer
closeLock
private Integer
sourceLock
private boolean
closed
private TimeBase
amTimeBase
private int
appletWindowHandle
int
pwidth
int
pheight
int
outWidth
int
outHeight
private int
width
private int
height
boolean
peerExists
private boolean
muted
private boolean
outputSizeSet
private EventThread
eventThread
private boolean
isFileStream
private boolean
isRandomAccess
private boolean
isSeekable
private boolean
hasAudio
private boolean
hasVideo
private boolean
deallocated
private boolean
sourceIsOn
private DataSource
source
private PullSourceStream
stream
private SourceStream
originalStream
private String
mpegFile
private com.sun.media.content.video.mpeg.Handler
player
private Component
visualComponent
private boolean
seekFailed
private Time
timeWhenMediaStopped
private boolean
mediaTimeChanged
private Time
requestedMediaTime
private Time
lastMediaTime
private int
nextRead
private boolean
inEOM
private VideoFormat
videoFormat
private AudioFormat
audioFormat
private String
id
private boolean
blockedOnRead
private Thread
blockThread
private boolean
abortingRealize
private boolean
setRatePending
private float
setRateValue
private GainControl
gc
private Control[]
controls
private FormatControl
audioControl
private FormatControl
videoControl
private static boolean
libraryLoaded
private static JMFSecurity
jmfSecurity
private static boolean
securityPrivelege
private Method[]
m
private Class[]
cl
private Object[]
args
protected Time
eomDuration
Constructors Summary
public AMController(com.sun.media.content.video.mpeg.Handler handler)
Methods


     
	try {
	    jmfSecurity = JMFSecurityManager.getJMFSecurity();
	    securityPrivelege = true;
	} catch (SecurityException e) {
	}
    
	String library = "jmam";
	if (!libraryLoaded) {
	    try {
		JMFSecurityManager.loadLibrary(library);
		libraryLoaded = true;
	    } catch (Throwable t) {
		throw new RuntimeException("Unable to load native MPEG library");
	    }

	}
	player = handler;
    
Methods Summary
protected voidabortPrefetch()

	// System.err.println("AMController.abortPrefetch()");
	startSource(false, true);
    
protected voidabortRealize()

	// TODO: Abort downloading if caching is enabled
	// System.err.println("AMController.abortRealize()");
	abortingRealize = true;
	startSource(false, true);
    
public booleanaudioEnabled()

	if (amovie != null) {
	    return amovie.hasAudio();
	} else
	    return true;
    
public intcanRead(int bytes)
Stream related calls made from native code

	return bytes;
    
public longcanSeek(long seekTo)

	// TODO: Pause the controller if seek will block
	return seekTo;
    
private ActiveMoviecreateActiveMovie(javax.media.protocol.DataSource s)

	URL url = null;
	if (s == null)
	    return null;
	MediaLocator ml = s.getLocator();
	
	if (ml != null) {
	    try {
		url = ml.getURL();
	    } catch (MalformedURLException e) {
		ml = null;			      // It's possible that its not a URL
						      // Bug reported after beta3.
	    }
	}
	
	if (ml != null && ml.getProtocol().equals("file")) {
	    int indexPipe;

	    mpegFile =
		com.sun.media.protocol.file.DataSource.getFileName(ml);
            isFileStream = true;
	    isRandomAccess = true;
	    isSeekable = true;
            ActiveMovie am = new ActiveMovie(this, mpegFile);
	    hasVideo = am.hasVideo();
	    hasAudio = am.hasAudio();
	    updateControls(am);
	    return am;
	} else {
	    // Can only handle file: for MPEG_AUDIO
	    if (s.getContentType().equalsIgnoreCase("audio.mpeg") ||
		s.getContentType().equalsIgnoreCase("audio/mpeg"))
		
		return null;
	    
	    // Its a data source other than file. Lets open a stream
	    if (s instanceof PullDataSource) {
		
		PullSourceStream [] streams = 
		    (PullSourceStream []) ((PullDataSource)s).getStreams();
		if (streams != null && streams.length > 0) {
		    stream = streams[0];
		    originalStream = stream;
		    if (stream instanceof Seekable) {
			isSeekable = true;
			// ((Seekable)stream).seek(0);
			// Is it a random access stream?
			if (((Seekable)stream).isRandomAccess())
			    isRandomAccess = true;
			if (ml != null && ml.getProtocol().startsWith("http") &&
			    url != null) {
			    isRandomAccess = false;
			}
		    }
		    ActiveMovie am = new ActiveMovie(this, (PullSourceStream)stream,
						     isRandomAccess,
						     originalStream.getContentLength());
		    hasVideo = am.hasVideo();
		    hasAudio = am.hasAudio();
		    updateControls(am);
		    return am;
		}
	    }	    
	    return null;
	}
    
java.awt.ComponentcreateVisualComponent()

	Component c = null;
	Class visclass = null;
	
	//if (BuildInfo.getJavaVendor().indexOf("icrosoft") > 0) {
	    try {
		visclass = Class.forName("com.sun.media.amovie.MSVisualComponent");
	    } catch (Throwable t) {
	    }
	    //}

	if (visclass == null) {
	    try {
		visclass = Class.forName("com.sun.media.amovie.VisualComponent");
	    } catch (Throwable th) {
		return null;
	    }
	}

	Class params [] = { AMController.class };
	Constructor cons = null;
	try {
	    cons = visclass.getConstructor(params);
	    Object [] amparam = new AMController[1];
	    amparam[0] = this;
	    c = (Component) cons.newInstance(amparam);
	    return c;
	} catch (Throwable tr) {
	}
	return null;
    
protected voiddoClose()


	if (getState() == Controller.Realizing)
	    abortRealize();
	
	synchronized (closeLock) {
	    // Do nothing if already closed.
	    if (closed)
		return;
	    
	    // Stop all the threads and active movie.
	    doDeallocate();
	    
	    // Kill active movie
	    if (amovie != null) {
		amovie.dispose();
		amTimeBase = null;
		amovie = null;
	    }
	    // Disconnect the data source
	    if (source != null) {
		source.disconnect();
	    }
	    source = null;
	    closed = true; // Dont come back!
	}
    
protected synchronized voiddoDeallocate()

	// Stop the source
	startSource(false, false);
	
	// Restart from Time(0).
	timeWhenMediaStopped = getMediaTime();
	mediaTimeChanged = true;
	requestedMediaTime = new Time(0);
	lastMediaTime = timeWhenMediaStopped;

	// Kill all threads and ActiveMovie
	if (amovie != null) {
	    blockedOnRead = false;
 	    if (amovie.getVolume() == MINVOLUME)  // Is Mute on?
	        amovie.setVolume(MINVOLUME / 2); // TURN MUTE OFF
	    amovie.kill();

	    if (eventThread != null) {
	        eventThread.kill();
	        eventThread = null;
	    }
        }
    
protected booleandoPrefetch()

	if (amovie == null)
	    if (!doRealize())
		return false;

	// If activemovie was recreated, reattach the AM window to java panel
	if (amovie != null && visualComponent != null && peerExists) {
	    setOwner(visualComponent);
	}
	return true;
    
protected booleandoRealize()

	abortingRealize = false;

	if (amovie != null) {
	    if (amTimeBase instanceof AMTimeBase)
		amTimeBase = null;
	    amovie.kill();
	    amovie.dispose();
	    amovie = null;
	}

	startSource(true, true);

	synchronized (closeLock) {
	
	    amovie = createActiveMovie(source);
	    if (amovie == null)
		return false;
	
	    if (!amovie.isRealized()) {
		amovie = null;
		return false;
	    }

	    if (abortingRealize) {
		doDeallocate();
		amovie.dispose();
		amovie = null;
		return false;
	    }
	}

	try {
	    amovie.amStopWhenReady();
	    amovie.doneRealize();
	    if (amTimeBase == null)
		amTimeBase = new AMTimeBase( this );
	    setMediaLength((long)(amovie.getDuration() * 1e9));
	    amovie.setVisible(0);
	    startSource(false, false);
	} catch (Throwable t) {
	    return false;
	}
	return true;
    
public floatdoSetRate(float factor)

	if (amovie == null)
	    return 1.0F;
	if (factor < 0.1)
	    factor = 0.1f;
	if (factor > 10.0)
	    factor = 10.0f;
	if ((float) amovie.getRate()  != factor) {
	    setRatePending = true;
	    setRateValue = factor;
	}

	return factor;
    
protected final voiddoStart()

	GainControl gc;
	if (amovie == null)
	    doPrefetch();

	startSource(true, false);
	amovie.restart();

	if (setRatePending) {
	    amovie.setRate(setRateValue);
	    setRatePending = false;
	    if ((float) amovie.getRate() != setRateValue) {
		sendEvent(new RateChangeEvent(this, (float) amovie.getRate()));
	    }
	}
	
	// Restart the download thread.
	if (mediaTimeChanged) {
	    int returnVal = trySetMediaTime(requestedMediaTime);
	    if (returnVal == TRYSET_CANT) {
		// Couldn't set the media time.
		super.stop();
		sendEvent((StopEvent) new SeekFailedEvent(this, Started,
							  Prefetched,
							  getTargetState(),
							  getMediaTime()));
		return;
	    } else if (returnVal == TRYSET_PASTEOM) {
		// Seeking beyond EOM
		super.stop();
		sendEvent(new EndOfMediaEvent(this, Started, Prefetched,
					      getTargetState(), getMediaTime()));
		return;
	    }
	}
	// We've succeeded in setting the media time.
	mediaTimeChanged = false;
	
	if ((gc = player.getGainControl()) != null)
	    amovie.setVolume((int)(gc.getDB() * 100));
	amovie.amRun();
	if (!peerExists)
	    amovie.setVisible(0);
	if (gc != null)
	    muteChange(gc.getMute());	
	if (eventThread == null) {
	    if ( /*securityPrivelege  && */ (jmfSecurity != null) ) {
		String permission = null;
		try {
		    if (jmfSecurity.getName().startsWith("jmf-security")) {
			permission = "thread";
			jmfSecurity.requestPermission(m, cl, args,
						      JMFSecurity.THREAD);
			m[0].invoke(cl[0], args[0]);
			
			permission = "thread group";
			jmfSecurity.requestPermission(m, cl, args,
						      JMFSecurity.THREAD_GROUP);
			m[0].invoke(cl[0], args[0]);
		    } else if (jmfSecurity.getName().startsWith("internet")) {
			PolicyEngine.checkPermission(PermissionID.THREAD);
			PolicyEngine.assertPermission(PermissionID.THREAD);
		    }
		} catch (Throwable e) {
		    if (JMFSecurityManager.DEBUG) {
			System.err.println( "Unable to get " + permission +
					    " privilege  " + e);
		    }
		    securityPrivelege = false;
		    // TODO: Do the right thing if permissions cannot be obtained.
		    // User should be notified via an event
		}
	    }
	    
	    if ( (jmfSecurity != null) && (jmfSecurity.getName().startsWith("jdk12"))) {
		try {
		    Constructor cons = jdk12CreateThreadAction.cons;
		    
		    eventThread = (EventThread) jdk12.doPrivM.invoke(
                                           jdk12.ac,
					   new Object[] {
 					  cons.newInstance(
 					   new Object[] {
                                               EventThread.class,
                                           })});
		} catch (Exception e) {
		}
	    } else {
		eventThread = new EventThread();
	    }
	}
	eventThread.setController(this);
	eventThread.start();
    
public voiddoStop()

	// System.err.println("In doStop()");
	super.doStop();
	lastMediaTime = getMediaTime();
        if (amovie != null && !inEOM) {
	    amovie.amStop();
	    //amovie.amPause();
	    amovie.pause();
        }
	if (eventThread != null)
	    eventThread.pause();
	startSource(false, false);
	if (!isFileStream) 
	    nextRead = -2;
	sendEvent((StopEvent)new StopByRequestEvent(this, Started,
						    Prefetched,
						    getTargetState(),
						    getMediaTime()));
    
public voidfinalize()

	if (amovie != null)
	    doClose();
    
public voidgainChange(float g)

       	if (amovie != null && !muted && gc != null) {
	    float dB = gc.getDB();
	    if (dB > 0.0f) dB = 0.0f;
	    if (dB < -70f) dB = -100f; // silence is -10000 for active movie
	    amovie.setVolume( (int)(dB * 100));
	}
    
public javax.media.Control[]getControls()

	int n = 0;
	if (hasAudio) n++;
	if (audioFormat != null) n++;
	if (videoFormat != null) n++;
	controls = new Control[n];
	n = 0;
	if (hasAudio) {
	    if (gc == null) {
		gc = new GCA();
	    }
	    controls[n++] = gc;
	}
	if (audioFormat != null) {
	    if (audioControl == null) {
		audioControl = new FormatAdapter(audioFormat,
						 new Format[] {audioFormat},
						 true,
						 false,
						 false);
	    }
	    controls[n++] = audioControl;
	}
	if (videoFormat != null) {
	    if (videoControl == null) {
		videoControl = new FormatAdapter(videoFormat,
						 new Format[] {videoFormat},
						 true,
						 false,
						 false);
	    }
	    controls[n++] = videoControl;
	}
	return controls;
    
public javax.media.TimegetDuration()

    
       
	// If we've hit the end of media once, use that duration.
	if (eomDuration != DURATION_UNKNOWN)
	    return eomDuration;

	if (source instanceof Duration) {
	    Time d = ((Duration)source).getDuration();
	    if (d != null && d != Duration.DURATION_UNKNOWN &&
		d != Duration.DURATION_UNBOUNDED)
		return d;
	}
	
	if (amovie == null)
	    return Duration.DURATION_UNKNOWN;
	else {
	    double amduration = amovie.getDuration(); // Get the duration in secs
	    if (isRandomAccess)
		return new Time((long) (amduration * 1E+9)); // To nanoseconds
	    else
		return DURATION_UNKNOWN;
	}
    
public javax.media.TimeBasegetTimeBase()

	if (amTimeBase == null)
	    amTimeBase = new AMTimeBase( this );
	return amTimeBase;
    
public java.awt.ComponentgetVisualComponent()

	if (amovie == null)
	    return null;
	if (visualComponent == null) {
	    if (amovie.getVideoWidth() == 0 ||
		amovie.getVideoHeight() == 0)
		visualComponent = null;
	    else {
		visualComponent = createVisualComponent();

		// Component resize listener
		visualComponent.addComponentListener(new ComponentAdapter() {
		    private int lastWidth = -1;
		    private int lastHeight = -1;
		    
		    public void componentResized(ComponentEvent ce) {
			if (amovie != null) {
			    Dimension csize = ce.getComponent().getSize();
			    if (csize.width == lastWidth &&
				csize.height == lastHeight)
				return;
			    lastWidth = csize.width;
			    lastHeight = csize.height;
			    outputSizeSet = true;
			    zoom(lastWidth, lastHeight);
			}
		    }
		} );
		// End resize listener
	    }
	}
	return visualComponent;
    
public booleanisConfigurable()

	return false;
    
public voidmuteChange(boolean state)

	if (amovie != null) {
	    if (state) {
		muted = true;
		amovie.setVolume(MINVOLUME);
	    } else {
		muted = false;
		try {
		    float dB = gc.getDB();
		    if (dB > 0) dB = 0;
		    if (dB < -70f) dB = -100f;
		    amovie.setVolume( (int)(dB * 100) );
		} catch (Exception e) {
		}
	    }
	}
    
public intread(byte[] data, int offset, int length)

	if (abortingRealize)
	    return -1;
	/*
	if (canRead(length) == -1)
	    return -1;
	*/
	if (nextRead == -2) {
	    nextRead = 0;
	    return -2;
	}
	synchronized (sourceLock) {
	    if (stream != null && sourceIsOn) {
		try {
		    int readBytes = stream.read(data, offset, length);
		    return readBytes;
		} catch (IOException ioe) {
		    sendEvent(new ConnectionErrorEvent(this, ioe.getMessage()));
		    return -1;
		}
	    } else
		return -1;
	}
    
public longseek(long seekTo)

	if (abortingRealize) {
	    //System.err.println("seek.0");
	    return 0;
	}

	synchronized (sourceLock) {
	    if (stream instanceof Seekable && sourceIsOn) {
		//System.err.println("seek.1");
		
		// Don't seek if already there.
		if (((Seekable)stream).tell() == seekTo)
		    return seekTo;
		
		if (((Seekable)stream).isRandomAccess()) {
		    long seeked = ((Seekable)stream).seek(seekTo);
		    //System.err.println("Seeked = " + seekTo);
		    return seeked;
		} else if (seekTo == 0)
		    return ((Seekable)stream).seek(seekTo);
	    }
	}
	//System.err.println("seek.2");
	return 0;
    
voidsendEOM()

	if (amovie != null) {
	    amovie.amPause();
	    amovie.pause();
	}
	inEOM = true; // ay: To prevent hanging in doStop after EOM
	super.stop();
	inEOM = false;
	Time earlier = eomDuration;
	eomDuration = new Time(getMediaTime().getNanoseconds());
	startSource(false, false);
	sendEvent(new EndOfMediaEvent(this, Started, Prefetched,
				      getTargetState(), getMediaTime()));
	if (earlier == DURATION_UNKNOWN)
	    sendEvent(new DurationUpdateEvent(this, eomDuration));
	
	if (stream instanceof CachedStream && isRandomAccess == false) {
	    isRandomAccess = true;
	    if (amovie != null)
		amovie.setSeekable(true);
	}
    
public voidsetMediaTime(javax.media.Time time)

	super.setMediaTime(time);
	synchronized (this) {
	    requestedMediaTime = time;
	    mediaTimeChanged = true;
	}

	if (stream == null) {
	    amovie.restart();
	    if (trySetMediaTime(time) == TRYSET_DONE) 
		mediaTimeChanged = false;
	}
    
voidsetOwner(java.awt.Component parent)
Sets the parent for the activemovie window.

	try {
	    if (amovie != null && parent != null) {
		appletWindowHandle = com.sun.media.util.WindowUtil.getWindowHandle(parent);
		if (appletWindowHandle == 0) {
		    throw new NullPointerException("null peer");
		}
		amovie.setOwner(appletWindowHandle);
		amovie.setVisible(1);
		parent.getPreferredSize();
		amovie.setWindowPosition(0, 0, outWidth, outHeight);
	    }
	} catch (Throwable t) {
	}
    
public voidsetSource(javax.media.protocol.DataSource s)


	// Check for PullDataSource
	if (s instanceof PullDataSource)
	    source = s;
	else
	    throw new IncompatibleSourceException("MPEG Controller requires a PullDataSource");

	String excMessage = null;
	// Check if audio
	if (s.getContentType().equalsIgnoreCase("audio.mpeg") ||
	    s.getContentType().equalsIgnoreCase("audio/mpeg")) {
	    
	    // If there's an MP3 plugin, don't handle audio here
	    AudioFormat mp3 = new AudioFormat(AudioFormat.MPEGLAYER3);
	    AudioFormat linear = new AudioFormat(AudioFormat.LINEAR);
	    Vector v = PlugInManager.getPlugInList(mp3, linear, PlugInManager.CODEC);
	    if (v != null && v.size() > 0 &&
		!( v.size() == 1 && ((String)v.elementAt(0)).equals("com.ibm.media.codec.audio.ACMCodec"))) {
		// Some plug-in already handles MP3 and hence MP2 also
		// So this player shouldn't handle it
		excMessage = "Handler doesn't play MPEG Audio";
	    } else {
		// Handle only if local file && mp3
		MediaLocator ml = s.getLocator();
		if (ml == null)
		    excMessage = "No media locator, cannot handle stream";
		else {
		    String url = ml.toString();
		    if (url != null && url.endsWith(".mp2")) {
			// Don't handle MP2, since its handled by Unknown Handler
			excMessage = "Handler doesn't play MPEG Layer 2 audio";
		    } else {
			if (!ml.getProtocol().equals("file"))
			    excMessage = "Handler only plays local MPEG Audio files";
		    }
		}
	    }
	}
	if (excMessage != null)
	    throw new IncompatibleSourceException("MPEG Handler: " + excMessage);
    
public voidsetTimeBase(javax.media.TimeBase tb)

	super.setTimeBase(tb);

	// We can't support any other time base
	if (tb != null && tb != amTimeBase) {
	    Log.warning("the mpeg handler cannot handle the given timebase.");
	    /**
	     Allow this to silently go through so addController will be
	     slightly more useful.
	     --ivg
	    throw new IncompatibleTimeBaseException("the mpeg handler cannot handle the given timebase.");
	     */
	}
	amTimeBase = tb;
    
public booleanstartSource(boolean on, boolean regardless)

	if (sourceIsOn == on)
	    return true;
	synchronized (sourceLock) {
	    if (regardless) {
		try {
		    if (on) {
			source.start();
			if (amovie != null)
			    amovie.stopDataFlow(false);
		    } else {
			if (amovie != null)
			    amovie.stopDataFlow(true);
			source.stop();
		    }
		} catch (Exception ge) {
		    // System.err.println("Couldn't stop the data source");
		    return false;
		}
		sourceIsOn = on;
	    }
	    return true;
	}
    
protected inttrySetMediaTime(javax.media.Time time)

	if (amovie != null) {
	    long duration = getDuration().getNanoseconds();
	    long now = time.getNanoseconds();

	    // Dont know the duration and not seeking to zero?
	    if (getDuration() == DURATION_UNKNOWN && now != 0)
		return TRYSET_CANT;

	    // Seeking beyond duration?
	    if (now < 0)
		return TRYSET_CANT;
	    if (now > duration)
		return TRYSET_PASTEOM;

	    if (!isSeekable)
		return TRYSET_CANT;
	    
	    double nowSeconds = (double) now * 1e-9;
	    
	    if (isRandomAccess) {
		amovie.setCurrentPosition(nowSeconds);
		return TRYSET_DONE;
	    } else if (stream != null) {
		if (now != 0) {
		    return TRYSET_CANT;
		}
		// Seeking to zero should be ok.
		((Seekable)stream).seek(0);
		amovie.setCurrentPosition( 0 );
		return TRYSET_DONE;
	    }
	}
	return TRYSET_CANT;
    
private voidupdateControls(ActiveMovie amovie)

	if (hasAudio) {
	    audioFormat = new AudioFormat(AudioFormat.MPEG);
	}
	if (hasVideo) {
	    Dimension size = new Dimension(amovie.getVideoWidth(),
					   amovie.getVideoHeight());
	    videoFormat = new VideoFormat(VideoFormat.MPEG,
					  size,
					  Format.NOT_SPECIFIED,
					  Format.byteArray,
					  Format.NOT_SPECIFIED);
	}
    
public booleanvideoEnabled()

	if (amovie != null)
	    return amovie.hasVideo();
	else
	    return true;
    
private voidzoom(int width, int height)

	outWidth = width;
	if (outWidth < 120)
	    outWidth = 120;
	outHeight = height;
	if (outHeight < 1)
	    outHeight = 1;
	zoomChanged();
    
private voidzoomChanged()

       if (amovie == null)
	 return;
       int width = amovie.getVideoWidth();
       int height = amovie.getVideoHeight();
       if (peerExists)
	   amovie.setWindowPosition(0, 0, outWidth, outHeight);
       if (pwidth != width || pheight != height) {
	   pwidth = width;
	   pheight = height;
	   //sendSizeChangeEvent(pwidth, pheight, 1.0F);
       }