6729836: JavaSound treats large file sizes as negative and cannot read or skip

Reviewed-by: prr
This commit is contained in:
Sergey Bylokhov 2016-05-04 01:03:46 +03:00
parent 4ff0d8b26f
commit 214029ed2b
15 changed files with 680 additions and 93 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,17 +25,14 @@
package com.sun.media.sound;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
/**
* AIFF file format.
*
* @author Jan Borgersen
*/
final class AiffFileFormat extends AudioFileFormat {
final class AiffFileFormat extends StandardFileFormat {
static final int AIFF_MAGIC = 1179603533;
@ -70,11 +67,8 @@ final class AiffFileFormat extends AudioFileFormat {
/** FVER chunk size in bytes, inclusive magic and length field */
private final int fverChunkSize=0;
AiffFileFormat( AudioFileFormat aff ) {
this( aff.getType(), aff.getByteLength(), aff.getFormat(), aff.getFrameLength() );
}
AiffFileFormat(Type type, int byteLength, AudioFormat format, int frameLength) {
AiffFileFormat(final Type type, final long byteLength,
final AudioFormat format, final long frameLength) {
super(type, byteLength, format, frameLength);
}

View File

@ -45,7 +45,7 @@ import javax.sound.sampled.UnsupportedAudioFileException;
public final class AiffFileReader extends SunFileReader {
@Override
AudioFileFormat getAudioFileFormatImpl(final InputStream stream)
StandardFileFormat getAudioFileFormatImpl(final InputStream stream)
throws UnsupportedAudioFileException, IOException {
DataInputStream dis = new DataInputStream(stream);
@ -60,11 +60,11 @@ public final class AiffFileReader extends SunFileReader {
throw new UnsupportedAudioFileException("not an AIFF file");
}
int frameLength = 0;
long /* unsigned 32bit */ frameLength = 0;
int length = dis.readInt();
int iffType = dis.readInt();
int totallength;
final long totallength;
if(length <= 0 ) {
length = AudioSystem.NOT_SPECIFIED;
totallength = AudioSystem.NOT_SPECIFIED;
@ -106,12 +106,7 @@ public final class AiffFileReader extends SunFileReader {
if (channels <= 0) {
throw new UnsupportedAudioFileException("Invalid number of channels");
}
frameLength = dis.readInt(); // numSampleFrames
if (frameLength < 0) {
// AiffFileFormat uses int, unlike AIS which uses long
//TODO this (negative) value should be passed as long to AIS
frameLength = AudioSystem.NOT_SPECIFIED;
}
frameLength = dis.readInt() & 0xffffffffL; // numSampleFrames
int sampleSizeInBits = dis.readUnsignedShort();
if (sampleSizeInBits < 1 || sampleSizeInBits > 32) {

View File

@ -33,7 +33,7 @@ import javax.sound.sampled.AudioFormat;
*
* @author Jan Borgersen
*/
final class AuFileFormat extends AudioFileFormat {
final class AuFileFormat extends StandardFileFormat {
// magic numbers
static final int AU_SUN_MAGIC = 0x2e736e64; // ".snd"
@ -55,11 +55,18 @@ final class AuFileFormat extends AudioFileFormat {
static final int AU_HEADERSIZE = 24;
/**
* According the specification of AU file format this is the value for
* length field if length is not known. This is a maximum possible value for
* the unsigned int.
*/
static final long /*unsigned int */ UNKNOWN_SIZE = 0xffffffffL;
private int auType;
AuFileFormat(AudioFileFormat.Type type, int lengthInBytes, AudioFormat format, int lengthInFrames) {
super(type,lengthInBytes,format,lengthInFrames);
AuFileFormat(final AudioFileFormat.Type type, final long byteLength,
final AudioFormat format, final long frameLength) {
super(type, byteLength, format, frameLength);
AudioFormat.Encoding encoding = format.getEncoding();

View File

@ -29,7 +29,6 @@ import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFileFormat.Type;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
@ -45,7 +44,7 @@ import javax.sound.sampled.UnsupportedAudioFileException;
public final class AuFileReader extends SunFileReader {
@Override
public AudioFileFormat getAudioFileFormatImpl(final InputStream stream)
public StandardFileFormat getAudioFileFormatImpl(final InputStream stream)
throws UnsupportedAudioFileException, IOException {
final DataInputStream dis = new DataInputStream(stream);
final int magic = dis.readInt();
@ -56,7 +55,7 @@ public final class AuFileReader extends SunFileReader {
}
final int headerSize = dis.readInt();
final int dataSize = dis.readInt();
final long /* unsigned int */ dataSize = dis.readInt() & 0xffffffffL;
final int auType = dis.readInt();
final int sampleRate = dis.readInt();
final int channels = dis.readInt();
@ -120,21 +119,21 @@ public final class AuFileReader extends SunFileReader {
// unsupported filetype, throw exception
throw new UnsupportedAudioFileException("not a valid AU file");
}
// now seek past the header
dis.skipBytes(headerSize - AuFileFormat.AU_HEADERSIZE);
final int frameSize = calculatePCMFrameSize(sampleSizeInBits, channels);
//$$fb 2002-11-02: fix for 4629669: AU file reader: problems with empty files
final int length;
if (dataSize < 0) {
length = AudioSystem.NOT_SPECIFIED;
} else {
//$$fb 2003-10-20: fix for 4940459: AudioInputStream.getFrameLength() returns 0 instead of NOT_SPECIFIED
length = dataSize / frameSize;
//$$fb 2003-10-20: fix for 4940459: AudioInputStream.getFrameLength() returns 0 instead of NOT_SPECIFIED
long frameLength = AudioSystem.NOT_SPECIFIED;
long byteLength = AudioSystem.NOT_SPECIFIED;
if (dataSize != AuFileFormat.UNKNOWN_SIZE) {
frameLength = dataSize / frameSize;
byteLength = dataSize + headerSize;
}
// now seek past the header
dis.skipBytes(headerSize - AuFileFormat.AU_HEADERSIZE);
final AudioFormat format = new AudioFormat(encoding, sampleRate,
sampleSizeInBits, channels,
frameSize, sampleRate, true);
return new AuFileFormat(Type.AU, dataSize + headerSize, format, length);
return new AuFileFormat(Type.AU, byteLength, format, frameLength);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -38,7 +38,6 @@ import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import javax.sound.midi.Sequence;
import javax.sound.midi.Track;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFileFormat.Type;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
@ -56,10 +55,10 @@ public final class SoftMidiAudioFileReader extends SunFileReader {
private static final AudioFormat format = new AudioFormat(44100, 16, 2,
true, false);
private static AudioFileFormat getAudioFileFormat(final Sequence seq) {
private static StandardFileFormat getAudioFileFormat(final Sequence seq) {
long totallen = seq.getMicrosecondLength() / 1000000;
long len = (long) (format.getFrameRate() * (totallen + 4));
return new AudioFileFormat(MIDI, format, (int) len);
return new StandardFileFormat(MIDI, format, len);
}
private AudioInputStream getAudioInputStream(final Sequence seq)
@ -140,7 +139,7 @@ public final class SoftMidiAudioFileReader extends SunFileReader {
}
@Override
AudioFileFormat getAudioFileFormatImpl(final InputStream stream)
StandardFileFormat getAudioFileFormatImpl(final InputStream stream)
throws UnsupportedAudioFileException, IOException {
try {
return getAudioFileFormat(MidiSystem.getSequence(stream));

View File

@ -0,0 +1,114 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
/**
* An instance of the {@code StandardFileFormat} describes the file's length in
* bytes and the length in sample frames as longs. This will provide an
* additional precision unlike the {@code AudioFileFormat}.
*/
class StandardFileFormat extends AudioFileFormat {
/**
* File length in bytes stored as long.
*/
private final long byteLength;
/**
* Audio data length in sample frames stored as long.
*/
private final long frameLength;
/**
* Constructs {@code StandardFileFormat} object.
*
* @param type the type of the audio file
* @param format the format of the audio data contained in the file
* @param frameLength the audio data length in sample frames, or
* {@code AudioSystem.NOT_SPECIFIED}
*/
StandardFileFormat(final Type type, final AudioFormat format,
final long frameLength) {
this(type, AudioSystem.NOT_SPECIFIED, format, frameLength);
}
/**
* Constructs {@code StandardFileFormat} object.
*
* @param type the type of the audio file
* @param byteLength the length of the file in bytes, or
* {@code AudioSystem.NOT_SPECIFIED}
* @param format the format of the audio data contained in the file
* @param frameLength the audio data length in sample frames, or
* {@code AudioSystem.NOT_SPECIFIED}
*/
StandardFileFormat(final Type type, final long byteLength,
final AudioFormat format, final long frameLength) {
super(type, clip(byteLength), format, clip(frameLength));
this.byteLength = byteLength;
this.frameLength = frameLength;
}
/**
* Replaces the passed value to {@code AudioSystem.NOT_SPECIFIED} if the
* value is greater than {@code Integer.MAX_VALUE}.
*
* @param value which should be clipped
* @return the clipped value
*/
private static int clip(final long value) {
if (value > Integer.MAX_VALUE) {
return AudioSystem.NOT_SPECIFIED;
}
return (int) value;
}
/**
* Obtains the length of the audio data contained in the file, expressed in
* sample frames. The long precision is used.
*
* @return the number of sample frames of audio data in the file
* @see AudioSystem#NOT_SPECIFIED
*/
public final long getLongFrameLength() {
return frameLength;
}
/**
* Obtains the size in bytes of the entire audio file (not just its audio
* data). The long precision is used.
*
* @return the audio file length in bytes
* @see AudioSystem#NOT_SPECIFIED
*/
public final long getLongByteLength() {
return byteLength;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -47,7 +47,7 @@ import javax.sound.sampled.spi.AudioFileReader;
abstract class SunFileReader extends AudioFileReader {
@Override
public final AudioFileFormat getAudioFileFormat(final InputStream stream)
public final StandardFileFormat getAudioFileFormat(final InputStream stream)
throws UnsupportedAudioFileException, IOException {
stream.mark(200); // The biggest value which was historically used
try {
@ -87,11 +87,11 @@ abstract class SunFileReader extends AudioFileReader {
throws UnsupportedAudioFileException, IOException {
stream.mark(200); // The biggest value which was historically used
try {
final AudioFileFormat fileFormat = getAudioFileFormatImpl(stream);
final StandardFileFormat format = getAudioFileFormatImpl(stream);
// we've got everything, the stream is supported and it is at the
// beginning of the audio data, so return an AudioInputStream
return new AudioInputStream(stream, fileFormat.getFormat(),
fileFormat.getFrameLength());
return new AudioInputStream(stream, format.getFormat(),
format.getLongFrameLength());
} catch (UnsupportedAudioFileException | EOFException ignored) {
// stream is unsupported or the header is less than was expected
stream.reset();
@ -140,7 +140,7 @@ abstract class SunFileReader extends AudioFileReader {
* UnsupportedAudioFileException if the header is less than was
* expected
*/
abstract AudioFileFormat getAudioFileFormatImpl(InputStream stream)
abstract StandardFileFormat getAudioFileFormatImpl(InputStream stream)
throws UnsupportedAudioFileException, IOException;
// HELPER METHODS

View File

@ -34,7 +34,6 @@ import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioFormat.Encoding;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
/**
@ -167,7 +166,7 @@ public final class WaveExtensibleFileReader extends SunFileReader {
}
@Override
AudioFileFormat getAudioFileFormatImpl(final InputStream stream)
StandardFileFormat getAudioFileFormatImpl(final InputStream stream)
throws UnsupportedAudioFileException, IOException {
RIFFReader riffiterator = new RIFFReader(stream);
@ -249,19 +248,17 @@ public final class WaveExtensibleFileReader extends SunFileReader {
} else {
throw new UnsupportedAudioFileException();
}
long frameLength = dataSize / audioformat.getFrameSize();
if (frameLength > Integer.MAX_VALUE) {
frameLength = AudioSystem.NOT_SPECIFIED;
}
return new AudioFileFormat(AudioFileFormat.Type.WAVE, audioformat,
(int) frameLength);
return new StandardFileFormat(AudioFileFormat.Type.WAVE, audioformat,
dataSize / audioformat.getFrameSize());
}
@Override
public AudioInputStream getAudioInputStream(final InputStream stream)
throws UnsupportedAudioFileException, IOException {
final AudioFileFormat format = getAudioFileFormat(stream);
final StandardFileFormat format = getAudioFileFormat(stream);
final AudioFormat af = format.getFormat();
final long length = format.getLongFrameLength();
// we've got everything, the stream is supported and it is at the
// beginning of the header, so find the data chunk again and return an
// AudioInputStream
@ -269,8 +266,6 @@ public final class WaveExtensibleFileReader extends SunFileReader {
while (riffiterator.hasNextChunk()) {
RIFFReader chunk = riffiterator.nextChunk();
if (chunk.getFormat().equals("data")) {
final AudioFormat af = format.getFormat();
final long length = chunk.getSize() / af.getFrameSize();
return new AudioInputStream(chunk, af, length);
}
}

View File

@ -33,7 +33,7 @@ import javax.sound.sampled.AudioFormat;
*
* @author Jan Borgersen
*/
final class WaveFileFormat extends AudioFileFormat {
final class WaveFileFormat extends StandardFileFormat {
/**
* Wave format type.
@ -73,9 +73,9 @@ final class WaveFileFormat extends AudioFileFormat {
static final int WAVE_FORMAT_SX7383 = 0x1C07;
static final int WAVE_FORMAT_EXTENSIBLE= 0xFFFE;
WaveFileFormat(AudioFileFormat.Type type, int lengthInBytes, AudioFormat format, int lengthInFrames) {
super(type,lengthInBytes,format,lengthInFrames);
WaveFileFormat(final AudioFileFormat.Type type, final long byteLength,
final AudioFormat format, final long frameLength) {
super(type, byteLength, format, frameLength);
AudioFormat.Encoding encoding = format.getEncoding();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -45,7 +45,7 @@ import javax.sound.sampled.UnsupportedAudioFileException;
public final class WaveFileReader extends SunFileReader {
@Override
AudioFileFormat getAudioFileFormatImpl(final InputStream stream)
StandardFileFormat getAudioFileFormatImpl(final InputStream stream)
throws UnsupportedAudioFileException, IOException {
// assumes sream is rewound
@ -64,9 +64,9 @@ public final class WaveFileReader extends SunFileReader {
DataInputStream dis = new DataInputStream( stream );
int magic = dis.readInt();
int fileLength = rllong(dis);
long /* unsigned int */ fileLength = rllong(dis) & 0xffffffffL;
int waveMagic = dis.readInt();
int totallength;
long totallength;
if (fileLength <= 0) {
fileLength = AudioSystem.NOT_SPECIFIED;
totallength = AudioSystem.NOT_SPECIFIED;
@ -186,19 +186,18 @@ public final class WaveFileReader extends SunFileReader {
}
}
// this is the length of the data chunk
int dataLength = rllong(dis); nread += 4;
long /* unsigned int */ dataLength = rllong(dis) & 0xffffffffL; nread += 4;
// now build the new AudioFileFormat and return
final int frameSize = calculatePCMFrameSize(sampleSizeInBits, channels);
AudioFormat format = new AudioFormat(encoding,
(float)sampleRate,
sampleSizeInBits, channels,
calculatePCMFrameSize(sampleSizeInBits, channels),
frameSize,
(float)sampleRate, false);
return new WaveFileFormat(AudioFileFormat.Type.WAVE,
totallength,
format,
dataLength / format.getFrameSize());
long frameLength = dataLength / format.getFrameSize();
return new WaveFileFormat(AudioFileFormat.Type.WAVE, totallength,
format, frameLength);
}
}

View File

@ -32,7 +32,6 @@ import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioFormat.Encoding;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
/**
@ -43,7 +42,7 @@ import javax.sound.sampled.UnsupportedAudioFileException;
public final class WaveFloatFileReader extends SunFileReader {
@Override
AudioFileFormat getAudioFileFormatImpl(final InputStream stream)
StandardFileFormat getAudioFileFormatImpl(final InputStream stream)
throws UnsupportedAudioFileException, IOException {
RIFFReader riffiterator = new RIFFReader(stream);
@ -88,20 +87,17 @@ public final class WaveFloatFileReader extends SunFileReader {
AudioFormat audioformat = new AudioFormat(
Encoding.PCM_FLOAT, samplerate, bits, channels,
framesize, samplerate, false);
long frameLength = dataSize / audioformat.getFrameSize();
if (frameLength > Integer.MAX_VALUE) {
frameLength = AudioSystem.NOT_SPECIFIED;
}
return new AudioFileFormat(AudioFileFormat.Type.WAVE, audioformat,
(int) frameLength);
return new StandardFileFormat(AudioFileFormat.Type.WAVE, audioformat,
dataSize / audioformat.getFrameSize());
}
@Override
public AudioInputStream getAudioInputStream(final InputStream stream)
throws UnsupportedAudioFileException, IOException {
final AudioFileFormat format = getAudioFileFormat(stream);
final StandardFileFormat format = getAudioFileFormat(stream);
final AudioFormat af = format.getFormat();
final long length = format.getLongFrameLength();
// we've got everything, the stream is supported and it is at the
// beginning of the header, so find the data chunk again and return an
// AudioInputStream
@ -109,8 +105,6 @@ public final class WaveFloatFileReader extends SunFileReader {
while (riffiterator.hasNextChunk()) {
RIFFReader chunk = riffiterator.nextChunk();
if (chunk.getFormat().equals("data")) {
final AudioFormat af = format.getFormat();
final long length = chunk.getSize() / af.getFrameSize();
return new AudioInputStream(chunk, af, length);
}
}

View File

@ -0,0 +1,247 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.ByteArrayInputStream;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
/**
* @test
* @bug 6729836
*/
public final class RecognizeHugeAiffFiles {
/**
* The maximum number of sample frames per AIFF specification.
*/
private static final /* unsigned int */ long MAX_UNSIGNED_INT = 0xffffffffL;
/**
* The supported aiff sample size in bits.
*/
private static final byte[] aiffBits = {
1, 2, 4, 8, 11, 16, 20, 24, 27, 32
};
/**
* The list of supported sample rates.
*/
private static final int[] sampleRates = {
8000, 11025, 16000, 22050, 32000, 37800, 44056, 44100, 47250, 48000,
50000, 50400, 88200, 96000, 176400, 192000, 352800, 2822400,
5644800, Integer.MAX_VALUE
};
/**
* The list of supported channels.
*/
private static final int[] channels = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
};
/**
* The list of supported number of frames.
* <p>
* The {@code MAX_UNSIGNED_INT} is a maximum.
*/
private static final long[] numberOfFrames = {
0, 1, 2, 3, Integer.MAX_VALUE - 1, Integer.MAX_VALUE,
(long) Integer.MAX_VALUE + 1, MAX_UNSIGNED_INT - 1, MAX_UNSIGNED_INT
};
public static void main(final String[] args) throws Exception {
for (final byte bits : aiffBits) {
for (final int sampleRate : sampleRates) {
for (final int channel : channels) {
for (final long dataSize : numberOfFrames) {
testAFF(bits, sampleRate, channel, dataSize);
testAIS(bits, sampleRate, channel, dataSize);
}
}
}
}
}
/**
* Tests the {@code AudioFileFormat} fetched from the fake header.
* <p>
* Note that the frameLength and byteLength are stored as int which means
* that {@code AudioFileFormat} will store the data above {@code MAX_INT} as
* NOT_SPECIFIED.
*/
private static void testAFF(final byte bits, final int rate,
final int channel, final long frameLength)
throws Exception {
final byte[] header = createHeader(bits, rate, channel, frameLength);
final ByteArrayInputStream fake = new ByteArrayInputStream(header);
final AudioFileFormat aff = AudioSystem.getAudioFileFormat(fake);
if (aff.getType() != AudioFileFormat.Type.AIFF) {
throw new RuntimeException("Error");
}
if (frameLength <= Integer.MAX_VALUE) {
if (aff.getFrameLength() != frameLength) {
System.err.println("Expected: " + frameLength);
System.err.println("Actual: " + aff.getFrameLength());
throw new RuntimeException();
}
} else {
if (aff.getFrameLength() != AudioSystem.NOT_SPECIFIED) {
System.err.println("Expected: " + AudioSystem.NOT_SPECIFIED);
System.err.println("Actual: " + aff.getFrameLength());
throw new RuntimeException();
}
}
validateFormat(bits, rate, channel, aff.getFormat());
}
/**
* Tests the {@code AudioInputStream} fetched from the fake header.
* <p>
* Note that the frameLength is stored as long which means that {@code
* AudioInputStream} must store all possible data from aiff file.
*/
private static void testAIS(final byte bits, final int rate,
final int channel, final long frameLength)
throws Exception {
final byte[] header = createHeader(bits, rate, channel, frameLength);
final ByteArrayInputStream fake = new ByteArrayInputStream(header);
final AudioInputStream ais = AudioSystem.getAudioInputStream(fake);
final AudioFormat format = ais.getFormat();
if (frameLength != ais.getFrameLength()) {
System.err.println("Expected: " + frameLength);
System.err.println("Actual: " + ais.getFrameLength());
throw new RuntimeException();
}
if (ais.available() < 0) {
System.err.println("available should be >=0: " + ais.available());
throw new RuntimeException();
}
validateFormat(bits, rate, channel, format);
}
/**
* Tests that format contains the same data as were provided to the fake
* stream.
*/
private static void validateFormat(final byte bits, final int rate,
final int channel,
final AudioFormat format) {
if (Float.compare(format.getSampleRate(), rate) != 0) {
System.err.println("Expected: " + rate);
System.err.println("Actual: " + format.getSampleRate());
throw new RuntimeException();
}
if (format.getChannels() != channel) {
System.err.println("Expected: " + channel);
System.err.println("Actual: " + format.getChannels());
throw new RuntimeException();
}
int frameSize = ((bits + 7) / 8) * channel;
if (format.getFrameSize() != frameSize) {
System.out.println("Expected: " + frameSize);
System.err.println("Actual: " + format.getFrameSize());
throw new RuntimeException();
}
}
private static final int DOUBLE_MANTISSA_LENGTH = 52;
private static final int DOUBLE_EXPONENT_LENGTH = 11;
private static final long DOUBLE_SIGN_MASK = 0x8000000000000000L;
private static final long DOUBLE_EXPONENT_MASK = 0x7FF0000000000000L;
private static final long DOUBLE_MANTISSA_MASK = 0x000FFFFFFFFFFFFFL;
private static final int DOUBLE_EXPONENT_OFFSET = 1023;
private static final int EXTENDED_EXPONENT_OFFSET = 16383;
private static final int EXTENDED_MANTISSA_LENGTH = 63;
private static final int EXTENDED_EXPONENT_LENGTH = 15;
private static final long EXTENDED_INTEGER_MASK = 0x8000000000000000L;
/**
* Creates the custom header of the AIFF file. It is expected that all
* passed data are supported.
*/
private static byte[] createHeader(final byte bits, final int rate,
final int channel, final long frameLength) {
long doubleBits = Double.doubleToLongBits(rate);
long sign = (doubleBits & DOUBLE_SIGN_MASK)
>> (DOUBLE_EXPONENT_LENGTH + DOUBLE_MANTISSA_LENGTH);
long doubleExponent = (doubleBits & DOUBLE_EXPONENT_MASK)
>> DOUBLE_MANTISSA_LENGTH;
long doubleMantissa = doubleBits & DOUBLE_MANTISSA_MASK;
long extendedExponent = doubleExponent - DOUBLE_EXPONENT_OFFSET
+ EXTENDED_EXPONENT_OFFSET;
long extendedMantissa = doubleMantissa
<< (EXTENDED_MANTISSA_LENGTH - DOUBLE_MANTISSA_LENGTH);
long extendedSign = sign << EXTENDED_EXPONENT_LENGTH;
short extendedBits79To64 = (short) (extendedSign | extendedExponent);
long extendedBits63To0 = EXTENDED_INTEGER_MASK | extendedMantissa;
return new byte[]{
// AIFF_MAGIC
0x46, 0x4f, 0x52, 0x4d,
// fileLength (will use the number of frames for testing)
(byte) (frameLength >> 24), (byte) (frameLength >> 16),
(byte) (frameLength >> 8), (byte) frameLength,
// form aiff
0x41, 0x49, 0x46, 0x46,
// COMM_MAGIC
0x43, 0x4f, 0x4d, 0x4d,
// comm chunk size
0, 0, 0, 18,
// channels
(byte) (channel >> 8),(byte) channel,
// numSampleFrames
(byte) (frameLength >> 24), (byte) (frameLength >> 16),
(byte) (frameLength >> 8), (byte) (frameLength),
// samplesize
(byte) (bits >> 8),(byte) (bits),
// samplerate
(byte) (extendedBits79To64 >> 8),
(byte) extendedBits79To64,
(byte) (extendedBits63To0 >> 56),
(byte) (extendedBits63To0 >> 48),
(byte) (extendedBits63To0 >> 40),
(byte) (extendedBits63To0 >> 32), (byte) (extendedBits63To0 >> 24),
(byte) (extendedBits63To0 >> 16), (byte) (extendedBits63To0 >> 8),
(byte) extendedBits63To0,
// SND_MAGIC
0x53, 0x53, 0x4e, 0x44,
// data chunk size
0, 0, 0, 0,
// dataOffset
0, 0, 0, 0,
// blocksize
0, 0, 0, 0,
};
}
}

View File

@ -0,0 +1,232 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.ByteArrayInputStream;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
/**
* @test
* @bug 6729836
*/
public final class RecognizeHugeAuFiles {
/**
* The size of the header's data.
*/
private static final byte AU_HEADER = 44;
/**
* This value should be used if the size in bytes is unknown.
*/
private static final /* unsigned int */ long MAX_UNSIGNED_INT = 0xffffffffL;
/**
* The list of supported au formats and sample size in bits per format.
*/
private static final byte[][] auTypeBits = {
{1, 8}, {2, 8}, {3, 16}, {4, 24}, {5, 32}, {6, 32}, {27, 8}
};
/**
* The list of supported sample rates(stored as unsigned int).
*/
private static final int[] sampleRates = {
8000, 11025, 16000, 22050, 32000, 37800, 44056, 44100, 47250, 48000,
50000, 50400, 88200, 96000, 176400, 192000, 352800, 2822400,
5644800, Integer.MAX_VALUE
};
/**
* The list of supported channels (stored as unsigned int).
*/
private static final int[] channels = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
};
/**
* The list of supported size of data (stored as unsigned int).
* <p>
* The {@code MAX_UNSIGNED_INT} used if the size in bytes is unknown.
*/
private static final long[] dataSizes = {
0, 1, 2, 3, Integer.MAX_VALUE - AU_HEADER, Integer.MAX_VALUE - 1,
Integer.MAX_VALUE, (long) Integer.MAX_VALUE + 1,
(long) Integer.MAX_VALUE + AU_HEADER, MAX_UNSIGNED_INT - AU_HEADER,
MAX_UNSIGNED_INT - 1, MAX_UNSIGNED_INT
};
public static void main(final String[] args) throws Exception {
for (final byte[] type : auTypeBits) {
for (final int sampleRate : sampleRates) {
for (final int channel : channels) {
for (final long dataSize : dataSizes) {
testAFF(type, sampleRate, channel, dataSize);
testAIS(type, sampleRate, channel, dataSize);
}
}
}
}
}
/**
* Tests the {@code AudioFileFormat} fetched from the fake header.
* <p>
* Note that the frameLength and byteLength are stored as int which means
* that {@code AudioFileFormat} will store the data above {@code MAX_INT}
* as NOT_SPECIFIED.
*/
private static void testAFF(final byte[] type, final int rate,
final int channel, final long size)
throws Exception {
final byte[] header = createHeader(type, rate, channel, size);
final ByteArrayInputStream fake = new ByteArrayInputStream(header);
final AudioFileFormat aff = AudioSystem.getAudioFileFormat(fake);
final AudioFormat format = aff.getFormat();
if (aff.getType() != AudioFileFormat.Type.AU) {
throw new RuntimeException("Error");
}
final long frameLength = size / format.getFrameSize();
if (size != MAX_UNSIGNED_INT && frameLength <= Integer.MAX_VALUE) {
if (aff.getFrameLength() != frameLength) {
System.err.println("Expected: " + frameLength);
System.err.println("Actual: " + aff.getFrameLength());
throw new RuntimeException();
}
} else {
if (aff.getFrameLength() != AudioSystem.NOT_SPECIFIED) {
System.err.println("Expected: " + AudioSystem.NOT_SPECIFIED);
System.err.println("Actual: " + aff.getFrameLength());
throw new RuntimeException();
}
}
final long byteLength = size + AU_HEADER;
if (byteLength <= Integer.MAX_VALUE) {
if (aff.getByteLength() != byteLength) {
System.err.println("Expected: " + byteLength);
System.err.println("Actual: " + aff.getByteLength());
throw new RuntimeException();
}
} else {
if (aff.getByteLength() != AudioSystem.NOT_SPECIFIED) {
System.err.println("Expected: " + AudioSystem.NOT_SPECIFIED);
System.err.println("Actual: " + aff.getByteLength());
throw new RuntimeException();
}
}
validateFormat(type[1], rate, channel, aff.getFormat());
}
/**
* Tests the {@code AudioInputStream} fetched from the fake header.
* <p>
* Note that the frameLength is stored as long which means
* that {@code AudioInputStream} must store all possible data from au file.
*/
private static void testAIS(final byte[] type, final int rate,
final int channel, final long size)
throws Exception {
final byte[] header = createHeader(type, rate, channel, size);
final ByteArrayInputStream fake = new ByteArrayInputStream(header);
final AudioInputStream ais = AudioSystem.getAudioInputStream(fake);
final AudioFormat format = ais.getFormat();
final long frameLength = size / format.getFrameSize();
if (size != MAX_UNSIGNED_INT) {
if (frameLength != ais.getFrameLength()) {
System.err.println("Expected: " + frameLength);
System.err.println("Actual: " + ais.getFrameLength());
throw new RuntimeException();
}
} else {
if (ais.getFrameLength() != AudioSystem.NOT_SPECIFIED) {
System.err.println("Expected: " + AudioSystem.NOT_SPECIFIED);
System.err.println("Actual: " + ais.getFrameLength());
throw new RuntimeException();
}
}
if (ais.available() < 0) {
System.err.println("available should be >=0: " + ais.available());
throw new RuntimeException();
}
validateFormat(type[1], rate, channel, format);
}
/**
* Tests that format contains the same data as were provided to the fake
* stream.
*/
private static void validateFormat(final byte bits, final int rate,
final int channel,
final AudioFormat format) {
if (Float.compare(format.getSampleRate(), rate) != 0) {
System.out.println("Expected: " + rate);
System.out.println("Actual: " + format.getSampleRate());
throw new RuntimeException();
}
if (format.getChannels() != channel) {
System.out.println("Expected: " + channel);
System.out.println("Actual: " + format.getChannels());
throw new RuntimeException();
}
int frameSize = ((bits + 7) / 8) * channel;
if (format.getFrameSize() != frameSize) {
System.out.println("Expected: " + frameSize);
System.out.println("Actual: " + format.getFrameSize());
throw new RuntimeException();
}
}
/**
* Creates the custom header of the AU file. It is expected that all passed
* data are supported.
*/
private static byte[] createHeader(final byte[] type, final int rate,
final int channel, final long size) {
return new byte[]{
// AU_SUN_MAGIC
0x2e, 0x73, 0x6e, 0x64,
// headerSize
0, 0, 0, AU_HEADER,
// dataSize
(byte) (size >> 24), (byte) (size >> 16), (byte) (size >> 8),
(byte) size,
// encoding
0, 0, 0, type[0],
// sampleRate
(byte) (rate >> 24), (byte) (rate >> 16), (byte) (rate >> 8),
(byte) (rate),
// channels
(byte) (channel >> 24), (byte) (channel >> 16),
(byte) (channel >> 8), (byte) (channel),
// data
0, 0, 0, 0, 0, 0
};
}
}

View File

@ -125,7 +125,7 @@ public final class RecognizeHugeWaveExtFiles {
* Tests the {@code AudioInputStream} fetched from the fake header.
* <p>
* Note that the frameLength is stored as long which means that {@code
* AudioInputStream} must store all possible data from au file.
* AudioInputStream} must store all possible data from wave file.
*/
private static void testAIS(final int[] type, final int rate,
final int channel, final long size)
@ -166,8 +166,9 @@ public final class RecognizeHugeWaveExtFiles {
System.err.println("Actual: " + format.getChannels());
throw new RuntimeException();
}
if (format.getFrameSize() != ((bits + 7) / 8) * channel) {
System.err.println("Expected: " + (bits * channel + 1) / 8);
int frameSize = ((bits + 7) / 8) * channel;
if (format.getFrameSize() != frameSize) {
System.err.println("Expected: " + frameSize);
System.err.println("Actual: " + format.getFrameSize());
throw new RuntimeException();
}

View File

@ -30,9 +30,9 @@ import javax.sound.sampled.AudioSystem;
/**
* @test
* @bug 8132782
* @bug 8132782 6729836
*/
public final class RecognizeHugeWaveFloatFiles {
public final class RecognizeHugeWaveFiles {
/**
* The maximum size in bytes per WAVE specification.
@ -43,7 +43,17 @@ public final class RecognizeHugeWaveFloatFiles {
* The supported wave pcm_float format and sample size in bits.
*/
private static final byte[][] waveTypeBits = {
{0x0003/*WAVE_FORMAT_IEEE_FLOAT*/, 32}
{0x0001/*WAVE_FORMAT_PCM*/,1},
{0x0001/*WAVE_FORMAT_PCM*/,2},
{0x0001/*WAVE_FORMAT_PCM*/,4},
{0x0001/*WAVE_FORMAT_PCM*/,8},
{0x0001/*WAVE_FORMAT_PCM*/,16},
{0x0001/*WAVE_FORMAT_PCM*/,20},
{0x0001/*WAVE_FORMAT_PCM*/,24},
{0x0001/*WAVE_FORMAT_PCM*/,32},
{0x0003/*WAVE_FORMAT_IEEE_FLOAT*/, 32},
{0x0006/*WAVE_FORMAT_ALAW*/, 8},
{0x0007/*WAVE_FORMAT_MULAW*/, 8}
};
/**
@ -125,7 +135,7 @@ public final class RecognizeHugeWaveFloatFiles {
* Tests the {@code AudioInputStream} fetched from the fake header.
* <p>
* Note that the frameLength is stored as long which means that {@code
* AudioInputStream} must store all possible data from au file.
* AudioInputStream} must store all possible data from wave file.
*/
private static void testAIS(final byte[] type, final int rate,
final int channel, final long size)
@ -166,8 +176,9 @@ public final class RecognizeHugeWaveFloatFiles {
System.err.println("Actual: " + format.getChannels());
throw new RuntimeException();
}
if (format.getFrameSize() != ((bits + 7) / 8) * channel) {
System.err.println("Expected: " + (bits * channel + 1) / 8);
int frameSize = ((bits + 7) / 8) * channel;
if (format.getFrameSize() != frameSize) {
System.err.println("Expected: " + frameSize);
System.err.println("Actual: " + format.getFrameSize());
throw new RuntimeException();
}