Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6729836

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

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Icon: P3 P3
    • 9
    • 6
    • client-libs
    • b120
    • x86
    • windows_xp

      FULL PRODUCT VERSION :
      1.5.0_15
      1.6.0_10
      1.7.0-ea

      ADDITIONAL OS VERSION INFORMATION :
      Windows XP SP2


      EXTRA RELEVANT SYSTEM CONFIGURATION :
      You need to have a large ( > 2 GB ) wave file.

      A DESCRIPTION OF THE PROBLEM :
      Within the AudioInputStream constructor, one of the parameters is length. When the input length is very large (greater than 2 GB), the length of the file is treated as negative so all calls to skip and read fail because the filePos is greater than the fileLength. In my case, the call to the constructor comes from the WaveFileReader, for which I do not have the source. It could be that all readers should be fixed to pass a positive length to AudioInputStream, or AudioInpuStream can mask the top 1 or 2 bits of the input length to treat the input number as unsigned. I think that the inherent problem is that Java ints and longs are signed, and someone is overflowing a number and making it negative. In my experience, when I get back an AudioInputStream with a negative length, I can get the correct length by masking wih 0x3FFFFFFF, but this is still an int so we're losing some capabilities for long numbers.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Run test program. Choose a wave file that is 2.6 GB in size. Mine is 2,764,296,174 bytes.


      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      Frame length for a wave file of size 2,764,296,174 bytes should be 691074000. Expect skip method to skip ahead and return number of bytes skipped.
      ACTUAL -
      Frame length will be negative and should not be. Frame length for a wave file of size 2,764,296,174 bytes was -382667824 as reported by JavaSound. This causes calls to read method to fail because framePos > frameLength. Also, calls to skip method do not skip bytes but either fail with an exception when the underlying stream is a FileInputStream (when compiled in JDK 1.4.2) or returns 0 when underlying stream is PushbackInputStream because n <= 0. This is because in the skip method, this check is done:

      if( (n/frameSize) > (frameLength-framePos) ) {
      n = (frameLength-framePos) * frameSize;
      }

      This causes n to be ("large negative" - 0) * 4 = large negative.

      If you can check for negative input length to AudioInputStream constructor and mask out high bits if negative, then this should work.

      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      Stream frame length = -382667824
      Received -1 bytes on the read
      skip returned new offset=0
      second skip returned new offset=0

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import java.io.File;

      import javax.sound.sampled.AudioInputStream;
      import javax.sound.sampled.AudioSystem;
      import javax.swing.JFileChooser;

      public class TestAudioInputStream {


          public static void main(String[] inArgs) {
          
           JFileChooser chooser = new JFileChooser();
           chooser.setDialogTitle("Choose a sound file to open");
           int choice = chooser.showOpenDialog(null);
          
           if (choice == JFileChooser.APPROVE_OPTION) {
          
           File file = chooser.getSelectedFile();
          
           try {
           AudioInputStream stream = AudioSystem.getAudioInputStream(file);
           long length = stream.getFrameLength();
          
           System.out.println("Stream frame length = " + length);
          
           // Attempt to read at start of file.
                      int bufferSize = 128;
                      byte[] buffer = new byte[bufferSize];
                      int received = stream.read(buffer, 0, bufferSize);
                      System.out.println("Received " + received + " bytes on the read");
          
           // Try to skip ahead just one frame = 4 bytes.
                      try {

                          long skipped = stream.skip(100000);
                       System.out.println("skip returned new offset=" + skipped);
                      
                       skipped = stream.skip(600000000);
                       System.out.println("second skip returned new offset=" + skipped);

                      } catch (java.io.IOException e) {
                       
                       System.out.println("skip failed, " + e.getMessage());
                      
                      }
          
      } catch (java.io.FileNotFoundException e) {

      System.out.println("File not found");

      } catch (java.io.IOException e) {

      System.out.println(e.getMessage());

      } catch (javax.sound.sampled.UnsupportedAudioFileException e) {
              
      System.out.println(e.getMessage());

      }

           }

          }

      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      I've tried passing my AudioInputStream to my own CorrectedAudioInputStream constructor, which extends AudioInputStream and which does mask out the high bits; however, when I get to the skip call, I need to get access to the underlying InputStream within the AudioInputStream, and this is a private member so I cannot access it. I therefore call super.skip(n), which is called on the original AudioInputStream object, and since that object still has the negative frameLength, I still get the error in skip. For read, I would need to implement the whole thing myself.

      My only workaround is to implement AudioInputStream myself.

            serb Sergey Bylokhov
            ndcosta Nelson Dcosta (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: