Merge pull request #5565 from ByteHamster/simplify-opus
Simplify opus parsing
This commit is contained in:
commit
473ba14f88
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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");
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue