mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Add a type check for OGG files with a single payload page
Also make some javadocs more consistent with the rest of the library. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=211071559
This commit is contained in:
parent
595b6b8fde
commit
514edb699f
7 changed files with 91 additions and 53 deletions
|
|
@ -23,9 +23,7 @@ import com.google.android.exoplayer2.util.Assertions;
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/** Seeks in an Ogg stream. */
|
||||||
* Used to seek in an Ogg stream.
|
|
||||||
*/
|
|
||||||
/* package */ final class DefaultOggSeeker implements OggSeeker {
|
/* package */ final class DefaultOggSeeker implements OggSeeker {
|
||||||
|
|
||||||
//@VisibleForTesting
|
//@VisibleForTesting
|
||||||
|
|
@ -56,19 +54,27 @@ import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an OggSeeker.
|
* Constructs an OggSeeker.
|
||||||
|
*
|
||||||
* @param startPosition Start position of the payload (inclusive).
|
* @param startPosition Start position of the payload (inclusive).
|
||||||
* @param endPosition End position of the payload (exclusive).
|
* @param endPosition End position of the payload (exclusive).
|
||||||
* @param streamReader StreamReader instance which owns this OggSeeker
|
* @param streamReader The {@link StreamReader} that owns this seeker.
|
||||||
* @param firstPayloadPageSize The total size of the first payload page, in bytes.
|
* @param firstPayloadPageSize The total size of the first payload page, in bytes.
|
||||||
* @param firstPayloadPageGranulePosition The granule position of the first payload page.
|
* @param firstPayloadPageGranulePosition The granule position of the first payload page.
|
||||||
|
* @param firstPayloadPageIsLastPage Whether the first payload page is also the last page in the
|
||||||
|
* ogg stream.
|
||||||
*/
|
*/
|
||||||
public DefaultOggSeeker(long startPosition, long endPosition, StreamReader streamReader,
|
public DefaultOggSeeker(
|
||||||
int firstPayloadPageSize, long firstPayloadPageGranulePosition) {
|
long startPosition,
|
||||||
|
long endPosition,
|
||||||
|
StreamReader streamReader,
|
||||||
|
long firstPayloadPageSize,
|
||||||
|
long firstPayloadPageGranulePosition,
|
||||||
|
boolean firstPayloadPageIsLastPage) {
|
||||||
Assertions.checkArgument(startPosition >= 0 && endPosition > startPosition);
|
Assertions.checkArgument(startPosition >= 0 && endPosition > startPosition);
|
||||||
this.streamReader = streamReader;
|
this.streamReader = streamReader;
|
||||||
this.startPosition = startPosition;
|
this.startPosition = startPosition;
|
||||||
this.endPosition = endPosition;
|
this.endPosition = endPosition;
|
||||||
if (firstPayloadPageSize == endPosition - startPosition) {
|
if (firstPayloadPageSize == endPosition - startPosition || firstPayloadPageIsLastPage) {
|
||||||
totalGranules = firstPayloadPageGranulePosition;
|
totalGranules = firstPayloadPageGranulePosition;
|
||||||
state = STATE_IDLE;
|
state = STATE_IDLE;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -240,11 +246,11 @@ import java.io.IOException;
|
||||||
* Skips to the next page.
|
* Skips to the next page.
|
||||||
*
|
*
|
||||||
* @param input The {@code ExtractorInput} to skip to the next page.
|
* @param input The {@code ExtractorInput} to skip to the next page.
|
||||||
* @throws IOException thrown if peeking/reading from the input fails.
|
* @throws IOException If peeking/reading from the input fails.
|
||||||
* @throws InterruptedException thrown if interrupted while peeking/reading from the input.
|
* @throws InterruptedException If the thread is interrupted.
|
||||||
* @throws EOFException if the next page can't be found before the end of the input.
|
* @throws EOFException If the next page can't be found before the end of the input.
|
||||||
*/
|
*/
|
||||||
//@VisibleForTesting
|
// @VisibleForTesting
|
||||||
void skipToNextPage(ExtractorInput input) throws IOException, InterruptedException {
|
void skipToNextPage(ExtractorInput input) throws IOException, InterruptedException {
|
||||||
if (!skipToNextPage(input, endPosition)) {
|
if (!skipToNextPage(input, endPosition)) {
|
||||||
// Not found until eof.
|
// Not found until eof.
|
||||||
|
|
@ -256,21 +262,21 @@ import java.io.IOException;
|
||||||
* Skips to the next page. Searches for the next page header.
|
* Skips to the next page. Searches for the next page header.
|
||||||
*
|
*
|
||||||
* @param input The {@code ExtractorInput} to skip to the next page.
|
* @param input The {@code ExtractorInput} to skip to the next page.
|
||||||
* @param until Searches until this position.
|
* @param limit The limit up to which the search should take place.
|
||||||
* @return true if the next page is found.
|
* @return Whether the next page was found.
|
||||||
* @throws IOException thrown if peeking/reading from the input fails.
|
* @throws IOException thrown if peeking/reading from the input fails.
|
||||||
* @throws InterruptedException thrown if interrupted while peeking/reading from the input.
|
* @throws InterruptedException thrown if interrupted while peeking/reading from the input.
|
||||||
*/
|
*/
|
||||||
//@VisibleForTesting
|
// @VisibleForTesting
|
||||||
boolean skipToNextPage(ExtractorInput input, long until)
|
boolean skipToNextPage(ExtractorInput input, long limit)
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
until = Math.min(until + 3, endPosition);
|
limit = Math.min(limit + 3, endPosition);
|
||||||
byte[] buffer = new byte[2048];
|
byte[] buffer = new byte[2048];
|
||||||
int peekLength = buffer.length;
|
int peekLength = buffer.length;
|
||||||
while (true) {
|
while (true) {
|
||||||
if (input.getPosition() + peekLength > until) {
|
if (input.getPosition() + peekLength > limit) {
|
||||||
// Make sure to not peek beyond the end of the input.
|
// Make sure to not peek beyond the end of the input.
|
||||||
peekLength = (int) (until - input.getPosition());
|
peekLength = (int) (limit - input.getPosition());
|
||||||
if (peekLength < 4) {
|
if (peekLength < 4) {
|
||||||
// Not found until end.
|
// Not found until end.
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -278,7 +284,9 @@ import java.io.IOException;
|
||||||
}
|
}
|
||||||
input.peekFully(buffer, 0, peekLength, false);
|
input.peekFully(buffer, 0, peekLength, false);
|
||||||
for (int i = 0; i < peekLength - 3; i++) {
|
for (int i = 0; i < peekLength - 3; i++) {
|
||||||
if (buffer[i] == 'O' && buffer[i + 1] == 'g' && buffer[i + 2] == 'g'
|
if (buffer[i] == 'O'
|
||||||
|
&& buffer[i + 1] == 'g'
|
||||||
|
&& buffer[i + 2] == 'g'
|
||||||
&& buffer[i + 3] == 'S') {
|
&& buffer[i + 3] == 'S') {
|
||||||
// Match! Skip to the start of the pattern.
|
// Match! Skip to the start of the pattern.
|
||||||
input.skipFully(i);
|
input.skipFully(i);
|
||||||
|
|
@ -295,13 +303,12 @@ import java.io.IOException;
|
||||||
* total number of samples per channel.
|
* total number of samples per channel.
|
||||||
*
|
*
|
||||||
* @param input The {@link ExtractorInput} to read from.
|
* @param input The {@link ExtractorInput} to read from.
|
||||||
* @return the total number of samples of this input.
|
* @return The total number of samples of this input.
|
||||||
* @throws IOException thrown if reading from the input fails.
|
* @throws IOException If reading from the input fails.
|
||||||
* @throws InterruptedException thrown if interrupted while reading from the input.
|
* @throws InterruptedException If the thread is interrupted.
|
||||||
*/
|
*/
|
||||||
//@VisibleForTesting
|
// @VisibleForTesting
|
||||||
long readGranuleOfLastPage(ExtractorInput input)
|
long readGranuleOfLastPage(ExtractorInput input) throws IOException, InterruptedException {
|
||||||
throws IOException, InterruptedException {
|
|
||||||
skipToNextPage(input);
|
skipToNextPage(input);
|
||||||
pageHeader.reset();
|
pageHeader.reset();
|
||||||
while ((pageHeader.type & 0x04) != 0x04 && input.getPosition() < endPosition) {
|
while ((pageHeader.type & 0x04) != 0x04 && input.getPosition() < endPosition) {
|
||||||
|
|
|
||||||
|
|
@ -51,11 +51,11 @@ import java.util.Arrays;
|
||||||
* can resume properly from an error while reading a continued packet spanned across multiple
|
* can resume properly from an error while reading a continued packet spanned across multiple
|
||||||
* pages.
|
* pages.
|
||||||
*
|
*
|
||||||
* @param input the {@link ExtractorInput} to read data from.
|
* @param input The {@link ExtractorInput} to read data from.
|
||||||
* @return {@code true} if the read was successful. {@code false} if the end of the input was
|
* @return {@code true} if the read was successful. The read fails if the end of the input is
|
||||||
* encountered having read no data.
|
* encountered without reading data.
|
||||||
* @throws IOException thrown if reading from the input fails.
|
* @throws IOException If reading from the input fails.
|
||||||
* @throws InterruptedException thrown if interrupted while reading from input.
|
* @throws InterruptedException If the thread is interrupted.
|
||||||
*/
|
*/
|
||||||
public boolean populate(ExtractorInput input) throws IOException, InterruptedException {
|
public boolean populate(ExtractorInput input) throws IOException, InterruptedException {
|
||||||
Assertions.checkState(input != null);
|
Assertions.checkState(input != null);
|
||||||
|
|
|
||||||
|
|
@ -71,13 +71,13 @@ import java.io.IOException;
|
||||||
/**
|
/**
|
||||||
* Peeks an Ogg page header and updates this {@link OggPageHeader}.
|
* Peeks an Ogg page header and updates this {@link OggPageHeader}.
|
||||||
*
|
*
|
||||||
* @param input the {@link ExtractorInput} to read from.
|
* @param input The {@link ExtractorInput} to read from.
|
||||||
* @param quiet if {@code true} no Exceptions are thrown but {@code false} is return if something
|
* @param quiet If {@code true}, no exceptions are thrown but {@code false} is returned if
|
||||||
* goes wrong.
|
* something goes wrong.
|
||||||
* @return {@code true} if the read was successful. {@code false} if the end of the input was
|
* @return {@code true} if the read was successful. The read fails if the end of the input is
|
||||||
* encountered having read no data.
|
* encountered without reading data.
|
||||||
* @throws IOException thrown if reading data fails or the stream is invalid.
|
* @throws IOException If reading data fails or the stream is invalid.
|
||||||
* @throws InterruptedException thrown if thread is interrupted when reading/peeking.
|
* @throws InterruptedException If the thread is interrupted.
|
||||||
*/
|
*/
|
||||||
public boolean populate(ExtractorInput input, boolean quiet)
|
public boolean populate(ExtractorInput input, boolean quiet)
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import com.google.android.exoplayer2.Format;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
@ -67,8 +66,7 @@ import java.util.List;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean readHeaders(ParsableByteArray packet, long position, SetupData setupData)
|
protected boolean readHeaders(ParsableByteArray packet, long position, SetupData setupData) {
|
||||||
throws IOException, InterruptedException {
|
|
||||||
if (!headerRead) {
|
if (!headerRead) {
|
||||||
byte[] metadata = Arrays.copyOf(packet.data, packet.limit());
|
byte[] metadata = Arrays.copyOf(packet.data, packet.limit());
|
||||||
int channelCount = metadata[9] & 0xFF;
|
int channelCount = metadata[9] & 0xFF;
|
||||||
|
|
|
||||||
|
|
@ -144,9 +144,15 @@ import java.io.IOException;
|
||||||
oggSeeker = new UnseekableOggSeeker();
|
oggSeeker = new UnseekableOggSeeker();
|
||||||
} else {
|
} else {
|
||||||
OggPageHeader firstPayloadPageHeader = oggPacket.getPageHeader();
|
OggPageHeader firstPayloadPageHeader = oggPacket.getPageHeader();
|
||||||
oggSeeker = new DefaultOggSeeker(payloadStartPosition, input.getLength(), this,
|
boolean isLastPage = (firstPayloadPageHeader.type & 0x04) != 0; // Type 4 is end of stream.
|
||||||
firstPayloadPageHeader.headerSize + firstPayloadPageHeader.bodySize,
|
oggSeeker =
|
||||||
firstPayloadPageHeader.granulePosition);
|
new DefaultOggSeeker(
|
||||||
|
payloadStartPosition,
|
||||||
|
input.getLength(),
|
||||||
|
this,
|
||||||
|
firstPayloadPageHeader.headerSize + firstPayloadPageHeader.bodySize,
|
||||||
|
firstPayloadPageHeader.granulePosition,
|
||||||
|
isLastPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
setupData = null;
|
setupData = null;
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,13 @@ public final class DefaultOggSeekerTest {
|
||||||
@Test
|
@Test
|
||||||
public void testSetupWithUnsetEndPositionFails() {
|
public void testSetupWithUnsetEndPositionFails() {
|
||||||
try {
|
try {
|
||||||
new DefaultOggSeeker(0, C.LENGTH_UNSET, new TestStreamReader(), 1, 1);
|
new DefaultOggSeeker(
|
||||||
|
/* startPosition= */ 0,
|
||||||
|
/* endPosition= */ C.LENGTH_UNSET,
|
||||||
|
/* streamReader= */ new TestStreamReader(),
|
||||||
|
/* firstPayloadPageSize= */ 1,
|
||||||
|
/* firstPayloadPageGranulePosition= */ 1,
|
||||||
|
/* firstPayloadPageIsLastPage= */ false);
|
||||||
fail();
|
fail();
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
// ignored
|
// ignored
|
||||||
|
|
@ -56,11 +62,12 @@ public final class DefaultOggSeekerTest {
|
||||||
TestStreamReader streamReader = new TestStreamReader();
|
TestStreamReader streamReader = new TestStreamReader();
|
||||||
DefaultOggSeeker oggSeeker =
|
DefaultOggSeeker oggSeeker =
|
||||||
new DefaultOggSeeker(
|
new DefaultOggSeeker(
|
||||||
0,
|
/* startPosition= */ 0,
|
||||||
testFile.data.length,
|
/* endPosition= */ testFile.data.length,
|
||||||
streamReader,
|
/* streamReader= */ streamReader,
|
||||||
testFile.firstPayloadPageSize,
|
/* firstPayloadPageSize= */ testFile.firstPayloadPageSize,
|
||||||
testFile.firstPayloadPageGranulePosition);
|
/* firstPayloadPageGranulePosition= */ testFile.firstPayloadPageGranulePosition,
|
||||||
|
/* firstPayloadPageIsLastPage= */ false);
|
||||||
OggPageHeader pageHeader = new OggPageHeader();
|
OggPageHeader pageHeader = new OggPageHeader();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
|
||||||
|
|
@ -85,8 +85,14 @@ public final class DefaultOggSeekerUtilMethodsTest {
|
||||||
|
|
||||||
private static void skipToNextPage(ExtractorInput extractorInput)
|
private static void skipToNextPage(ExtractorInput extractorInput)
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
DefaultOggSeeker oggSeeker = new DefaultOggSeeker(0, extractorInput.getLength(),
|
DefaultOggSeeker oggSeeker =
|
||||||
new FlacReader(), 1, 2);
|
new DefaultOggSeeker(
|
||||||
|
/* startPosition= */ 0,
|
||||||
|
/* endPosition= */ extractorInput.getLength(),
|
||||||
|
/* streamReader= */ new FlacReader(),
|
||||||
|
/* firstPayloadPageSize= */ 1,
|
||||||
|
/* firstPayloadPageGranulePosition= */ 2,
|
||||||
|
/* firstPayloadPageIsLastPage= */ false);
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
oggSeeker.skipToNextPage(extractorInput);
|
oggSeeker.skipToNextPage(extractorInput);
|
||||||
|
|
@ -157,7 +163,14 @@ public final class DefaultOggSeekerUtilMethodsTest {
|
||||||
|
|
||||||
private void skipToPageOfGranule(ExtractorInput input, long granule,
|
private void skipToPageOfGranule(ExtractorInput input, long granule,
|
||||||
long elapsedSamplesExpected) throws IOException, InterruptedException {
|
long elapsedSamplesExpected) throws IOException, InterruptedException {
|
||||||
DefaultOggSeeker oggSeeker = new DefaultOggSeeker(0, input.getLength(), new FlacReader(), 1, 2);
|
DefaultOggSeeker oggSeeker =
|
||||||
|
new DefaultOggSeeker(
|
||||||
|
/* startPosition= */ 0,
|
||||||
|
/* endPosition= */ input.getLength(),
|
||||||
|
/* streamReader= */ new FlacReader(),
|
||||||
|
/* firstPayloadPageSize= */ 1,
|
||||||
|
/* firstPayloadPageGranulePosition= */ 2,
|
||||||
|
/* firstPayloadPageIsLastPage= */ false);
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
assertThat(oggSeeker.skipToPageOfGranule(input, granule, -1))
|
assertThat(oggSeeker.skipToPageOfGranule(input, granule, -1))
|
||||||
|
|
@ -211,7 +224,14 @@ public final class DefaultOggSeekerUtilMethodsTest {
|
||||||
|
|
||||||
private void assertReadGranuleOfLastPage(FakeExtractorInput input, int expected)
|
private void assertReadGranuleOfLastPage(FakeExtractorInput input, int expected)
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
DefaultOggSeeker oggSeeker = new DefaultOggSeeker(0, input.getLength(), new FlacReader(), 1, 2);
|
DefaultOggSeeker oggSeeker =
|
||||||
|
new DefaultOggSeeker(
|
||||||
|
/* startPosition= */ 0,
|
||||||
|
/* endPosition= */ input.getLength(),
|
||||||
|
/* streamReader= */ new FlacReader(),
|
||||||
|
/* firstPayloadPageSize= */ 1,
|
||||||
|
/* firstPayloadPageGranulePosition= */ 2,
|
||||||
|
/* firstPayloadPageIsLastPage= */ false);
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
assertThat(oggSeeker.readGranuleOfLastPage(input)).isEqualTo(expected);
|
assertThat(oggSeeker.readGranuleOfLastPage(input)).isEqualTo(expected);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue