Semantive / waveform-android

Waveform view for Android (no longer actively maintained)
Apache License 2.0
249 stars 67 forks source link

java.io.IOException: Went over by 7 bytes #4

Open zukopvd opened 8 years ago

zukopvd commented 8 years ago

Hello! Can you please suggest me what can i do with "Went over 7 bytes" exception? One of my test devices always trows it at CheapAAC.parseMp4(CheapAAC.java:285). It's always "7" bytes for every m4a file that i try to use. How can i solve it?

Thanks. Serge.

zukopvd commented 8 years ago

Hello again!

Here is more information about this issue. I'm working with m4a files that was encoded by android MediaCodec (code below). On all of my test devices encoding works fine: resutl codec name is correct (OMX.google.aac.decoder), file is playable and can be parsed to waveform by ringdroid. But issue comes to the one of devices when I try to parse file with your waveform-android libriary. Device is: Sony Xperia Tablet Z3 Compact under Android 5.1.1 - file that was encoded there is playable, can be parsed with ringdroid, but with waveform-android it's always throw "java.io.IOException: Went over by 7 bytes" and it's always at third step of parse iterations:

04-03 15:08:26.853 24412-24628/com.semantive.waveformandroid.waveform.soundfile.CheapAAC: skipLen: 16, atomLen: 24, mOffset: 8, initialOffset: 0, header: [0, 0, 0, 24, 102, 116, 121, 112] 04-03 15:08:26.853 24412-24628/com.semantive.waveformandroid.waveform.soundfile.CheapAAC: skipLen: 6136, atomLen: 6144, mOffset: 32, initialOffset: 24, header: [0, 0, 24, 0, 102, 114, 101, 101] 04-03 15:08:26.854 24412-24628/com.semantive.waveformandroid.waveform.soundfile.CheapAAC: skipLen: -7, atomLen: 1, mOffset: 6176, initialOffset: 6168, header: [0, 0, 0, 1, 109, 100, 97, 116] 04-03 15:08:26.854 24412-24628/ W/System.err: java.io.IOException: Went over by 7 bytes 04-03 15:08:26.854 24412-24628/W/System.err: at com.semantive.waveformandroid.waveform.soundfile.CheapAAC.parseMp4(CheapAAC.java:288) 04-03 15:08:26.854 24412-24628/ W/System.err: at com.semantive.waveformandroid.waveform.soundfile.CheapAAC.ReadFile(CheapAAC.java:200) 04-03 15:08:26.855 24412-24628/ W/System.err: at com.semantive.waveformandroid.waveform.soundfile.CheapSoundFile.create(CheapSoundFile.java:100)

If I put to Sony any m4a file, that was encoded on other device parse works fine. Here is encoding code that I used. May be I should do something in different way:

