Fix wav format pcm encoding detection error
(cherry picked from commit 8a9aec9c40df6eccf8dce88ccb7089147ae9ffef)
This commit is contained in:
parent
7e023f915b
commit
921b7a58c8
@ -25,6 +25,7 @@ import java.nio.ByteBuffer;
|
|||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps a byte array, providing a set of methods for parsing data from it. Numerical values are
|
* Wraps a byte array, providing a set of methods for parsing data from it. Numerical values are
|
||||||
@ -661,6 +662,26 @@ public final class ParsableByteArray {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads microsoft GUID as java UUID
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if there is not enough data decoding
|
||||||
|
* @return Decoded UUID
|
||||||
|
* */
|
||||||
|
public UUID readMSGUID() {
|
||||||
|
if (bytesLeft() < 16) {
|
||||||
|
throw new IllegalStateException("Not enough data");
|
||||||
|
}
|
||||||
|
// flip little ending to big ending
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/guiddef/ns-guiddef-guid
|
||||||
|
long d1 = readLittleEndianUnsignedInt();
|
||||||
|
long d2 = readLittleEndianUnsignedShort();
|
||||||
|
long d3 = readLittleEndianUnsignedShort();
|
||||||
|
long mostSigBits = d1 << 32 | d2 << 16 | d3;
|
||||||
|
long leastSigBits = readLong();
|
||||||
|
return new UUID(mostSigBits, leastSigBits);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the index of the next occurrence of '\n' or '\r', or {@link #limit} if none is found.
|
* Returns the index of the next occurrence of '\n' or '\r', or {@link #limit} if none is found.
|
||||||
*/
|
*/
|
||||||
|
@ -20,6 +20,8 @@ import androidx.media3.common.Format;
|
|||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/** Utilities for handling WAVE files. */
|
/** Utilities for handling WAVE files. */
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class WavUtil {
|
public final class WavUtil {
|
||||||
@ -60,6 +62,14 @@ public final class WavUtil {
|
|||||||
/** WAVE type value for extended WAVE format. */
|
/** WAVE type value for extended WAVE format. */
|
||||||
public static final int TYPE_WAVE_FORMAT_EXTENSIBLE = 0xFFFE;
|
public static final int TYPE_WAVE_FORMAT_EXTENSIBLE = 0xFFFE;
|
||||||
|
|
||||||
|
public static final UUID KSDATAFORMAT_SUBTYPE_PCM = UUID.fromString("00000001-0000-0010-8000-00aa00389b71");
|
||||||
|
|
||||||
|
public static final UUID KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = UUID.fromString("00000003-0000-0010-8000-00aa00389b71");
|
||||||
|
|
||||||
|
public static final UUID KSDATAFORMAT_SUBTYPE_ALAW = UUID.fromString("00000006-0000-0010-8000-00aa00389b71");
|
||||||
|
|
||||||
|
public static final UUID KSDATAFORMAT_SUBTYPE_MULAW = UUID.fromString("00000007-0000-0010-8000-00aa00389b71");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the WAVE format type value for the given {@link C.PcmEncoding}.
|
* Returns the WAVE format type value for the given {@link C.PcmEncoding}.
|
||||||
*
|
*
|
||||||
|
@ -175,9 +175,19 @@ public final class WavExtractor implements Extractor {
|
|||||||
@RequiresNonNull({"extractorOutput", "trackOutput"})
|
@RequiresNonNull({"extractorOutput", "trackOutput"})
|
||||||
private void readFormat(ExtractorInput input) throws IOException {
|
private void readFormat(ExtractorInput input) throws IOException {
|
||||||
WavFormat wavFormat = WavHeaderReader.readFormat(input);
|
WavFormat wavFormat = WavHeaderReader.readFormat(input);
|
||||||
|
// WAVE_FORMAT_EXTENSIBLE Determined by SubFormat
|
||||||
|
// available SubFormats (GUID , Decoder)
|
||||||
|
// ("00000001-0000-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_PCM)
|
||||||
|
// ("00000003-0000-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
|
||||||
|
// ("00000006-0000-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_ALAW)
|
||||||
|
// ("00000007-0000-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_MULAW)
|
||||||
|
// full subformat GUIDs for compressed audio formats
|
||||||
|
// https://learn.microsoft.com/en-us/windows-hardware/drivers/audio/subformat-guids-for-compressed-audio-formats
|
||||||
if (wavFormat.formatType == WavUtil.TYPE_IMA_ADPCM) {
|
if (wavFormat.formatType == WavUtil.TYPE_IMA_ADPCM) {
|
||||||
outputWriter = new ImaAdPcmOutputWriter(extractorOutput, trackOutput, wavFormat);
|
outputWriter = new ImaAdPcmOutputWriter(extractorOutput, trackOutput, wavFormat);
|
||||||
} else if (wavFormat.formatType == WavUtil.TYPE_ALAW) {
|
} else if (wavFormat.formatType == WavUtil.TYPE_ALAW ||
|
||||||
|
wavFormat.formatType == WavUtil.TYPE_WAVE_FORMAT_EXTENSIBLE &&
|
||||||
|
WavUtil.KSDATAFORMAT_SUBTYPE_ALAW.equals(wavFormat.uuid)) {
|
||||||
outputWriter =
|
outputWriter =
|
||||||
new PassthroughOutputWriter(
|
new PassthroughOutputWriter(
|
||||||
extractorOutput,
|
extractorOutput,
|
||||||
@ -185,7 +195,9 @@ public final class WavExtractor implements Extractor {
|
|||||||
wavFormat,
|
wavFormat,
|
||||||
MimeTypes.AUDIO_ALAW,
|
MimeTypes.AUDIO_ALAW,
|
||||||
/* pcmEncoding= */ Format.NO_VALUE);
|
/* pcmEncoding= */ Format.NO_VALUE);
|
||||||
} else if (wavFormat.formatType == WavUtil.TYPE_MLAW) {
|
} else if (wavFormat.formatType == WavUtil.TYPE_MLAW ||
|
||||||
|
wavFormat.formatType == WavUtil.TYPE_WAVE_FORMAT_EXTENSIBLE &&
|
||||||
|
WavUtil.KSDATAFORMAT_SUBTYPE_MULAW.equals(wavFormat.uuid)) {
|
||||||
outputWriter =
|
outputWriter =
|
||||||
new PassthroughOutputWriter(
|
new PassthroughOutputWriter(
|
||||||
extractorOutput,
|
extractorOutput,
|
||||||
@ -197,6 +209,10 @@ public final class WavExtractor implements Extractor {
|
|||||||
@C.PcmEncoding
|
@C.PcmEncoding
|
||||||
int pcmEncoding =
|
int pcmEncoding =
|
||||||
WavUtil.getPcmEncodingForType(wavFormat.formatType, wavFormat.bitsPerSample);
|
WavUtil.getPcmEncodingForType(wavFormat.formatType, wavFormat.bitsPerSample);
|
||||||
|
if (wavFormat.formatType == WavUtil.TYPE_WAVE_FORMAT_EXTENSIBLE &&
|
||||||
|
WavUtil.KSDATAFORMAT_SUBTYPE_IEEE_FLOAT.equals(wavFormat.uuid)) {
|
||||||
|
pcmEncoding = C.ENCODING_PCM_FLOAT;
|
||||||
|
}
|
||||||
if (pcmEncoding == C.ENCODING_INVALID) {
|
if (pcmEncoding == C.ENCODING_INVALID) {
|
||||||
throw ParserException.createForUnsupportedContainerFeature(
|
throw ParserException.createForUnsupportedContainerFeature(
|
||||||
"Unsupported WAV format type: " + wavFormat.formatType);
|
"Unsupported WAV format type: " + wavFormat.formatType);
|
||||||
|
@ -15,6 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.extractor.wav;
|
package androidx.media3.extractor.wav;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
/** Format information for a WAV file. */
|
/** Format information for a WAV file. */
|
||||||
/* package */ final class WavFormat {
|
/* package */ final class WavFormat {
|
||||||
|
|
||||||
@ -42,6 +45,16 @@ package androidx.media3.extractor.wav;
|
|||||||
/** Extra data appended to the format chunk. */
|
/** Extra data appended to the format chunk. */
|
||||||
public final byte[] extraData;
|
public final byte[] extraData;
|
||||||
|
|
||||||
|
/** Number of valid bits */
|
||||||
|
public final int validBitsPerSample;
|
||||||
|
|
||||||
|
/** Speaker position mask */
|
||||||
|
public final int channelMask;
|
||||||
|
|
||||||
|
/** GUID, including the data format code */
|
||||||
|
@Nullable
|
||||||
|
public final UUID uuid;
|
||||||
|
|
||||||
public WavFormat(
|
public WavFormat(
|
||||||
int formatType,
|
int formatType,
|
||||||
int numChannels,
|
int numChannels,
|
||||||
@ -57,5 +70,32 @@ package androidx.media3.extractor.wav;
|
|||||||
this.blockSize = blockSize;
|
this.blockSize = blockSize;
|
||||||
this.bitsPerSample = bitsPerSample;
|
this.bitsPerSample = bitsPerSample;
|
||||||
this.extraData = extraData;
|
this.extraData = extraData;
|
||||||
|
this.validBitsPerSample = 0;
|
||||||
|
this.channelMask = 0;
|
||||||
|
this.uuid = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WavFormat(
|
||||||
|
int formatType,
|
||||||
|
int numChannels,
|
||||||
|
int frameRateHz,
|
||||||
|
int averageBytesPerSecond,
|
||||||
|
int blockSize,
|
||||||
|
int bitsPerSample,
|
||||||
|
byte[] extraData,
|
||||||
|
int validBitsPerSample,
|
||||||
|
int channelMask,
|
||||||
|
@Nullable UUID uuid) {
|
||||||
|
this.formatType = formatType;
|
||||||
|
this.numChannels = numChannels;
|
||||||
|
this.frameRateHz = frameRateHz;
|
||||||
|
this.averageBytesPerSecond = averageBytesPerSecond;
|
||||||
|
this.blockSize = blockSize;
|
||||||
|
this.bitsPerSample = bitsPerSample;
|
||||||
|
this.extraData = extraData;
|
||||||
|
this.validBitsPerSample = validBitsPerSample;
|
||||||
|
this.channelMask = channelMask;
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ import androidx.media3.common.util.Util;
|
|||||||
import androidx.media3.extractor.ExtractorInput;
|
import androidx.media3.extractor.ExtractorInput;
|
||||||
import androidx.media3.extractor.WavUtil;
|
import androidx.media3.extractor.WavUtil;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/** Reads a WAV header 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 {
|
||||||
@ -109,9 +110,21 @@ import java.io.IOException;
|
|||||||
|
|
||||||
int bytesLeft = (int) chunkHeader.size - 16;
|
int bytesLeft = (int) chunkHeader.size - 16;
|
||||||
byte[] extraData;
|
byte[] extraData;
|
||||||
|
int validBitsPerSample = 0;
|
||||||
|
int channelMask = 0;
|
||||||
|
UUID uuid = null;
|
||||||
|
|
||||||
if (bytesLeft > 0) {
|
if (bytesLeft > 0) {
|
||||||
extraData = new byte[bytesLeft];
|
extraData = new byte[bytesLeft];
|
||||||
input.peekFully(extraData, 0, bytesLeft);
|
input.peekFully(extraData, 0, bytesLeft);
|
||||||
|
if (bytesLeft == 24) {
|
||||||
|
ParsableByteArray extra = new ParsableByteArray(extraData);
|
||||||
|
// ignore useless extra data length ( 0 or 22 )
|
||||||
|
int cbSize = extra.readLittleEndianUnsignedShort();
|
||||||
|
validBitsPerSample = extra.readLittleEndianUnsignedShort();
|
||||||
|
channelMask = extra.readLittleEndianUnsignedIntToInt();
|
||||||
|
uuid = extra.readMSGUID();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
extraData = Util.EMPTY_BYTE_ARRAY;
|
extraData = Util.EMPTY_BYTE_ARRAY;
|
||||||
}
|
}
|
||||||
@ -124,7 +137,10 @@ import java.io.IOException;
|
|||||||
averageBytesPerSecond,
|
averageBytesPerSecond,
|
||||||
blockSize,
|
blockSize,
|
||||||
bitsPerSample,
|
bitsPerSample,
|
||||||
extraData);
|
extraData,
|
||||||
|
validBitsPerSample,
|
||||||
|
channelMask,
|
||||||
|
uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user