/*
* @(#)BasicTrack.java 1.22 02/08/21
*
* Copyright (c) 1996-2002 Sun Microsystems, Inc. All rights reserved.
*/
// TODO: make this an inner class of BasicPullParser if it makes sense. (ie if it works
// for wav and qt parsers)
package com.sun.media.parser;
import java.io.IOException;
import javax.media.*;
import javax.media.Format;
import javax.media.protocol.SourceStream;
import javax.media.protocol.PullSourceStream;
public class BasicTrack implements Track {
private Format format;
private boolean enabled = true;
protected Time duration;
private Time startTime;
private int numBuffers;
private int dataSize;
private PullSourceStream stream;
private long minLocation;
private long maxLocation;
private long maxStartLocation;
private BasicPullParser parser; // TODO: Won't need this if inner class.
private long sequenceNumber = 0;
private TrackListener listener;
private long seekLocation = -1L;
private long mediaSizeAtEOM = -1L; // update when EOM implied by IOException occurs
private boolean warnedUserOfReadPastEOM = false;
public BasicTrack(BasicPullParser parser,
Format format, boolean enabled, Time duration, Time startTime,
int numBuffers, int dataSize, PullSourceStream stream) {
this(parser, format, enabled, duration, startTime,
numBuffers, dataSize, stream,
0L, Long.MAX_VALUE);
}
/**
* Note to implementors who want to use this class.
* If the maxLocation is not known, then
* specify Long.MAX_VALUE for this parameter
*/
public BasicTrack(BasicPullParser parser,
Format format, boolean enabled, Time duration, Time startTime,
int numBuffers, int dataSize, PullSourceStream stream,
long minLocation, long maxLocation) {
this.parser = parser;
this.format = format;
this.enabled = enabled;
this.duration = duration;
this.startTime = startTime;
this.numBuffers = numBuffers;
this.dataSize = dataSize;
this.stream = stream;
this.minLocation = minLocation;
this.maxLocation = maxLocation;
maxStartLocation = maxLocation - dataSize;
}
public Format getFormat() {
return format;
}
public void setEnabled(boolean t) {
enabled = t;
}
public boolean isEnabled() {
return enabled;
}
public Time getDuration() {
return duration;
}
public Time getStartTime() {
return startTime;
}
// TODO: create a list of TrackListeners
public void setTrackListener(TrackListener l) {
listener = l;
}
public synchronized void setSeekLocation(long location) {
seekLocation = location;
}
public synchronized long getSeekLocation() {
return seekLocation;
}
public void readFrame(Buffer buffer) {
if (buffer == null)
return;
if (!enabled) {
buffer.setDiscard(true);
return;
}
buffer.setFormat(format); // Need to do this every time ???
Object obj = buffer.getData();
byte[] data;
long location;
boolean needToSeek;
synchronized(this) {
if (seekLocation != -1) {
location = seekLocation;
if (seekLocation < maxLocation)
seekLocation = -1;
needToSeek = true;
} else {
location = parser.getLocation(stream);
needToSeek = false;
}
}
int needDataSize;
//TODO START
if (location < minLocation) {
// TODO: should probably seek or skip to minLocation
buffer.setDiscard(true);
return;
} else if (location >= maxLocation) {
buffer.setLength(0);
buffer.setEOM(true);
return;
} else if (location > maxStartLocation) {
needDataSize = dataSize - (int) (location - maxStartLocation);
} else {
needDataSize = dataSize;
}
//TODO END
if ( (obj == null) ||
(! (obj instanceof byte[]) ) ||
( ((byte[])obj).length < needDataSize) ) {
// System.out.println("readFrame creating new byte data of size " + needDataSize);
data = new byte[needDataSize];
buffer.setData(data);
} else {
data = (byte[]) obj;
}
try {
if ( (parser.cacheStream != null) && (listener != null) ) {
if ( parser.cacheStream.willReadBytesBlock(location, needDataSize) ) {
// System.out.println("read will block: " + location + " : " +
// needDataSize);
listener.readHasBlocked(this);
}
}
if (needToSeek) {
// TODO: need to handle case where the stream is not seekable and
// caching is not beeing done (ie cacheStream is null)
long pos = ((javax.media.protocol.Seekable)stream).seek(location);
if ( pos == com.sun.media.protocol.BasicSourceStream.LENGTH_DISCARD) {
buffer.setDiscard(true);
return;
}
}
if (parser.getMediaTime() != null)
buffer.setTimeStamp(parser.getMediaTime().getNanoseconds());
else
buffer.setTimeStamp(Buffer.TIME_UNKNOWN);
buffer.setDuration(Buffer.TIME_UNKNOWN);
int actualBytesRead = parser.readBytes(stream, data, needDataSize);
buffer.setOffset(0);
buffer.setLength(actualBytesRead);
// TODO: need setSequenceNumber and getSequenceNumber in Buffer
buffer.setSequenceNumber(++sequenceNumber);
// System.out.println("Time stamp: " + (buffer.getTimeStamp())/1.E9);
} catch (IOException e) {
if (maxLocation != Long.MAX_VALUE) {
// Known maxLocation. So, this is a case of
// deliberately reading past EOM
if (!warnedUserOfReadPastEOM) {
com.sun.media.Log.warning("Warning: Attempt to read past End of Media");
com.sun.media.Log.warning("This typically happens if the duration is not known or");
com.sun.media.Log.warning("if the media file has incorrect header info");
warnedUserOfReadPastEOM = true;
}
buffer.setLength(0); // Need this??
buffer.setEOM(true);
} else {
// Unknown maxLocation, due to unknown content length
// EOM reached before the required bytes could be read.
long length = parser.streams[0].getContentLength();
if ( length != SourceStream.LENGTH_UNKNOWN ) {
// If content-length is known, discard this buffer, update
// maxLocation, maxStartLocation and mediaSizeAtEOM.
// The next readFrame will read the remaining data till EOM.
maxLocation = length;
maxStartLocation = maxLocation - dataSize;
mediaSizeAtEOM = maxLocation - minLocation;
buffer.setLength(0); // Need this??
buffer.setDiscard(true);
} else {
// Content Length is still unknown after an IOException.
// We can still discard this buffer and keep discarding
// until content length is known. But this may go into
// into an infinite loop, if there are real IO errors
// So, return EOM
maxLocation = parser.getLocation(stream);
maxStartLocation = maxLocation - dataSize;
mediaSizeAtEOM = maxLocation - minLocation;
buffer.setLength(0); // Need this??
buffer.setEOM(true);
}
}
// TODO: $$$$ Update maxFrame and duration
}
// System.out.println("parser's location: " + parser.getLocation(stream));
// parser.getMediaTime(); // Side effect printout
}
public int mapTimeToFrame(Time t) {
return FRAME_UNKNOWN;
}
public Time mapFrameToTime(int frameNumber) {
return TIME_UNKNOWN;
}
public long getMediaSizeAtEOM() {
return mediaSizeAtEOM; // updated when EOM implied by IOException occurs
}
}
|