From 7168f93829b55ab55c46ec678f38dffef4039b96 Mon Sep 17 00:00:00 2001 From: kimvde Date: Wed, 3 Nov 2021 11:01:07 +0000 Subject: [PATCH] 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 --- .../media3/extractor/wav/WavExtractor.java | 135 ++++++++++-------- .../wav/{WavHeader.java => WavFormat.java} | 8 +- .../media3/extractor/wav/WavHeaderReader.java | 57 ++++---- .../media3/extractor/wav/WavSeekMap.java | 16 +-- 4 files changed, 121 insertions(+), 95 deletions(-) rename libraries/extractor/src/main/java/androidx/media3/extractor/wav/{WavHeader.java => WavFormat.java} (91%) diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavExtractor.java index 903834dc8d..24362afefc 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavExtractor.java @@ -63,12 +63,18 @@ public final class WavExtractor implements Extractor { @Documented @Retention(RetentionPolicy.SOURCE) @Target({ElementType.TYPE_USE}) - @IntDef({STATE_READING_HEADER, STATE_SKIPPING_TO_SAMPLE_DATA, STATE_READING_SAMPLE_DATA}) + @IntDef({ + STATE_READING_FILE_TYPE, + STATE_READING_FORMAT, + STATE_SKIPPING_TO_SAMPLE_DATA, + STATE_READING_SAMPLE_DATA + }) private @interface State {} - private static final int STATE_READING_HEADER = 0; - private static final int STATE_SKIPPING_TO_SAMPLE_DATA = 1; - private static final int STATE_READING_SAMPLE_DATA = 2; + private static final int STATE_READING_FILE_TYPE = 0; + private static final int STATE_READING_FORMAT = 1; + private static final int STATE_SKIPPING_TO_SAMPLE_DATA = 2; + private static final int STATE_READING_SAMPLE_DATA = 3; private @MonotonicNonNull ExtractorOutput extractorOutput; private @MonotonicNonNull TrackOutput trackOutput; @@ -78,14 +84,14 @@ public final class WavExtractor implements Extractor { private long dataEndPosition; public WavExtractor() { - state = STATE_READING_HEADER; + state = STATE_READING_FILE_TYPE; dataStartPosition = C.POSITION_UNSET; dataEndPosition = C.POSITION_UNSET; } @Override public boolean sniff(ExtractorInput input) throws IOException { - return WavHeaderReader.peek(input) != null; + return WavHeaderReader.checkFileType(input); } @Override @@ -97,7 +103,7 @@ public final class WavExtractor implements Extractor { @Override public void seek(long position, long timeUs) { - state = position == 0 ? STATE_READING_HEADER : STATE_READING_SAMPLE_DATA; + state = position == 0 ? STATE_READING_FILE_TYPE : STATE_READING_SAMPLE_DATA; if (outputWriter != null) { outputWriter.reset(timeUs); } @@ -113,8 +119,11 @@ public final class WavExtractor implements Extractor { public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException { assertInitialized(); switch (state) { - case STATE_READING_HEADER: - readHeader(input); + case STATE_READING_FILE_TYPE: + readFileType(input); + return Extractor.RESULT_CONTINUE; + case STATE_READING_FORMAT: + readFormat(input); return Extractor.RESULT_CONTINUE; case STATE_SKIPPING_TO_SAMPLE_DATA: skipToSampleData(input); @@ -132,50 +141,54 @@ public final class WavExtractor implements Extractor { Util.castNonNull(extractorOutput); } - @RequiresNonNull({"extractorOutput", "trackOutput"}) - private void readHeader(ExtractorInput input) throws IOException { + private void readFileType(ExtractorInput input) throws IOException { Assertions.checkState(input.getPosition() == 0); if (dataStartPosition != C.POSITION_UNSET) { input.skipFully(dataStartPosition); state = STATE_READING_SAMPLE_DATA; return; } - WavHeader header = WavHeaderReader.peek(input); - if (header == null) { + if (!WavHeaderReader.checkFileType(input)) { // Should only happen if the media wasn't sniffed. throw ParserException.createForMalformedContainer( - "Unsupported or unrecognized wav header.", /* cause= */ null); + "Unsupported or unrecognized wav file type.", /* cause= */ null); } input.skipFully((int) (input.getPeekPosition() - input.getPosition())); + state = STATE_READING_FORMAT; + } - if (header.formatType == WavUtil.TYPE_IMA_ADPCM) { - outputWriter = new ImaAdPcmOutputWriter(extractorOutput, trackOutput, header); - } else if (header.formatType == WavUtil.TYPE_ALAW) { + @RequiresNonNull({"extractorOutput", "trackOutput"}) + private void readFormat(ExtractorInput input) throws IOException { + WavFormat wavFormat = WavHeaderReader.readFormat(input); + if (wavFormat.formatType == WavUtil.TYPE_IMA_ADPCM) { + outputWriter = new ImaAdPcmOutputWriter(extractorOutput, trackOutput, wavFormat); + } else if (wavFormat.formatType == WavUtil.TYPE_ALAW) { outputWriter = new PassthroughOutputWriter( extractorOutput, trackOutput, - header, + wavFormat, MimeTypes.AUDIO_ALAW, /* pcmEncoding= */ Format.NO_VALUE); - } else if (header.formatType == WavUtil.TYPE_MLAW) { + } else if (wavFormat.formatType == WavUtil.TYPE_MLAW) { outputWriter = new PassthroughOutputWriter( extractorOutput, trackOutput, - header, + wavFormat, MimeTypes.AUDIO_MLAW, /* pcmEncoding= */ Format.NO_VALUE); } else { @C.PcmEncoding - int pcmEncoding = WavUtil.getPcmEncodingForType(header.formatType, header.bitsPerSample); + int pcmEncoding = + WavUtil.getPcmEncodingForType(wavFormat.formatType, wavFormat.bitsPerSample); if (pcmEncoding == C.ENCODING_INVALID) { throw ParserException.createForUnsupportedContainerFeature( - "Unsupported WAV format type: " + header.formatType); + "Unsupported WAV format type: " + wavFormat.formatType); } outputWriter = new PassthroughOutputWriter( - extractorOutput, trackOutput, header, MimeTypes.AUDIO_RAW, pcmEncoding); + extractorOutput, trackOutput, wavFormat, MimeTypes.AUDIO_RAW, pcmEncoding); } state = STATE_SKIPPING_TO_SAMPLE_DATA; } @@ -236,7 +249,7 @@ public final class WavExtractor implements Extractor { private final ExtractorOutput extractorOutput; private final TrackOutput trackOutput; - private final WavHeader header; + private final WavFormat wavFormat; private final Format format; /** The target size of each output sample, in bytes. */ private final int targetSampleSizeBytes; @@ -258,33 +271,33 @@ public final class WavExtractor implements Extractor { public PassthroughOutputWriter( ExtractorOutput extractorOutput, TrackOutput trackOutput, - WavHeader header, + WavFormat wavFormat, String mimeType, @C.PcmEncoding int pcmEncoding) throws ParserException { this.extractorOutput = extractorOutput; this.trackOutput = trackOutput; - this.header = header; + this.wavFormat = wavFormat; - int bytesPerFrame = header.numChannels * header.bitsPerSample / 8; - // Validate the header. Blocks are expected to correspond to single frames. - if (header.blockSize != bytesPerFrame) { + int bytesPerFrame = wavFormat.numChannels * wavFormat.bitsPerSample / 8; + // Validate the WAV format. Blocks are expected to correspond to single frames. + if (wavFormat.blockSize != bytesPerFrame) { throw ParserException.createForMalformedContainer( - "Expected block size: " + bytesPerFrame + "; got: " + header.blockSize, + "Expected block size: " + bytesPerFrame + "; got: " + wavFormat.blockSize, /* cause= */ null); } - int constantBitrate = header.frameRateHz * bytesPerFrame * 8; + int constantBitrate = wavFormat.frameRateHz * bytesPerFrame * 8; targetSampleSizeBytes = - max(bytesPerFrame, header.frameRateHz * bytesPerFrame / TARGET_SAMPLES_PER_SECOND); + max(bytesPerFrame, wavFormat.frameRateHz * bytesPerFrame / TARGET_SAMPLES_PER_SECOND); format = new Format.Builder() .setSampleMimeType(mimeType) .setAverageBitrate(constantBitrate) .setPeakBitrate(constantBitrate) .setMaxInputSize(targetSampleSizeBytes) - .setChannelCount(header.numChannels) - .setSampleRate(header.frameRateHz) + .setChannelCount(wavFormat.numChannels) + .setSampleRate(wavFormat.frameRateHz) .setPcmEncoding(pcmEncoding) .build(); } @@ -299,7 +312,7 @@ public final class WavExtractor implements Extractor { @Override public void init(int dataStartPosition, long dataEndPosition) { extractorOutput.seekMap( - new WavSeekMap(header, /* framesPerBlock= */ 1, dataStartPosition, dataEndPosition)); + new WavSeekMap(wavFormat, /* framesPerBlock= */ 1, dataStartPosition, dataEndPosition)); trackOutput.format(format); } @@ -320,13 +333,13 @@ public final class WavExtractor implements Extractor { // Write the corresponding sample metadata. Samples must be a whole number of frames. It's // possible that the number of pending output bytes is not a whole number of frames if the // stream ended unexpectedly. - int bytesPerFrame = header.blockSize; + int bytesPerFrame = wavFormat.blockSize; int pendingFrames = pendingOutputBytes / bytesPerFrame; if (pendingFrames > 0) { long timeUs = startTimeUs + Util.scaleLargeTimestamp( - outputFrameCount, C.MICROS_PER_SECOND, header.frameRateHz); + outputFrameCount, C.MICROS_PER_SECOND, wavFormat.frameRateHz); int size = pendingFrames * bytesPerFrame; int offset = pendingOutputBytes - size; trackOutput.sampleMetadata( @@ -356,7 +369,7 @@ public final class WavExtractor implements Extractor { private final ExtractorOutput extractorOutput; private final TrackOutput trackOutput; - private final WavHeader header; + private final WavFormat wavFormat; /** Number of frames per block of the input (yet to be decoded) data. */ private final int framesPerBlock; @@ -386,23 +399,26 @@ public final class WavExtractor implements Extractor { private long outputFrameCount; public ImaAdPcmOutputWriter( - ExtractorOutput extractorOutput, TrackOutput trackOutput, WavHeader header) + ExtractorOutput extractorOutput, TrackOutput trackOutput, WavFormat wavFormat) throws ParserException { this.extractorOutput = extractorOutput; this.trackOutput = trackOutput; - this.header = header; - targetSampleSizeFrames = max(1, header.frameRateHz / TARGET_SAMPLES_PER_SECOND); + this.wavFormat = wavFormat; + targetSampleSizeFrames = max(1, wavFormat.frameRateHz / TARGET_SAMPLES_PER_SECOND); - ParsableByteArray scratch = new ParsableByteArray(header.extraData); + ParsableByteArray scratch = new ParsableByteArray(wavFormat.extraData); scratch.readLittleEndianUnsignedShort(); framesPerBlock = scratch.readLittleEndianUnsignedShort(); - int numChannels = header.numChannels; - // Validate the header. This calculation is defined in "Microsoft Multimedia Standards Update + int numChannels = wavFormat.numChannels; + // Validate the WAV format. This calculation is defined in "Microsoft Multimedia Standards + // Update // - New Multimedia Types and Data Techniques" (1994). See the "IMA ADPCM Wave Type" and "DVI // ADPCM Wave Type" sections, and the calculation of wSamplesPerBlock in the latter. int expectedFramesPerBlock = - (((header.blockSize - (4 * numChannels)) * 8) / (header.bitsPerSample * numChannels)) + 1; + (((wavFormat.blockSize - (4 * numChannels)) * 8) + / (wavFormat.bitsPerSample * numChannels)) + + 1; if (framesPerBlock != expectedFramesPerBlock) { throw ParserException.createForMalformedContainer( "Expected frames per block: " + expectedFramesPerBlock + "; got: " + framesPerBlock, @@ -412,22 +428,22 @@ public final class WavExtractor implements Extractor { // Calculate the number of blocks we'll need to decode to obtain an output sample of the // target sample size, and allocate suitably sized buffers for input and decoded data. int maxBlocksToDecode = Util.ceilDivide(targetSampleSizeFrames, framesPerBlock); - inputData = new byte[maxBlocksToDecode * header.blockSize]; + inputData = new byte[maxBlocksToDecode * wavFormat.blockSize]; decodedData = new ParsableByteArray( maxBlocksToDecode * numOutputFramesToBytes(framesPerBlock, numChannels)); // Create the format. We calculate the bitrate of the data before decoding, since this is the // bitrate of the stream itself. - int constantBitrate = header.frameRateHz * header.blockSize * 8 / framesPerBlock; + int constantBitrate = wavFormat.frameRateHz * wavFormat.blockSize * 8 / framesPerBlock; format = new Format.Builder() .setSampleMimeType(MimeTypes.AUDIO_RAW) .setAverageBitrate(constantBitrate) .setPeakBitrate(constantBitrate) .setMaxInputSize(numOutputFramesToBytes(targetSampleSizeFrames, numChannels)) - .setChannelCount(header.numChannels) - .setSampleRate(header.frameRateHz) + .setChannelCount(wavFormat.numChannels) + .setSampleRate(wavFormat.frameRateHz) .setPcmEncoding(C.ENCODING_PCM_16BIT) .build(); } @@ -443,7 +459,7 @@ public final class WavExtractor implements Extractor { @Override public void init(int dataStartPosition, long dataEndPosition) { extractorOutput.seekMap( - new WavSeekMap(header, framesPerBlock, dataStartPosition, dataEndPosition)); + new WavSeekMap(wavFormat, framesPerBlock, dataStartPosition, dataEndPosition)); trackOutput.format(format); } @@ -455,7 +471,7 @@ public final class WavExtractor implements Extractor { targetSampleSizeFrames - numOutputBytesToFrames(pendingOutputBytes); // Calculate the whole number of blocks that we need to decode to obtain this many frames. int blocksToDecode = Util.ceilDivide(targetFramesRemaining, framesPerBlock); - int targetReadBytes = blocksToDecode * header.blockSize; + int targetReadBytes = blocksToDecode * wavFormat.blockSize; // Read input data until we've reached the target number of blocks, or the end of the data. boolean endOfSampleData = bytesLeft == 0; @@ -469,11 +485,11 @@ public final class WavExtractor implements Extractor { } } - int pendingBlockCount = pendingInputBytes / header.blockSize; + int pendingBlockCount = pendingInputBytes / wavFormat.blockSize; if (pendingBlockCount > 0) { // We have at least one whole block to decode. decode(inputData, pendingBlockCount, decodedData); - pendingInputBytes -= pendingBlockCount * header.blockSize; + pendingInputBytes -= pendingBlockCount * wavFormat.blockSize; // Write all of the decoded data to the track output. int decodedDataSize = decodedData.limit(); @@ -501,7 +517,8 @@ public final class WavExtractor implements Extractor { private void writeSampleMetadata(int sampleFrames) { long timeUs = startTimeUs - + Util.scaleLargeTimestamp(outputFrameCount, C.MICROS_PER_SECOND, header.frameRateHz); + + Util.scaleLargeTimestamp( + outputFrameCount, C.MICROS_PER_SECOND, wavFormat.frameRateHz); int size = numOutputFramesToBytes(sampleFrames); int offset = pendingOutputBytes - size; trackOutput.sampleMetadata( @@ -519,7 +536,7 @@ public final class WavExtractor implements Extractor { */ private void decode(byte[] input, int blockCount, ParsableByteArray output) { for (int blockIndex = 0; blockIndex < blockCount; blockIndex++) { - for (int channelIndex = 0; channelIndex < header.numChannels; channelIndex++) { + for (int channelIndex = 0; channelIndex < wavFormat.numChannels; channelIndex++) { decodeBlockForChannel(input, blockIndex, channelIndex, output.getData()); } } @@ -530,8 +547,8 @@ public final class WavExtractor implements Extractor { private void decodeBlockForChannel( byte[] input, int blockIndex, int channelIndex, byte[] output) { - int blockSize = header.blockSize; - int numChannels = header.numChannels; + int blockSize = wavFormat.blockSize; + int numChannels = wavFormat.numChannels; // The input data consists for a four byte header [Ci] for each of the N channels, followed // by interleaved data segments [Ci-DATAj], each of which are four bytes long. @@ -592,11 +609,11 @@ public final class WavExtractor implements Extractor { } private int numOutputBytesToFrames(int bytes) { - return bytes / (2 * header.numChannels); + return bytes / (2 * wavFormat.numChannels); } private int numOutputFramesToBytes(int frames) { - return numOutputFramesToBytes(frames, header.numChannels); + return numOutputFramesToBytes(frames, wavFormat.numChannels); } private static int numOutputFramesToBytes(int frames, int numChannels) { diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavHeader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavFormat.java similarity index 91% rename from libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavHeader.java rename to libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavFormat.java index d7157e56fa..714e4eb44e 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavHeader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavFormat.java @@ -15,8 +15,8 @@ */ package androidx.media3.extractor.wav; -/** Header for a WAV file. */ -/* package */ final class WavHeader { +/** Format information for a WAV file. */ +/* package */ final class WavFormat { /** * The format type. Standard format types are the "WAVE form Registration Number" constants @@ -33,10 +33,10 @@ package androidx.media3.extractor.wav; public final int blockSize; /** Bits per sample for a single channel. */ 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 WavHeader( + public WavFormat( int formatType, int numChannels, int frameRateHz, diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavHeaderReader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavHeaderReader.java index a36e49cf62..214404d2d7 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavHeaderReader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavHeaderReader.java @@ -16,7 +16,6 @@ package androidx.media3.extractor.wav; import android.util.Pair; -import androidx.annotation.Nullable; import androidx.media3.common.C; import androidx.media3.common.ParserException; import androidx.media3.common.util.Assertions; @@ -27,45 +26,56 @@ import androidx.media3.extractor.ExtractorInput; import androidx.media3.extractor.WavUtil; 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 { 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. - * @throws ParserException If the input file is an incorrect RIFF WAV. + * @param input The input stream to peek from. The position should point to the start of the + * 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. - * @return A new {@code WavHeader} peeked from {@code input}, or null if the input is not a - * supported WAV format. */ - @Nullable - public static WavHeader peek(ExtractorInput input) throws IOException { - Assertions.checkNotNull(input); - - // Allocate a scratch buffer large enough to store the format chunk. - ParsableByteArray scratch = new ParsableByteArray(16); - + public static boolean checkFileType(ExtractorInput input) throws IOException { + ParsableByteArray scratch = new ParsableByteArray(ChunkHeader.SIZE_IN_BYTES); // Attempt to read the RIFF chunk. ChunkHeader chunkHeader = ChunkHeader.peek(input, scratch); if (chunkHeader.id != WavUtil.RIFF_FOURCC) { - return null; + return false; } input.peekFully(scratch.getData(), 0, 4); scratch.setPosition(0); - int riffFormat = scratch.readInt(); - if (riffFormat != WavUtil.WAVE_FOURCC) { - Log.e(TAG, "Unsupported RIFF format: " + riffFormat); - return null; + int formType = scratch.readInt(); + if (formType != WavUtil.WAVE_FOURCC) { + Log.e(TAG, "Unsupported form type: " + formType); + 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. - chunkHeader = ChunkHeader.peek(input, scratch); + ChunkHeader chunkHeader = ChunkHeader.peek(input, scratch); 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); } @@ -88,7 +98,8 @@ import java.io.IOException; extraData = Util.EMPTY_BYTE_ARRAY; } - return new WavHeader( + input.skipFully((int) (input.getPeekPosition() - input.getPosition())); + return new WavFormat( audioFormatType, numChannels, frameRateHz, @@ -109,8 +120,6 @@ import java.io.IOException; * @throws IOException If reading from the input fails. */ public static Pair 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. input.resetPeekPosition(); diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavSeekMap.java b/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavSeekMap.java index 995eb52bb2..9dc9938fb2 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavSeekMap.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavSeekMap.java @@ -22,18 +22,18 @@ import androidx.media3.extractor.SeekPoint; /* package */ final class WavSeekMap implements SeekMap { - private final WavHeader wavHeader; + private final WavFormat wavFormat; private final int framesPerBlock; private final long firstBlockPosition; private final long blockCount; private final long durationUs; public WavSeekMap( - WavHeader wavHeader, int framesPerBlock, long dataStartPosition, long dataEndPosition) { - this.wavHeader = wavHeader; + WavFormat wavFormat, int framesPerBlock, long dataStartPosition, long dataEndPosition) { + this.wavFormat = wavFormat; this.framesPerBlock = framesPerBlock; this.firstBlockPosition = dataStartPosition; - this.blockCount = (dataEndPosition - dataStartPosition) / wavHeader.blockSize; + this.blockCount = (dataEndPosition - dataStartPosition) / wavFormat.blockSize; durationUs = blockIndexToTimeUs(blockCount); } @@ -50,17 +50,17 @@ import androidx.media3.extractor.SeekPoint; @Override public SeekPoints getSeekPoints(long timeUs) { // 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); - long seekPosition = firstBlockPosition + (blockIndex * wavHeader.blockSize); + long seekPosition = firstBlockPosition + (blockIndex * wavFormat.blockSize); long seekTimeUs = blockIndexToTimeUs(blockIndex); SeekPoint seekPoint = new SeekPoint(seekTimeUs, seekPosition); if (seekTimeUs >= timeUs || blockIndex == blockCount - 1) { return new SeekPoints(seekPoint); } else { long secondBlockIndex = blockIndex + 1; - long secondSeekPosition = firstBlockPosition + (secondBlockIndex * wavHeader.blockSize); + long secondSeekPosition = firstBlockPosition + (secondBlockIndex * wavFormat.blockSize); long secondSeekTimeUs = blockIndexToTimeUs(secondBlockIndex); SeekPoint secondSeekPoint = new SeekPoint(secondSeekTimeUs, secondSeekPosition); return new SeekPoints(seekPoint, secondSeekPoint); @@ -69,6 +69,6 @@ import androidx.media3.extractor.SeekPoint; private long blockIndexToTimeUs(long blockIndex) { return Util.scaleLargeTimestamp( - blockIndex * framesPerBlock, C.MICROS_PER_SECOND, wavHeader.frameRateHz); + blockIndex * framesPerBlock, C.MICROS_PER_SECOND, wavFormat.frameRateHz); } }