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

ActiveMovie.java

/*
 * @(#)ActiveMovie.java	1.4 02/08/21
 *
 * Copyright (c) 1996-2002 Sun Microsystems, Inc.  All rights reserved.
 */

package com.sun.media.amovie;

import javax.media.*;
import javax.media.protocol.*;

public class ActiveMovie implements Runnable {
    
    private int pGraph = 0;
    private int aStream = 0;
    private int filterPin = 0;
    private byte [] jbuffer;

    private boolean paused = false;
    private boolean donePaused = true;

    private Thread spinner = null;
    private Integer semaphore = new Integer(0);
    
    private boolean realized = false;
    private int     streamType = 3;

    private AMController controller;
    private PullSourceStream stream = null;
    private boolean seekable;
    private boolean randomAccess;
    private long readLocation = 0;
    private long streamLocation = 0;
    private boolean controllerRealized = false;
    private int cacheBuffer = 0;
    private int cacheTotalSize = 0;
    private int cacheAllocated = 0;

    private boolean deallocated = false;
    
    // volume constants
    public static final int MIN_VOLUME = -10000;
    public static final int MAX_VOLUME = 0;
    
    ActiveMovie(AMController controller, String file) {
	this.controller = controller;
        realized = openFile(file);
    }
    
    ActiveMovie(AMController controller, PullSourceStream source,
		boolean randomAccess, long contentLength) {

	this.controller = controller;
	this.stream  = source;
	this.seekable = source instanceof Seekable;
	this.randomAccess = randomAccess;

	// boolean seekable;
	this.jbuffer = new byte[65536];
	
	//seekable = (source instanceof Seekable) &&
	//         ((Seekable)source).isRandomAccess();

	initiateSpin();
	if (seekable)
	    seek(0);
	controller.canRead(64 * 1024);
	int size = controller.read(jbuffer, 0, 64 * 1024);

	if (!randomAccess && size > 0)
	    addToCache(jbuffer, 0, size);
	if (size > 0) {
	    //System.err.println("Content Length = " + contentLength);
	    if (!randomAccess && contentLength > 0)
		contentLength += 600 * 1024;
	    streamType = getStreamType(jbuffer, size);
	    streamLocation += size;
	    seek(0);

	    realized = openStream(seekable, randomAccess, streamType,
				  contentLength);

	} else
	    realized = false;
    }

    public void setSeekable(boolean seekable) {
	if (seekable) {
	    this.randomAccess = seekable;
	    this.seekable = seekable;
	}
	setNSeekable(seekable);
    }
    
    native void setNSeekable(boolean seekable);

    private void initiateSpin() {
	spinner = new Thread( this );
	spinner.start();
    }

    public void run() {
	// We need to block the thread if ActiveMovie is paused.
	while (true) {
	    synchronized (this) {
		while (paused) {
		    if (!donePaused) {
			donePaused = true;
			notifyAll();
		    }
		    try {
			wait();
		    } catch (InterruptedException e) {}
		}
	    }
	    
	    doNRequest(jbuffer);
	    
	    try {
		spinner.sleep(50);
	    } catch (Exception e) {
		System.err.println("Exception in run()" + e);
	    }
	}

    }

    void doneRealize() {
	controllerRealized = true;
    }
    
    boolean isRealized() {
	return realized;
    }

    boolean hasVideo() {
	return (streamType & 2) == 2;
    }

    boolean hasAudio() {
	return (streamType & 1) == 1;
    }

    native void amRun();

    native void amPause();

    native void amStop();

    native void amStopWhenReady();

    void stopDataFlow(boolean stop) {
	if (filterPin != 0)
	    stopDataFlow(filterPin, stop);
    }

    native void stopDataFlow(int filterPin, boolean stop);

    native double getDuration();

    native double getCurrentPosition();

    native void setCurrentPosition(double pos);

    native void setStopTime(double time);

    native int getBitRate();

    native double getFrameRate();

    native int getVideoWidth();

    native int getVideoHeight();

    native void setOwner(int owner);

    native void setVisible(int visible);

    native void setWindowPosition(int left, int top, int right, int bottom);

    native int getVolume();

    native void setVolume(int volume);

    native void setRate(double rate);

    native double getRate();

    native long getTime(); 

    native boolean waitForCompletion();

    native int  getStreamType(byte [] array, int size);
    
    native void doNRequest(byte [] array);

    // Called from native code
    public int canRead(int nBytes) {
	return controller.canRead(nBytes);
    }

    // Called from native code
    public long canSeek(long seekTo) {
	return controller.canSeek(seekTo);
    }

    public int read(byte [] array, int offset, int length) {
	int totalRead = 0;
	if (deallocated)
	    return -1;
	if (cacheTotalSize > 0 && !randomAccess) {
	    if (readLocation < cacheTotalSize && streamLocation == cacheTotalSize) {
		totalRead = (int) (cacheTotalSize - readLocation);
		if (totalRead > length)
		    totalRead = length;
		getFromCache((int) readLocation,
			     array, offset, totalRead);
		readLocation += totalRead;
		if (totalRead == length) {
		    return totalRead;
		} else {
		    length -= totalRead;
		    offset += totalRead;
		}
	    }
	}
	
	int actualRead = 0;
	int remaining = length;
	
	while (totalRead < length) {
	    if (canRead(remaining) > 0)
		actualRead = controller.read(array, offset, remaining);
	    else
		actualRead = -1;
	    if (actualRead == -1) {
		// EOS
		// cacheTotalSize = 0;
		if (totalRead > 0)
		    return totalRead;
		else
		    return -1;
	    } else if (actualRead == -2) {
		return -2;
	    } else if (actualRead > 0) {
		remaining -= actualRead;
		totalRead += actualRead;

		// Cache the data if the controller is not realized yet.
		if (!controllerRealized && !randomAccess) {
		    if (streamLocation == cacheTotalSize) {
			addToCache(array, offset, actualRead);
		    }
		}

		offset += actualRead;
		streamLocation += actualRead;
		readLocation = streamLocation;
		if (streamLocation > cacheTotalSize &&
		    controllerRealized)

		    cacheTotalSize = 0;
	    }
	}
	
	if (actualRead > 0)
	    return totalRead;
	else
	    return actualRead;
    }

    public long seek(long seekTo) {
	if (deallocated)
	    return 0;
	if (seekTo < cacheTotalSize && !randomAccess) {
	    readLocation = seekTo;
	    return seekTo;
	} else if (seekable && (randomAccess || (seekTo == 0))) {
	    long seeked = controller.seek(seekTo);
	    streamLocation = seekTo;
	    return seeked;
	} else {
	    // Couldn't seek
	    return -1;
	}
    }

    /****************************************************************
     * Cache Stuff
     ****************************************************************/
    
    private void addToCache(byte [] buffer, int offset, int size) {
	if (cacheBuffer == 0) {
	    cacheBuffer = nCreateCache(384 * 1024);
	    cacheAllocated = 384 * 1024;
	}
	
	if ((cacheTotalSize + size) > cacheAllocated)
	    return;

	nAddToCache(cacheBuffer, cacheTotalSize, buffer, offset, size);
	cacheTotalSize += size;
    }

    private void getFromCache(int location, byte [] buffer, int offset, int size) {
	nGetFromCache(cacheBuffer, location, buffer, offset, size);
    }

    private native int nCreateCache(int cacheSize);

    private native void nAddToCache(int cacheBuffer, int cacheOffset,
				   byte [] buffer, int bufOffset, int size);

    private native void nGetFromCache(int cacheBuffer, int cacheOffset,
				     byte [] buffer, int bufOffset, int size);

    private native void nFreeCache(int cacheBuffer);

    /****************************************************************
     * End Cache Stuff
     ****************************************************************/
    
    void dispose() {
	if (spinner != null) {
	    spinner.stop();
	    spinner = null;
	}

	dispose0();				      // native call
	
	if (cacheBuffer != 0) {
	    nFreeCache(cacheBuffer);
	    cacheBuffer = 0;
	}
    }

    protected void finalize() {
        dispose();
    }

    public synchronized void pause() {
	// System.err.println("In ActiveMovie.pause()");
	if (paused) return;
	donePaused = false;
	paused = true;
	// Block for the donePaused to clear.  This is
	// done so that the last read could be completed before
	// it returns.
	if (!donePaused) {
	    try {
		wait(250);
		donePaused = true;
	    } catch (InterruptedException e) {}
	}
    }

    public void restart() {
	// Restart the paused thread.
	// System.err.println("In ActiveMovie.restart()");
	deallocated = false;
	stopDataFlow(false);
	unPause();
    }

    private void unPause() {
	if (!paused) return;
	synchronized (this) {
	    donePaused = true;
	    paused = false;
	    notifyAll();
	}
    }

    public void kill() {
	deallocated = true;
	unPause();
	stopDataFlow(true);
	amStop();
    }

    native void dispose0();

    native boolean openFile(String file);
    native boolean openStream(boolean seekable,
			      boolean randomAccess,
			      int streamType,
			      long contentLength);
    static native int findWindow(String name);
}