Commit 9e247d28 by kimvde Committed by tonihei

WavExtractor: split header reading state into 2 states

This refactoring is the basis to support RF64 (see
Issue: google/ExoPlayer#9543).

#minor-release

PiperOrigin-RevId: 407301056
parent ac664870
...@@ -15,8 +15,8 @@ ...@@ -15,8 +15,8 @@
*/ */
package com.google.android.exoplayer2.extractor.wav; package com.google.android.exoplayer2.extractor.wav;
/** Header for a WAV file. */ /** Format information for a WAV file. */
/* package */ final class WavHeader { /* package */ final class WavFormat {
/** /**
* The format type. Standard format types are the "WAVE form Registration Number" constants * The format type. Standard format types are the "WAVE form Registration Number" constants
...@@ -33,10 +33,10 @@ package com.google.android.exoplayer2.extractor.wav; ...@@ -33,10 +33,10 @@ package com.google.android.exoplayer2.extractor.wav;
public final int blockSize; public final int blockSize;
/** Bits per sample for a single channel. */ /** Bits per sample for a single channel. */
public final int bitsPerSample; public final int bitsPerSample;
/** Extra data appended to the format chunk of the header. */ /** Extra data appended to the format chunk. */
public final byte[] extraData; public final byte[] extraData;
public WavHeader( public WavFormat(
int formatType, int formatType,
int numChannels, int numChannels,
int frameRateHz, int frameRateHz,
......
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
package com.google.android.exoplayer2.extractor.wav; package com.google.android.exoplayer2.extractor.wav;
import android.util.Pair; import android.util.Pair;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.audio.WavUtil; import com.google.android.exoplayer2.audio.WavUtil;
...@@ -27,45 +26,56 @@ import com.google.android.exoplayer2.util.ParsableByteArray; ...@@ -27,45 +26,56 @@ 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.io.IOException;
/** Reads a {@code WavHeader} from an input stream; supports resuming from input failures. */ /** Reads a WAV header from an input stream; supports resuming from input failures. */
/* package */ final class WavHeaderReader { /* package */ final class WavHeaderReader {
private static final String TAG = "WavHeaderReader"; private static final String TAG = "WavHeaderReader";
/** /**
* Peeks and returns a {@code WavHeader}. * Returns whether the given {@code input} starts with a RIFF chunk header, followed by a WAVE
* tag.
* *
* @param input Input stream to peek the WAV header from. * @param input The input stream to peek from. The position should point to the start of the
* @throws ParserException If the input file is an incorrect RIFF WAV. * stream.
* @return Whether the given {@code input} starts with a RIFF chunk header, followed by a WAVE
* tag.
* @throws IOException If peeking from the input fails. * @throws IOException If peeking from the input fails.
* @return A new {@code WavHeader} peeked from {@code input}, or null if the input is not a
* supported WAV format.
*/ */
@Nullable public static boolean checkFileType(ExtractorInput input) throws IOException {
public static WavHeader peek(ExtractorInput input) throws IOException { ParsableByteArray scratch = new ParsableByteArray(ChunkHeader.SIZE_IN_BYTES);
Assertions.checkNotNull(input);
// Allocate a scratch buffer large enough to store the format chunk.
ParsableByteArray scratch = new ParsableByteArray(16);
// Attempt to read the RIFF chunk. // Attempt to read the RIFF chunk.
ChunkHeader chunkHeader = ChunkHeader.peek(input, scratch); ChunkHeader chunkHeader = ChunkHeader.peek(input, scratch);
if (chunkHeader.id != WavUtil.RIFF_FOURCC) { if (chunkHeader.id != WavUtil.RIFF_FOURCC) {
return null; return false;
} }
input.peekFully(scratch.getData(), 0, 4); input.peekFully(scratch.getData(), 0, 4);
scratch.setPosition(0); scratch.setPosition(0);
int riffFormat = scratch.readInt(); int formType = scratch.readInt();
if (riffFormat != WavUtil.WAVE_FOURCC) { if (formType != WavUtil.WAVE_FOURCC) {
Log.e(TAG, "Unsupported RIFF format: " + riffFormat); Log.e(TAG, "Unsupported form type: " + formType);
return null; return false;
} }
return true;
}
/**
* Reads and returns a {@code WavFormat}.
*
* @param input Input stream to read the WAV format from. The position should point to the byte
* following the WAVE tag.
* @throws IOException If reading from the input fails.
* @return A new {@code WavFormat} read from {@code input}.
*/
public static WavFormat readFormat(ExtractorInput input) throws IOException {
// Allocate a scratch buffer large enough to store the format chunk.
ParsableByteArray scratch = new ParsableByteArray(16);
// Skip chunks until we find the format chunk. // Skip chunks until we find the format chunk.
chunkHeader = ChunkHeader.peek(input, scratch); ChunkHeader chunkHeader = ChunkHeader.peek(input, scratch);
while (chunkHeader.id != WavUtil.FMT_FOURCC) { while (chunkHeader.id != WavUtil.FMT_FOURCC) {
input.advancePeekPosition((int) chunkHeader.size); input.skipFully(ChunkHeader.SIZE_IN_BYTES + (int) chunkHeader.size);
chunkHeader = ChunkHeader.peek(input, scratch); chunkHeader = ChunkHeader.peek(input, scratch);
} }
...@@ -88,7 +98,8 @@ import java.io.IOException; ...@@ -88,7 +98,8 @@ import java.io.IOException;
extraData = Util.EMPTY_BYTE_ARRAY; extraData = Util.EMPTY_BYTE_ARRAY;
} }
return new WavHeader( input.skipFully((int) (input.getPeekPosition() - input.getPosition()));
return new WavFormat(
audioFormatType, audioFormatType,
numChannels, numChannels,
frameRateHz, frameRateHz,
...@@ -109,8 +120,6 @@ import java.io.IOException; ...@@ -109,8 +120,6 @@ import java.io.IOException;
* @throws IOException If reading from the input fails. * @throws IOException If reading from the input fails.
*/ */
public static Pair<Long, Long> skipToSampleData(ExtractorInput input) throws IOException { public static Pair<Long, Long> skipToSampleData(ExtractorInput input) throws IOException {
Assertions.checkNotNull(input);
// Make sure the peek position is set to the read position before we peek the first header. // Make sure the peek position is set to the read position before we peek the first header.
input.resetPeekPosition(); input.resetPeekPosition();
......
...@@ -22,18 +22,18 @@ import com.google.android.exoplayer2.util.Util; ...@@ -22,18 +22,18 @@ import com.google.android.exoplayer2.util.Util;
/* package */ final class WavSeekMap implements SeekMap { /* package */ final class WavSeekMap implements SeekMap {
private final WavHeader wavHeader; private final WavFormat wavFormat;
private final int framesPerBlock; private final int framesPerBlock;
private final long firstBlockPosition; private final long firstBlockPosition;
private final long blockCount; private final long blockCount;
private final long durationUs; private final long durationUs;
public WavSeekMap( public WavSeekMap(
WavHeader wavHeader, int framesPerBlock, long dataStartPosition, long dataEndPosition) { WavFormat wavFormat, int framesPerBlock, long dataStartPosition, long dataEndPosition) {
this.wavHeader = wavHeader; this.wavFormat = wavFormat;
this.framesPerBlock = framesPerBlock; this.framesPerBlock = framesPerBlock;
this.firstBlockPosition = dataStartPosition; this.firstBlockPosition = dataStartPosition;
this.blockCount = (dataEndPosition - dataStartPosition) / wavHeader.blockSize; this.blockCount = (dataEndPosition - dataStartPosition) / wavFormat.blockSize;
durationUs = blockIndexToTimeUs(blockCount); durationUs = blockIndexToTimeUs(blockCount);
} }
...@@ -50,17 +50,17 @@ import com.google.android.exoplayer2.util.Util; ...@@ -50,17 +50,17 @@ import com.google.android.exoplayer2.util.Util;
@Override @Override
public SeekPoints getSeekPoints(long timeUs) { public SeekPoints getSeekPoints(long timeUs) {
// Calculate the containing block index, constraining to valid indices. // Calculate the containing block index, constraining to valid indices.
long blockIndex = (timeUs * wavHeader.frameRateHz) / (C.MICROS_PER_SECOND * framesPerBlock); long blockIndex = (timeUs * wavFormat.frameRateHz) / (C.MICROS_PER_SECOND * framesPerBlock);
blockIndex = Util.constrainValue(blockIndex, 0, blockCount - 1); blockIndex = Util.constrainValue(blockIndex, 0, blockCount - 1);
long seekPosition = firstBlockPosition + (blockIndex * wavHeader.blockSize); long seekPosition = firstBlockPosition + (blockIndex * wavFormat.blockSize);
long seekTimeUs = blockIndexToTimeUs(blockIndex); long seekTimeUs = blockIndexToTimeUs(blockIndex);
SeekPoint seekPoint = new SeekPoint(seekTimeUs, seekPosition); SeekPoint seekPoint = new SeekPoint(seekTimeUs, seekPosition);
if (seekTimeUs >= timeUs || blockIndex == blockCount - 1) { if (seekTimeUs >= timeUs || blockIndex == blockCount - 1) {
return new SeekPoints(seekPoint); return new SeekPoints(seekPoint);
} else { } else {
long secondBlockIndex = blockIndex + 1; long secondBlockIndex = blockIndex + 1;
long secondSeekPosition = firstBlockPosition + (secondBlockIndex * wavHeader.blockSize); long secondSeekPosition = firstBlockPosition + (secondBlockIndex * wavFormat.blockSize);
long secondSeekTimeUs = blockIndexToTimeUs(secondBlockIndex); long secondSeekTimeUs = blockIndexToTimeUs(secondBlockIndex);
SeekPoint secondSeekPoint = new SeekPoint(secondSeekTimeUs, secondSeekPosition); SeekPoint secondSeekPoint = new SeekPoint(secondSeekTimeUs, secondSeekPosition);
return new SeekPoints(seekPoint, secondSeekPoint); return new SeekPoints(seekPoint, secondSeekPoint);
...@@ -69,6 +69,6 @@ import com.google.android.exoplayer2.util.Util; ...@@ -69,6 +69,6 @@ import com.google.android.exoplayer2.util.Util;
private long blockIndexToTimeUs(long blockIndex) { private long blockIndexToTimeUs(long blockIndex) {
return Util.scaleLargeTimestamp( return Util.scaleLargeTimestamp(
blockIndex * framesPerBlock, C.MICROS_PER_SECOND, wavHeader.frameRateHz); blockIndex * framesPerBlock, C.MICROS_PER_SECOND, wavFormat.frameRateHz);
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment