Merge pull request #5565 from ByteHamster/simplify-opus

Simplify opus parsing
This commit is contained in:
ByteHamster 2021-11-21 21:03:12 +01:00 committed by GitHub
commit 473ba14f88
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 37 additions and 113 deletions

View File

@ -1,81 +0,0 @@
package de.danoeh.antennapod.parser.media.vorbis;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
class OggInputStream extends InputStream {
private final InputStream input;
/** True if OggInputStream is currently inside an Ogg page. */
private boolean isInPage;
private long bytesLeft;
public OggInputStream(InputStream input) {
super();
isInPage = false;
this.input = input;
}
@Override
public int read() throws IOException {
if (!isInPage) {
readOggPage();
}
if (isInPage && bytesLeft > 0) {
int result = input.read();
bytesLeft -= 1;
if (bytesLeft == 0) {
isInPage = false;
}
return result;
}
return -1;
}
private void readOggPage() throws IOException {
// find OggS
int[] buffer = new int[4];
int c;
boolean isInOggS = false;
while ((c = input.read()) != -1) {
switch (c) {
case 'O':
isInOggS = true;
buffer[0] = c;
break;
case 'g':
if (buffer[1] != c) {
buffer[1] = c;
} else {
buffer[2] = c;
}
break;
case 'S':
buffer[3] = c;
break;
default:
if (isInOggS) {
Arrays.fill(buffer, 0);
isInOggS = false;
}
}
if (buffer[0] == 'O' && buffer[1] == 'g' && buffer[2] == 'g'
&& buffer[3] == 'S') {
break;
}
}
// read segments
IOUtils.skipFully(input, 22);
bytesLeft = 0;
int numSegments = input.read();
for (int i = 0; i < numSegments; i++) {
bytesLeft += input.read();
}
isInPage = true;
}
}

View File

@ -65,11 +65,6 @@ public class VorbisCommentChapterReader extends VorbisCommentReader {
} }
} }
@Override
public void onNoVorbisCommentFound() {
System.out.println("No vorbis comment found");
}
@Override @Override
public void onEndOfComment() { public void onEndOfComment() {
System.out.println("End of comment"); System.out.println("End of comment");

View File

@ -35,43 +35,54 @@ public abstract class VorbisCommentReader {
*/ */
protected abstract void onContentVectorValue(String key, String value) throws VorbisCommentReaderException; protected abstract void onContentVectorValue(String key, String value) throws VorbisCommentReaderException;
protected abstract void onNoVorbisCommentFound();
protected abstract void onEndOfComment(); protected abstract void onEndOfComment();
protected abstract void onError(VorbisCommentReaderException exception); protected abstract void onError(VorbisCommentReaderException exception);
public void readInputStream(InputStream input) throws VorbisCommentReaderException { public void readInputStream(InputStream input) throws VorbisCommentReaderException {
try { try {
// look for identification header findIdentificationHeader(input);
if (findIdentificationHeader(input)) {
onVorbisCommentFound(); onVorbisCommentFound();
input = new OggInputStream(input); findOggPage(input);
if (findCommentHeader(input)) { findCommentHeader(input);
VorbisCommentHeader commentHeader = readCommentHeader(input); VorbisCommentHeader commentHeader = readCommentHeader(input);
onVorbisCommentHeaderFound(commentHeader); onVorbisCommentHeaderFound(commentHeader);
for (int i = 0; i < commentHeader.getUserCommentLength(); i++) { for (int i = 0; i < commentHeader.getUserCommentLength(); i++) {
readUserComment(input); readUserComment(input);
} }
onEndOfComment(); onEndOfComment();
} else {
onError(new VorbisCommentReaderException("No comment header found"));
}
} else {
onNoVorbisCommentFound();
}
} catch (IOException e) { } catch (IOException e) {
onError(new VorbisCommentReaderException(e)); onError(new VorbisCommentReaderException(e));
} }
} }
private void findOggPage(InputStream input) throws IOException {
// find OggS
byte[] buffer = new byte[4];
final byte[] oggPageHeader = {'O', 'g', 'g', 'S'};
for (int bytesRead = 0; bytesRead < SECOND_PAGE_MAX_LENGTH; bytesRead++) {
buffer[bytesRead % buffer.length] = (byte) input.read();
if (bufferMatches(buffer, oggPageHeader, bytesRead)) {
break;
}
}
// read segments
IOUtils.skipFully(input, 22);
int numSegments = input.read();
IOUtils.skipFully(input, numSegments);
}
private void readUserComment(InputStream input) throws VorbisCommentReaderException { private void readUserComment(InputStream input) throws VorbisCommentReaderException {
try { try {
long vectorLength = EndianUtils.readSwappedUnsignedInteger(input); long vectorLength = EndianUtils.readSwappedUnsignedInteger(input);
if (vectorLength > 20 * 1024 * 1024) {
// Avoid reading entire file if it is encoded incorrectly
throw new VorbisCommentReaderException("User comment unrealistically long: " + vectorLength);
}
String key = readContentVectorKey(input, vectorLength).toLowerCase(Locale.US); String key = readContentVectorKey(input, vectorLength).toLowerCase(Locale.US);
boolean readValue = onContentVectorKey(key); boolean readValue = onContentVectorKey(key);
if (readValue) { if (readValue) {
String value = readUtf8String(input, (int) (vectorLength - key.length() - 1)); String value = readUtf8String(input, vectorLength - key.length() - 1);
onContentVectorValue(key, value); onContentVectorValue(key, value);
} else { } else {
IOUtils.skipFully(input, vectorLength - key.length() - 1); IOUtils.skipFully(input, vectorLength - key.length() - 1);
@ -93,33 +104,32 @@ public abstract class VorbisCommentReader {
* identification header is found, it will be skipped completely and the * identification header is found, it will be skipped completely and the
* method will return true, otherwise false. * method will return true, otherwise false.
*/ */
private boolean findIdentificationHeader(InputStream input) throws IOException { private void findIdentificationHeader(InputStream input) throws IOException {
byte[] buffer = new byte[FIRST_OPUS_PAGE_LENGTH]; byte[] buffer = new byte[FIRST_OPUS_PAGE_LENGTH];
IOUtils.readFully(input, buffer); IOUtils.readFully(input, buffer);
final byte[] oggIdentificationHeader = new byte[]{ PACKET_TYPE_IDENTIFICATION, 'v', 'o', 'r', 'b', 'i', 's' }; final byte[] oggIdentificationHeader = new byte[]{ PACKET_TYPE_IDENTIFICATION, 'v', 'o', 'r', 'b', 'i', 's' };
for (int i = 6; i < buffer.length; i++) { for (int i = 6; i < buffer.length; i++) {
if (bufferMatches(buffer, oggIdentificationHeader, i)) { if (bufferMatches(buffer, oggIdentificationHeader, i)) {
IOUtils.skip(input, FIRST_OGG_PAGE_LENGTH - FIRST_OPUS_PAGE_LENGTH); IOUtils.skip(input, FIRST_OGG_PAGE_LENGTH - FIRST_OPUS_PAGE_LENGTH);
return true;
} else if (bufferMatches(buffer, "OpusHead".getBytes(), i)) { } else if (bufferMatches(buffer, "OpusHead".getBytes(), i)) {
return true; return;
} }
} }
return false; throw new IOException("No identification header found");
} }
private boolean findCommentHeader(InputStream input) throws IOException { private void findCommentHeader(InputStream input) throws IOException {
byte[] buffer = new byte[64]; // Enough space for some bytes. Used circularly. byte[] buffer = new byte[64]; // Enough space for some bytes. Used circularly.
final byte[] oggCommentHeader = new byte[]{ PACKET_TYPE_COMMENT, 'v', 'o', 'r', 'b', 'i', 's' }; final byte[] oggCommentHeader = new byte[]{ PACKET_TYPE_COMMENT, 'v', 'o', 'r', 'b', 'i', 's' };
for (int bytesRead = 0; bytesRead < SECOND_PAGE_MAX_LENGTH; bytesRead++) { for (int bytesRead = 0; bytesRead < SECOND_PAGE_MAX_LENGTH; bytesRead++) {
buffer[bytesRead % buffer.length] = (byte) input.read(); buffer[bytesRead % buffer.length] = (byte) input.read();
if (bufferMatches(buffer, oggCommentHeader, bytesRead)) { if (bufferMatches(buffer, oggCommentHeader, bytesRead)) {
return true; return;
} else if (bufferMatches(buffer, "OpusTags".getBytes(), bytesRead)) { } else if (bufferMatches(buffer, "OpusTags".getBytes(), bytesRead)) {
return true; return;
} }
} }
return false; throw new IOException("No comment header found");
} }
/** /**