`

private static void convertWavToM4A(File wavFile, File dstM4AFile, MediaFormat format) {
    final String COMPRESSED_AUDIO_FILE_MIME_TYPE = "audio/mp4a-latm";

    final int COMPRESSED_AUDIO_FILE_BIT_RATE = 256000; 

    final int SAMPLING_RATE = format == null ? 44100 : format.getInteger(MediaFormat.KEY_SAMPLE_RATE);

    final int BUFFER_SIZE = SAMPLING_RATE;

    final int CODEC_TIMEOUT_IN_MS = 5000;

    final int CHANNELS_COUNT = format == null ? 1 : format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);

    try {
        FileInputStream fis = new FileInputStream(wavFile);
        if (dstM4AFile.exists()) dstM4AFile.delete();

        MediaMuxer mux = new MediaMuxer(dstM4AFile.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);

        MediaFormat outputFormat = MediaFormat.createAudioFormat(COMPRESSED_AUDIO_FILE_MIME_TYPE, SAMPLING_RATE, CHANNELS_COUNT);
        outputFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
        outputFormat.setInteger(MediaFormat.KEY_BIT_RATE, COMPRESSED_AUDIO_FILE_BIT_RATE);
        outputFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, BUFFER_SIZE);

        MediaCodec codec = MediaCodec.createEncoderByType(COMPRESSED_AUDIO_FILE_MIME_TYPE);
        codec.configure(outputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        codec.start();

        ByteBuffer[] codecInputBuffers = codec.getInputBuffers(); // Note: Array of buffers
        ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers();
        MediaCodec.BufferInfo outBuffInfo = new MediaCodec.BufferInfo();

        byte[] tempBuffer = new byte[BUFFER_SIZE];
        boolean hasMoreData = true;
        double presentationTimeUs = 0;
        int audioTrackIdx = 0;
        int totalBytesRead = 0;

        do {
            int inputBufIndex = 0;
            while (inputBufIndex != -1 && hasMoreData) {
                inputBufIndex = codec.dequeueInputBuffer(CODEC_TIMEOUT_IN_MS);

                if (inputBufIndex >= 0) {
                    ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
                    dstBuf.clear();

                    int bytesRead = fis.read(tempBuffer, 0, dstBuf.limit());

                    if (bytesRead == -1) { // -1 implies EOS
                        hasMoreData = false;
                        codec.queueInputBuffer(inputBufIndex, 0, 0, (long) presentationTimeUs, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                    } else {
                        totalBytesRead += bytesRead;
                        dstBuf.put(tempBuffer, 0, bytesRead);
                        codec.queueInputBuffer(inputBufIndex, 0, bytesRead, (long) presentationTimeUs, 0);
                        presentationTimeUs = 1000000l * (totalBytesRead / (SAMPLING_RATE/10000)) / SAMPLING_RATE;
                    }
                }
            }
            int outputBufIndex = 0;
            while (outputBufIndex != MediaCodec.INFO_TRY_AGAIN_LATER) {
                outputBufIndex = codec.dequeueOutputBuffer(outBuffInfo, CODEC_TIMEOUT_IN_MS);
                if (outputBufIndex >= 0) {
                    ByteBuffer encodedData = codecOutputBuffers[outputBufIndex];
                    encodedData.position(outBuffInfo.offset);
                    encodedData.limit(outBuffInfo.offset + outBuffInfo.size);
                    if ((outBuffInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0 && outBuffInfo.size != 0) {
                        codec.releaseOutputBuffer(outputBufIndex, false);
                    } else {
                        mux.writeSampleData(audioTrackIdx, codecOutputBuffers[outputBufIndex], outBuffInfo);
                        codec.releaseOutputBuffer(outputBufIndex, false);
                    }
                } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                    outputFormat = codec.getOutputFormat();

                    audioTrackIdx = mux.addTrack(outputFormat);
                    mux.start();
                } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {

                } else if (outputBufIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
                    // NO OP
                } else {

                }
            }

        } while (outBuffInfo.flags != MediaCodec.BUFFER_FLAG_END_OF_STREAM);
        codec.stop();
        codec.release();
        fis.close();
        mux.stop();
        mux.release();

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}`

I hope it's will be helpful to resolve this issue.

Thanks. Serge.

solitaire commented 8 years ago

Hi, Waveform Android is based on the Ringdroid app, but it doesn't use it's newest audio API (last year Ringdroid changed it completely). I can update the library to use the same audio API as the Ringdroid - this will probably solve the issue. Just to be sure, are your using the newest version of the Ringdroid (2.7.3)?

zukopvd commented 8 years ago

Thanks a lot for your reply! Yes, I'am using Ringdroid 2.7.3 - everything works with this version (but it's extremely slow for me). If API update can solve the issue it's will be great if you'll do it!

Here is attached example of audio file that can cause the issue. May be it's will be useful for testing. test_audio.m4a.zip

solitaire commented 8 years ago

Hi, I updated the library - it now works with the file you posted. The code is on a new branch new-audio-api and can be added to your gradle dependencies with compile 'com.github.Semantive:waveform-android:v1.2a'. This solution is far from being perfect - audio decoding makes it slow and memory consuming for larger files.

zukopvd commented 8 years ago

Hello! Yes, new api is too slow. The reason is that source file is decoded to wav format before parsing instead of using CheapMP3 or CheapAAC as at previous api version. May be it's possible to fix CheapAAC class, but I don't really understand how it works currently. Or may be it's time for me to find another solution. Thank you, anyway!