/*
* @(#)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);
}
|