Fix OGG/Vorbis stream looping

Previously, a discontinuity occurred when looping an OGG/Vorbis stream,
this happens due to a bug in J-Ogg library. To work around this issue,
we load the logical ogg stream and vorbis stream from scratch based
on the already loaded existing OGG pages instead of using the
buggy setTime(0) call.
experimental
shadowislord 10 years ago
parent 6bdebb937a
commit e1910fdff2
  1. 15
      jme3-jogg/src/main/java/com/jme3/audio/plugins/CachedOggStream.java
  2. 59
      jme3-jogg/src/main/java/com/jme3/audio/plugins/OGGLoader.java

@ -62,6 +62,7 @@ public class CachedOggStream implements PhysicalOggStream {
private OggPage lastPage;
private int pageNumber;
private int serialno;
public CachedOggStream(InputStream in) throws IOException {
sourceStream = in;
@ -106,6 +107,19 @@ public class CachedOggStream implements PhysicalOggStream {
}
}
public LogicalOggStream reloadLogicalOggStream() {
logicalStreams.clear();
LogicalOggStreamImpl los = new LogicalOggStreamImpl(this, serialno);
logicalStreams.put(serialno, los);
for (IntMap.Entry<OggPage> entry : oggPages) {
los.addPageNumberMapping(entry.getKey());
los.addGranulePosition(entry.getValue().getAbsoluteGranulePosition());
}
return los;
}
private int readOggNextPage() throws IOException {
if (eos)
return -1;
@ -121,6 +135,7 @@ public class CachedOggStream implements PhysicalOggStream {
LogicalOggStreamImpl los = (LogicalOggStreamImpl) logicalStreams.get(op.getStreamSerialNumber());
if (los == null) {
serialno = op.getStreamSerialNumber();
los = new LogicalOggStreamImpl(this, op.getStreamSerialNumber());
logicalStreams.put(op.getStreamSerialNumber(), los);
los.checkFormat(op);

@ -49,8 +49,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;
public class OGGLoader implements AssetLoader {
@ -66,9 +64,13 @@ public class OGGLoader implements AssetLoader {
private static class JOggInputStream extends InputStream {
private boolean endOfStream = false;
protected final VorbisStream vs;
protected PhysicalOggStream ps;
protected LogicalOggStream ls;
protected VorbisStream vs;
public JOggInputStream(VorbisStream vs){
public JOggInputStream(PhysicalOggStream ps, LogicalOggStream ls, VorbisStream vs){
this.ps = ps;
this.ls = ls;
this.vs = vs;
}
@ -84,8 +86,9 @@ public class OGGLoader implements AssetLoader {
@Override
public int read(byte[] buf, int offset, int length) throws IOException {
if (endOfStream)
if (endOfStream) {
return -1;
}
int bytesRead = 0, cnt = 0;
assert length % 2 == 0; // read buffer should be even
@ -101,7 +104,6 @@ public class OGGLoader implements AssetLoader {
}
System.out.println("");
System.out.println("EOS");
endOfStream = true;
break;
@ -111,7 +113,6 @@ public class OGGLoader implements AssetLoader {
swapBytes(buf, offset, bytesRead);
return bytesRead;
}
@Override
@ -123,29 +124,24 @@ public class OGGLoader implements AssetLoader {
private static class SeekableJOggInputStream extends JOggInputStream implements SeekableStream {
private LogicalOggStream los;
private float duration;
public SeekableJOggInputStream(VorbisStream vs, LogicalOggStream los, float duration){
super(vs);
this.los = los;
this.duration = duration;
public SeekableJOggInputStream(PhysicalOggStream ps, LogicalOggStream ls, VorbisStream vs){
super(ps, ls, vs);
}
public void setTime(float time) {
System.out.println("--setTime--)");
System.out.println("max granule : "+los.getMaximumGranulePosition());
System.out.println("current granule : "+los.getTime());
System.out.println("asked Time : "+time);
System.out.println("new granule : "+(time/duration*los.getMaximumGranulePosition()));
System.out.println("new granule2 : "+(time*vs.getIdentificationHeader().getSampleRate()));
if (time != 0.0) {
throw new UnsupportedOperationException("OGG/Vorbis seeking only supported for time = 0");
}
try {
los.setTime((long)(time*vs.getIdentificationHeader().getSampleRate()));
// HACK: Reload the logical and vorbis streams from scratch
// based on the existing ogg page data.
// This fixes an audio discontinuity issue when looping
// an streaming OGG file via setTime(0).
ls = ((CachedOggStream) ps).reloadLogicalOggStream();
vs = new VorbisStream(ls);
} catch (IOException ex) {
Logger.getLogger(OGGLoader.class.getName()).log(Level.SEVERE, null, ex);
ex.printStackTrace();
}
}
@ -237,11 +233,11 @@ public class OGGLoader implements AssetLoader {
}
}
private InputStream readToStream(boolean seekable,float streamDuration){
private InputStream readToStream(boolean seekable) {
if (seekable) {
return new SeekableJOggInputStream(vorbisStream,loStream,streamDuration);
return new SeekableJOggInputStream(oggStream, loStream, vorbisStream);
} else {
return new JOggInputStream(vorbisStream);
return new JOggInputStream(oggStream, loStream, vorbisStream);
}
}
@ -275,7 +271,7 @@ public class OGGLoader implements AssetLoader {
// might return -1 if unknown
float streamDuration = computeStreamDuration();
audioStream.updateData(readToStream(oggStream.isSeekable(),streamDuration), streamDuration);
audioStream.updateData(readToStream(oggStream.isSeekable()), streamDuration);
return audioStream;
}
}
@ -292,12 +288,7 @@ public class OGGLoader implements AssetLoader {
InputStream in = null;
try {
in = info.openStream();
AudioData data = load(in, readStream, streamCache);
if (data instanceof AudioStream){
// audio streams must remain open
in = null;
}
return data;
return load(in, readStream, streamCache);
} finally {
if (in != null){
in.close();

Loading…
Cancel
Save