diff options
author | Senventise <2375741859@qq.com> | 2024-06-09 16:04:59 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-09 10:04:59 +0200 |
commit | 91f8ed055f552cdf32fd8bbcdd632534542257f8 (patch) | |
tree | deba8bbdc381a20cd99217e341ae4481daed96f7 | |
parent | e2ff09bd344e4c53ef40381f533fe26914604492 (diff) | |
download | AntennaPod-91f8ed055f552cdf32fd8bbcdd632534542257f8.zip |
Support parsing vorbis comments spanning across pages (#7215)
4 files changed, 72 insertions, 43 deletions
diff --git a/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentReader.java b/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentReader.java index 7b907b969..607a6b1f1 100644 --- a/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentReader.java +++ b/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentReader.java @@ -20,16 +20,14 @@ public abstract class VorbisCommentReader { private static final int PACKET_TYPE_IDENTIFICATION = 1; private static final int PACKET_TYPE_COMMENT = 3; - private final InputStream input; + private final VorbisInputStream input; VorbisCommentReader(InputStream input) { - this.input = input; + this.input = new VorbisInputStream(input); } public void readInputStream() throws VorbisCommentReaderException { try { - findIdentificationHeader(); - findOggPage(); findCommentHeader(); VorbisCommentHeader commentHeader = readCommentHeader(); Log.d(TAG, commentHeader.toString()); @@ -41,26 +39,6 @@ public abstract class VorbisCommentReader { } } - private void findOggPage() 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++) { - int data = input.read(); - if (data == -1) { - throw new IOException("EOF while trying to find vorbis page"); - } - buffer[bytesRead % buffer.length] = (byte) data; - if (bufferMatches(buffer, oggPageHeader, bytesRead)) { - break; - } - } - // read segments - IOUtils.skipFully(input, 22); - int numSegments = input.read(); - IOUtils.skipFully(input, numSegments); - } - private void readUserComment() throws VorbisCommentReaderException { try { long vectorLength = EndianUtils.readSwappedUnsignedInteger(input); @@ -90,25 +68,6 @@ public abstract class VorbisCommentReader { return charset.newDecoder().decode(ByteBuffer.wrap(buffer)).toString(); } - /** - * Looks for an identification header in the first page of the file. If an - * identification header is found, it will be skipped completely - */ - private void findIdentificationHeader() throws IOException { - byte[] buffer = new byte[FIRST_OPUS_PAGE_LENGTH]; - IOUtils.readFully(input, buffer); - final byte[] oggIdentificationHeader = new byte[]{ PACKET_TYPE_IDENTIFICATION, 'v', 'o', 'r', 'b', 'i', 's' }; - for (int i = 6; i < buffer.length; i++) { - if (bufferMatches(buffer, oggIdentificationHeader, i)) { - IOUtils.skip(input, FIRST_OGG_PAGE_LENGTH - FIRST_OPUS_PAGE_LENGTH); - return; - } else if (bufferMatches(buffer, "OpusHead".getBytes(), i)) { - return; - } - } - throw new IOException("No vorbis identification header found"); - } - private void findCommentHeader() throws IOException { 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' }; diff --git a/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisInputStream.java b/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisInputStream.java new file mode 100644 index 000000000..802813ab9 --- /dev/null +++ b/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisInputStream.java @@ -0,0 +1,69 @@ +package de.danoeh.antennapod.parser.media.vorbis; + +import org.apache.commons.io.IOUtils; + +import java.io.BufferedInputStream; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; + + +public class VorbisInputStream extends FilterInputStream { + private static final byte[] CAPTURE_PATTERN = {'O', 'g', 'g', 'S'}; + private static final int HEADER_SKIP_LENGTH = 1 + 1 + 8 + 4 + 4 + 4; + + private final BufferedInputStream inputStream; + private int pageRemainBytes = 0; + + protected VorbisInputStream(InputStream in) { + super(in); + inputStream = new BufferedInputStream(in); + } + + private int parsePageHeader(InputStream in) throws IOException { + byte[] capturePattern = new byte[4]; + + IOUtils.readFully(in, capturePattern, 0, 4); + if (!Arrays.equals(CAPTURE_PATTERN, capturePattern)) { + throw new IOException("Invalid page header"); + } + + IOUtils.skipFully(in, HEADER_SKIP_LENGTH); + + int pageSegments = in.read(); + byte[] segmentTable = new byte[pageSegments]; + int pageLength = 0; + IOUtils.readFully(in, segmentTable); + for (byte segment:segmentTable) { + pageLength += (segment & 0xff); + } + + return pageLength; + } + + /** check and update remaining bytes **/ + private void updateRemainBytes() throws IOException { + if (pageRemainBytes == 0) { + pageRemainBytes = parsePageHeader(inputStream); + } else if (pageRemainBytes < 0) { + throw new IOException("Page remain bytes less than 0"); + } + } + + @Override + public int read() throws IOException { + updateRemainBytes(); + pageRemainBytes--; + return inputStream.read(); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + updateRemainBytes(); + int bytesToRead = Math.min(len, pageRemainBytes); + IOUtils.readFully(inputStream, b, off, bytesToRead); + this.pageRemainBytes -= bytesToRead; + return bytesToRead; + } +} diff --git a/parser/media/src/test/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentMetadataReaderTest.java b/parser/media/src/test/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentMetadataReaderTest.java index 7f71c8fa9..6dab507a3 100644 --- a/parser/media/src/test/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentMetadataReaderTest.java +++ b/parser/media/src/test/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentMetadataReaderTest.java @@ -16,6 +16,7 @@ public class VorbisCommentMetadataReaderTest { public void testRealFilesAuphonic() throws IOException, VorbisCommentReaderException { testRealFileAuphonic("auphonic.ogg"); testRealFileAuphonic("auphonic.opus"); + testRealFileAuphonic("opus-comment.opus"); } public void testRealFileAuphonic(String filename) throws IOException, VorbisCommentReaderException { diff --git a/parser/media/src/test/resources/opus-comment.opus b/parser/media/src/test/resources/opus-comment.opus Binary files differnew file mode 100644 index 000000000..d636f2be6 --- /dev/null +++ b/parser/media/src/test/resources/opus-comment.opus |