
org.monte.media.avi.AVIReader Maven / Gradle / Ivy
The newest version!
package org.monte.media.avi;
import org.monte.media.Codec;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.util.EnumSet;
import org.monte.media.math.Rational;
import org.monte.media.Format;
import org.monte.media.Buffer;
import org.monte.media.MovieReader;
import java.io.File;
import java.io.IOException;
import javax.imageio.stream.ImageInputStream;
import static java.lang.Math.*;
import static org.monte.media.FormatKeys.*;
import static org.monte.media.AudioFormatKeys.*;
import static org.monte.media.VideoFormatKeys.*;
import org.monte.media.BufferFlag;
import org.monte.media.Registry;
import static org.monte.media.BufferFlag.*;
public class AVIReader extends AVIInputStream implements MovieReader {
public final static Format AVI = new Format(MediaTypeKey, MediaType.FILE, MimeTypeKey, MIME_AVI);
private Rational movieDuration = null;
public AVIReader(ImageInputStream in) throws IOException {
super(in);
}
public AVIReader(File file) throws IOException {
super(file);
}
@Override
public Format getFileFormat() throws IOException {
return AVI;
}
@Override
public Format getFormat(int track) throws IOException {
ensureRealized();
return tracks.get(track).format;
}
@Override
public void read(int track, Buffer buffer) throws IOException {
ensureRealized();
Track tr = tracks.get(track);
if (tr.readIndex >= tr.samples.size()) {
buffer.setFlagsTo(END_OF_MEDIA, DISCARD);
buffer.length = 0;
return;
}
Sample s = tr.samples.get((int) tr.readIndex);
in.seek(s.offset);
{
byte[] b;
if (buffer.data instanceof byte[]) {
b = (byte[]) buffer.data;
if (b.length < s.length) {
buffer.data = b = new byte[(((int) s.length + 1023) / 1024) * 1024];
}
} else {
buffer.data = b = new byte[(((int) s.length + 1023) / 1024) * 1024];
}
in.readFully(b, 0, (int) s.length);
}
buffer.offset = 0;
buffer.length = (int) s.length;
if (s.header != null) {
{
byte[] b;
if (buffer.header instanceof byte[]) {
b = (byte[]) buffer.data;
if (b.length != s.header.length) {
buffer.header = b = new byte[(((int) s.header.length + 1023) / 1024) * 1024];
}
} else {
buffer.header = b = new byte[(((int) s.header.length + 1023) / 1024) * 1024];
}
in.readFully(b, 0, (int) s.header.length);
}
} else {
buffer.header = null;
}
switch (tr.mediaType) {
case AUDIO: {
Format af = tr.format;
buffer.sampleCount = buffer.length / af.get(FrameSizeKey);
}
break;
case VIDEO: {
buffer.sampleCount = 1;
}
break;
case MIDI:
case TEXT:
default:
throw new UnsupportedOperationException("Unsupported media type " + tr.mediaType);
}
buffer.format = tr.format;
buffer.track = track;
buffer.sampleDuration = new Rational(tr.scale, tr.rate);
buffer.timeStamp = new Rational((s.timeStamp + tr.startTime) * tr.scale, tr.rate);
buffer.flags = s.isKeyframe ? EnumSet.of(KEYFRAME) : EnumSet.noneOf(BufferFlag.class);
tr.readIndex++;
}
private void decodePC(byte[] pc, int offset, int length, byte[] r, byte[] g, byte[] b) {
offset += 4;
for (int i = 0; i < 256; i++) {
r[i] = pc[offset + i * 4];
g[i] = pc[offset + i * 4 + 1];
b[i] = pc[offset + i * 4 + 2];
}
}
public BufferedImage read(int track, BufferedImage img) throws IOException {
Track tr = tracks.get(track);
if (tr.inputBuffer == null) {
tr.inputBuffer = new Buffer();
}
if (tr.codec == null) {
createCodec(tr);
}
Buffer buf = new Buffer();
buf.data = img;
do {
read(track, tr.inputBuffer);
tr.codec.process(tr.inputBuffer, buf);
} while (buf.isFlag(DISCARD) && !buf.isFlag(END_OF_MEDIA));
if (tr.inputBuffer.isFlag(END_OF_MEDIA)) {
return null;
}
return (BufferedImage) buf.data;
}
private void createCodec(Track tr) {
Format fmt = tr.format;
Codec codec = createCodec(fmt);
String enc = fmt.get(EncodingKey);
if (codec == null) {
throw new UnsupportedOperationException("Track " + tr + " no codec found for format " + fmt);
} else {
if (fmt.get(MediaTypeKey) == MediaType.VIDEO) {
if (null == codec.setInputFormat(fmt)) {
throw new UnsupportedOperationException("Track " + tr + " codec does not support input format " + fmt + ". codec=" + codec);
}
Format outFormat = fmt.prepend(MediaTypeKey, MediaType.VIDEO,
MimeTypeKey, MIME_JAVA,
EncodingKey, ENCODING_BUFFERED_IMAGE, DataClassKey, BufferedImage.class);
if (null == codec.setOutputFormat(outFormat)) {
throw new UnsupportedOperationException("Track " + tr + " codec does not support output format " + outFormat + ". codec=" + codec);
}
}
}
tr.codec = codec;
}
private Codec createCodec(Format fmt) {
Codec[] codecs = Registry.getInstance().getDecoders(fmt.prepend(MimeTypeKey, MIME_AVI));
return codecs.length == 0 ? null : codecs[0];
}
@Override
public Rational getReadTime(int track) throws IOException {
Track tr = tracks.get(track);
if (tr.samples.size() > tr.readIndex) {
Sample s = tr.samples.get((int) tr.readIndex);
return new Rational((s.timeStamp + tr.startTime) * tr.scale, tr.rate);
}
return new Rational(0, 1);
}
@Override
public int nextTrack() throws IOException {
ensureRealized();
Rational ts = new Rational(Integer.MAX_VALUE, 1);
int nextTrack = -1;
for (int i = 0, n = tracks.size(); i < n; i++) {
Track tr = tracks.get(i);
if (tr.samples.isEmpty()) {
continue;
}
Sample currentSample = tr.readIndex < tr.samples.size() ? tr.samples.get((int) tr.readIndex) : tr.samples.get(tr.samples.size() - 1);
long readTimeStamp = currentSample.timeStamp;
if (tr.readIndex >= tr.samples.size()) {
readTimeStamp += currentSample.duration;
}
Rational trts = new Rational((readTimeStamp + tr.startTime) * tr.scale, tr.rate);
if (trts.compareTo(ts) < 0 && tr.readIndex < tr.samples.size()) {
ts = trts;
nextTrack = i;
}
}
return nextTrack;
}
@Override
public Rational getDuration() {
try {
ensureRealized();
} catch (IOException ex) {
ex.printStackTrace();
return new Rational(0, 1);
}
if (movieDuration == null) {
Rational maxDuration = new Rational(0, 1);
for (Track tr : tracks) {
Rational trackDuration = new Rational((tr.length * tr.scale + tr.startTime), tr.rate);
if (maxDuration.compareTo(trackDuration) < 0) {
maxDuration = trackDuration;
}
}
movieDuration = maxDuration;
}
return movieDuration;
}
@Override
public Rational getDuration(int track) {
Track tr = tracks.get(track);
Rational trackDuration = new Rational((tr.length * tr.scale + tr.startTime), tr.rate);
return trackDuration;
}
@Override
public long getTimeScale(int track) {
return tracks.get(track).rate;
}
@Override
public long timeToSample(int track, Rational time) {
Track tr = tracks.get(track);
long index = time.getNumerator() * tr.rate / time.getDenominator() / tr.scale - tr.startTime;
if (tr.mediaType == AVIMediaType.AUDIO) {
int count = 0;
for (int i = 0, n = tr.samples.size(); i < n; i++) {
long d = tr.samples.get(i).duration * tr.scale;
if (count + d > index) {
index = i;
break;
}
count += d;
}
}
return max(0, min(index, tr.samples.size()));
}
@Override
public Rational sampleToTime(int track, long sampleIndex) throws IOException {
ensureRealized();
Track tr = tracks.get(track);
Sample sample = tr.samples.get((int) max(0, min(tr.samples.size() - 1, sampleIndex)));
long time = (tr.startTime + sample.timeStamp) * tr.scale;
if (sampleIndex >= tr.samples.size()) {
time += sample.duration * tr.scale;
}
return new Rational(time, tr.rate);
}
@Override
public void setMovieReadTime(Rational newValue) throws IOException {
ensureRealized();
for (int t = 0, n = tracks.size(); t < n; t++) {
Track tr = tracks.get(t);
int sample = (int) min(timeToSample(t, newValue), tr.samples.size() - 1);
if (tr.readIndex > sample) {
tr.readIndex = 0;
}
for (; sample >= 0 && sample > tr.readIndex && !tr.samples.get(sample).isKeyframe; sample--);
tr.readIndex = sample;
}
}
@Override
public int findTrack(int fromTrack, Format format) throws IOException {
for (int i = fromTrack, n = getTrackCount(); i < n; i++) {
if (getFormat(i).matches(format)) {
return i;
}
}
return -1;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy