Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public class OggPacketInputStream extends InputStream {

private List<OggSeekPoint> seekPoints;
private OggPageHeader pageHeader;
private Integer trackSerial;
private int bytesLeftInPacket;
private boolean packetContinues;
private int nextPacketSegmentIndex;
Expand Down Expand Up @@ -60,6 +61,7 @@ public boolean startNewTrack() {
}

pageHeader = null;
trackSerial = null;
state = State.PACKET_BOUNDARY;
return true;
}
Expand Down Expand Up @@ -162,16 +164,40 @@ private boolean loadNextPage() throws IOException {
return false;
}

if (!readPageHeader()) {
if (packetContinues) {
throw new IllegalStateException("Stream ended in the middle of a packet.");
while (true) {
if (!readPageHeader()) {
if (packetContinues) {
throw new IllegalStateException("Stream ended in the middle of a packet.");
}
return false;
}
return false;

if (trackSerial == null) {
// track the first stream we detect. Ideally, this is an audio stream.
// If an Ogg file starts with, i.e. a Theora video stream, it won't be detected anyway
// This ensures that we're only tracking pages that belong to the initially detected audio stream
// so we aren't mixing in packets from other streams.
trackSerial = pageHeader.streamIdentifier;
} else if (pageHeader.streamIdentifier != trackSerial) {
// skip segments not belonging to the stream we're tracking
inputStream.skipFully(getTotalPageSegmentLength());
continue;
}

nextPacketSegmentIndex = 0;
state = State.PACKET_READ;
return true;
}
}

nextPacketSegmentIndex = 0;
state = State.PACKET_READ;
return true;
private long getTotalPageSegmentLength() {
long segmentLength = 0;

for (int i = 0; i < pageHeader.segmentCount; i++) {
segmentLength += segmentSizes[i];
}

return segmentLength;
}

/**
Expand Down Expand Up @@ -330,7 +356,8 @@ public List<OggSeekPoint> createSeekTable(int sampleRate) throws IOException {
byte[] data = new byte[(int) inputStream.getContentLength()];
int dataLength = StreamTools.readUntilEnd(inputStream, data, 0, data.length);

List<OggSeekPoint> seekPoints = new OggPageScanner(absoluteOffset, data, dataLength).createSeekTable(sampleRate);
List<OggSeekPoint> seekPoints = new OggPageScanner(absoluteOffset, data, dataLength)
.createSeekTable(sampleRate, pageHeader.streamIdentifier);

inputStream.seek(savedPosition);
return seekPoints;
Expand Down Expand Up @@ -373,7 +400,7 @@ private OggStreamSizeInfo scanForSizeInfo(int tailLength, int sampleRate) throws
int dataLength = StreamTools.readUntilEnd(inputStream, data, 0, data.length);

return new OggPageScanner(absoluteOffset, data, dataLength)
.scanForSizeInfo(pageHeader.byteStreamPosition, sampleRate);
.scanForSizeInfo(pageHeader.byteStreamPosition, sampleRate, pageHeader.streamIdentifier);
}

/**
Expand All @@ -393,11 +420,9 @@ private boolean continuePacket() throws IOException {
// Load more segments for this packet from the next page.
if (!loadNextNonEmptyPage()) {
throw new IllegalStateException("Track or stream end reached within an incomplete packet.");
} else if (!initialisePacket()) {
return false;
}

return true;
return initialisePacket();
}

private enum State {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class OggPageScanner {
private int pageSize;
private long byteStreamPosition;
private int pageSequence;
private int streamSerial;

/**
* @param absoluteOffset Current position of the stream in bytes.
Expand All @@ -37,7 +38,7 @@ public OggPageScanner(long absoluteOffset, byte[] data, int dataLength) {
* @return If the data contains the header of the last page in the OGG stream, then stream size information,
* otherwise <code>null</code>.
*/
public OggStreamSizeInfo scanForSizeInfo(long firstPageOffset, int sampleRate) {
public OggStreamSizeInfo scanForSizeInfo(long firstPageOffset, int sampleRate, int targetSerial) {
ByteBuffer buffer = ByteBuffer.wrap(data, 0, dataLength);
int head = buffer.getInt(0);

Expand All @@ -47,7 +48,7 @@ public OggStreamSizeInfo scanForSizeInfo(long firstPageOffset, int sampleRate) {

if (attemptReadHeader(buffer)) {
do {
if ((flags & OggPageHeader.FLAG_LAST_PAGE) != 0) {
if (streamSerial == targetSerial && (flags & OggPageHeader.FLAG_LAST_PAGE) != 0) {
return new OggStreamSizeInfo((byteStreamPosition - firstPageOffset) + pageSize,
Long.reverseBytes(reversedPosition), firstPageOffset, byteStreamPosition, sampleRate);
}
Expand All @@ -68,7 +69,7 @@ public OggStreamSizeInfo scanForSizeInfo(long firstPageOffset, int sampleRate) {
* @param sampleRate Sample rate of the track in the stream.
* @return A list of OggSeekPoint objects representing the seek points in the stream.
*/
public List<OggSeekPoint> createSeekTable(int sampleRate) {
public List<OggSeekPoint> createSeekTable(int sampleRate, int targetSerial) {
List<OggSeekPoint> seekPoints = new ArrayList<>();

ByteBuffer buffer = ByteBuffer.wrap(data, 0, dataLength);
Expand All @@ -78,7 +79,7 @@ public List<OggSeekPoint> createSeekTable(int sampleRate) {
if (head == OGG_PAGE_HEADER_INT) {
buffer.position(i);

if (attemptReadHeader(buffer)) {
if (attemptReadHeader(buffer) && streamSerial == targetSerial) {
long position = byteStreamPosition;
long granulePosition = Long.reverseBytes(reversedPosition);
long timecode = granulePosition / (sampleRate / 1000);
Expand Down Expand Up @@ -124,6 +125,7 @@ private boolean attemptReadHeader(ByteBuffer buffer) {

flags = buffer.get(start + 5) & 0xFF;
reversedPosition = buffer.getLong(start + 6);
streamSerial = Integer.reverseBytes(buffer.getInt(start + 14));
byteStreamPosition = absoluteOffset + start;
pageSize = minimumCapacity;

Expand Down
Loading