录制音频时出现Android SIGSEGV错误

Posted

技术标签:

【中文标题】录制音频时出现Android SIGSEGV错误【英文标题】:Android SIGSEGV error when recording audio 【发布时间】:2012-06-30 13:13:58 【问题描述】:

我需要你的帮助。我正在尝试使用 androids AudioRecord 类录制一些音频。 大多数情况下这工作得很好,但有时我会收到 SIGSEGV 错误。

这是在录制音频时在单独的线程中运行的代码:

private void startRecording() 
    recorder = new AudioRecord(MediaRecorder.Audiosource.MIC,
            RECORDER_SAMPLERATE, RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING, bufferSize);

    recorder.startRecording();

    isRecording = true;

    recordingThread = new Thread(new Runnable() 

        @Override
        public void run() 
            writeAudioDataToFile();
        
    , "AudioRecorder Thread");

    recordingThread.start();


private void writeAudioDataToFile() 
    byte data[] = new byte[bufferSize];
    String filename = getTempFilename();
    FileOutputStream os = null;

    try 
        os = new FileOutputStream(filename);
     catch (FileNotFoundException e) 
        e.printStackTrace();
    

    int read = 0;

    if (null != os) 
        while (isRecording) 
            read = recorder.read(data, 0, bufferSize);

            if (AudioRecord.ERROR_INVALID_OPERATION != read) 
                try 
                    os.write(data);
                 catch (IOException e) 
                    e.printStackTrace();
                
            
        

        try 
            os.close();
            os.flush();
         catch (IOException e) 
            e.printStackTrace();
        
    

当录制停止时,我会这样做:

public void stopRecording() 
    if (null != recorder) 
        isRecording = false;
        mRecordingBlocked = true;
        mProgressDialog = RecorderDialog.newInstance();
        mProgressDialog.show(getFragmentManager(), "progressDialog");

        recorder.stop();
        recorder.release();

        recorder = null;
        recordingThread = null;

        new Thread(new Runnable() 
            @Override
            public void run() 
                String fileName = getFilename();
                copyWaveFile(getTempFilename(), fileName);

                final TelephonyManager tm = (TelephonyManager) getActivity().getBaseContext().getSystemService(Context.TELEPHONY_SERVICE);

                final String tmDevice, tmSerial, androidId;
                tmDevice = "" + tm.getDeviceId();
                tmSerial = "" + tm.getSimSerialNumber();
                androidId = "" + android.provider.Settings.Secure.getString(getActivity().getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);

                UUID deviceUuid = new UUID(androidId.hashCode(), ((long) tmDevice.hashCode() << 32) | tmSerial.hashCode());
                String deviceId = deviceUuid.toString();
                App.db.insertEntry(deviceId, "Song title", fileName);

                deleteTempFile();

                if (cbox_overdub.isChecked()) 
                    phrases.add(playAudio(fileName, true));
                
            
        ).start();
    



private void copyWaveFile(String inFilename, String outFilename) 
    FileInputStream in;
    FileOutputStream out;
    long totalAudioLen = 0;
    long totalDataLen;
    long longSampleRate = RECORDER_SAMPLERATE;
    int channels = 2;
    long byteRate = RECORDER_BPP * RECORDER_SAMPLERATE * channels / 8;

    try 
        AppLog.logString("begin copyWaveFile try");
        File file = new File(inFilename);
        in = new FileInputStream(file);
        byte[] bytes = new byte[(int) file.length()];
        totalAudioLen = in.getChannel().size();
        totalDataLen = totalAudioLen + 36;
        in.read(bytes);
        in.close();

        out = new FileOutputStream(outFilename);
        AppLog.logString("fos created");

        WriteWaveFileHeader(out, totalAudioLen, totalDataLen,
                longSampleRate, channels, byteRate);
        AppLog.logString("wave header written");

        int bufferLength = 1024;
        publishProgress(0);
        for (int i = 0; i < bytes.length; i += bufferLength) 
            int progress = (int) ((i / (float) bytes.length) * 100);
            publishProgress(progress);
            if (bytes.length - i >= bufferLength) 
                out.write(bytes, i, bufferLength);
             else 
                out.write(bytes, i, bytes.length - i);
            
        
        publishProgress(100);
        AppLog.logString("progress complete");
        mRecordingBlocked = false;
        mProgressDialog.dismiss();

        out.close();
        out.flush();
     catch (FileNotFoundException e) 
        e.printStackTrace();
     catch (IOException e) 
        e.printStackTrace();
    

这个错误似乎发生在 AudioRecord 的实例已经停止之后。然后我将临时 temp_record.wav(SD 卡)的内容复制到目标(SD 卡),并设置适当的波形文件头。

我无法从以下调试信息中过滤出任何有用的信息。我希望有人有一个想法。谢谢!

这是 Logcat 输出:

06-30 14:47:53.311: INFO/DEBUG(9381): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
06-30 14:47:53.311: INFO/DEBUG(9381): Build fingerprint: 'otorola/RTGB/umts_milestone2:2.3.4/MILS2_U6_4.1-22/1317097892:user/release-keys'
06-30 14:47:53.311: INFO/DEBUG(9381): pid: 9459, tid: 9525  >>> de.intermeco.android.apps.ilaugh <<<
06-30 14:47:53.311: INFO/DEBUG(9381): signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 4640e000
06-30 14:47:53.311: INFO/DEBUG(9381): r0 4640e000  r1 00000000  r2 fffffd49  r3 4670da18
06-30 14:47:53.311: INFO/DEBUG(9381): r4 4640e000  r5 4640e000  r6 fffffd49  r7 4670da18
06-30 14:47:53.311: INFO/DEBUG(9381): r8 a904cf6a  r9 0000000a  10 a904cfad  fp a904cff0
06-30 14:47:53.311: INFO/DEBUG(9381): ip a9060074  sp 4670d9e0  lr afd11704  pc afd113dc  cpsr 20000050
06-30 14:47:53.311: INFO/DEBUG(9381): d0  6472656767756265  d1  0037fff00020000c
06-30 14:47:53.311: INFO/DEBUG(9381): d2  fff5ffd8fff5ffdb  d3  ffe0ffd8ffd9ffd8
06-30 14:47:53.311: INFO/DEBUG(9381): d4  fff2ffe50008ffd4  d5  ffdafffafff20000
06-30 14:47:53.311: INFO/DEBUG(9381): d6  fff40003ffcf0003  d7  ffe30013ffe1fffd
06-30 14:47:53.311: INFO/DEBUG(9381): d8  0000000000989680  d9  0000000000000000
06-30 14:47:53.311: INFO/DEBUG(9381): d10 0000000000000000  d11 0000000000000000
06-30 14:47:53.311: INFO/DEBUG(9381): d12 0000000000000000  d13 0000000000000000
06-30 14:47:53.311: INFO/DEBUG(9381): d14 0000000000000000  d15 0000000000000000
06-30 14:47:53.311: INFO/DEBUG(9381): d16 000000c24003a7e0  d17 4000000000000000
06-30 14:47:53.311: INFO/DEBUG(9381): d18 3ff0000000000000  d19 0000000000000000
06-30 14:47:53.311: INFO/DEBUG(9381): d20 b96377ce858a5d48  d21 3929f5135cb87c55
06-30 14:47:53.311: INFO/DEBUG(9381): d22 3e21ee9ebdb4b1c4  d23 bda8fae9be8838d4
06-30 14:47:53.319: INFO/DEBUG(9381): d24 0000000000000000  d25 0000000000000000
06-30 14:47:53.319: INFO/DEBUG(9381): d26 0000000000000000  d27 ffffffffffffffff
06-30 14:47:53.319: INFO/DEBUG(9381): d28 0100010001000100  d29 0100010001000100
06-30 14:47:53.319: INFO/DEBUG(9381): d30 4086800000000000  d31 3ff0000000000000
06-30 14:47:53.319: INFO/DEBUG(9381): scr 60000010
06-30 14:47:53.483: INFO/DEBUG(9381): #00  pc 000113dc  /system/lib/libc.so (pthread_mutex_lock)
06-30 14:47:53.483: INFO/DEBUG(9381): #01  pc 00011700  /system/lib/libc.so (__pthread_cond_timedwait_relative)
06-30 14:47:53.483: INFO/DEBUG(9381): #02  pc 0002d658  /system/lib/libmedia.so
06-30 14:47:53.483: INFO/DEBUG(9381): code around pc:
06-30 14:47:53.483: INFO/DEBUG(9381): afd113bc e3a02001 ebfffe82 e1a00005 e8bd87f0
06-30 14:47:53.483: INFO/DEBUG(9381): afd113cc 00036024 e92d47f0 e2504000 0a000019
06-30 14:47:53.483: INFO/DEBUG(9381): afd113dc e5946000 e5947000 e2166903 1a000017
06-30 14:47:53.483: INFO/DEBUG(9381): afd113ec e5945000 e1a02004 e2055a02 e1a00005
06-30 14:47:53.483: INFO/DEBUG(9381): afd113fc e3851001 ebffed7f e3500000 13856002
06-30 14:47:53.483: INFO/DEBUG(9381): code around lr:
06-30 14:47:53.483: INFO/DEBUG(9381): afd116e4 e1a03007 e1a02006 e2011001 e1a00004
06-30 14:47:53.483: INFO/DEBUG(9381): afd116f4 ebfffd9a e1a04000 e1a00005 ebffff32
06-30 14:47:53.483: INFO/DEBUG(9381): afd11704 e374006e 03a0006e 13a00000 e8bd81f0
06-30 14:47:53.483: INFO/DEBUG(9381): afd11714 e304cdd3 e3043240 e92d4010 e341c062
06-30 14:47:53.483: INFO/DEBUG(9381): afd11724 e1a0e002 e24dd008 e340300f e1a0200d
06-30 14:47:53.483: INFO/DEBUG(9381): stack:
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9a0  04000804
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9a4  00000001
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9a8  001c6b58
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9ac  00000262
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9b0  0000ee6b
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9b4  afd0fe34  /system/lib/libc.so
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9b8  001edbb0
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9bc  afd13bc1  /system/lib/libc.so
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9c0  001b0470
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9c4  001c6b58
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9c8  00000241
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9cc  3b9aca00
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9d0  00000000
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9d4  00989680
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9d8  df002777
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9dc  e3a070ad
06-30 14:47:53.483: INFO/DEBUG(9381): #00 4670d9e0  00000000
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9e4  4640e000
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9e8  fffffd49
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9ec  4670da18
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9f0  a904cf6a  /system/lib/libmedia.so
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9f4  0000000a
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9f8  a904cfad  /system/lib/libmedia.so
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9fc  afd11704  /system/lib/libc.so
06-30 14:47:53.483: INFO/DEBUG(9381): #01 4670da00  4640e000
06-30 14:47:53.483: INFO/DEBUG(9381): 4670da04  4640e004
06-30 14:47:53.483: INFO/DEBUG(9381): 4670da08  4670da98
06-30 14:47:53.483: INFO/DEBUG(9381): 4670da0c  000000c8
06-30 14:47:53.483: INFO/DEBUG(9381): 4670da10  a904cf6a  /system/lib/libmedia.so
06-30 14:47:53.483: INFO/DEBUG(9381): 4670da14  a902d65b  /system/lib/libmedia.so

【问题讨论】:

嗨,克里斯,我认为无法提供这些信息。调试日志不显示任何提示。你能重现错误吗?你能提供任何失败的代码吗?我有一种感觉,这是某种异步问题。 该错误时有发生,但无法通过特殊的“待办事项列表”重现。有时错误发生在第一条记录上,有时出现在很多条记录之后。我认为您可能是对的,这是一个异步问题,因为我正在使用单独的线程将音频写入文件。我将尝试更深入地研究这部分代码。 【参考方案1】:

感谢您再次指出 SIGSEV,因为我不知道它的含义,所以我读了一点。现在我做:) 我同意 CommonsWare 的意见,您应该通知固件的制造商/修改者。 但我也认为它不会在有用的时间跨度内进一步帮助你。也许我们会找到一种方法来修改您的代码,从而不再引发错误。

一般来说,您的代码看起来相当不错。我认为不完全安全和正确的是您停止录音机的方式。

    我不使用 isRecording 标志,而是使用 isCancelled 标志。所以你记录 while(!isCancelled)。当您想停止录制时,只需将 isCancelled 设置为 true 并调用 recorder.stop()。 录音停止后,您可以在录音机线程中的 writeAudioDataToFile() 之后立即调用您的逻辑来显示进度对话框和复制文件。我不会为这些事情使用额外的线程,因为我们仍在录音机线程中,并且您的 UI 没有被阻止。 何时调用 recorder.release(),它会释放内存...文档“释放本机 AudioRecord 资源”。我不知道这是否对问题有影响。在您提供的当前代码中,您调用 recorder.release() 而理论上记录器可以读取(不知道该类是否是线程安全的)。也许您也可以在复制所有文件后尝试调用它,即使我认为在记录器真正停止并且您的输出流关闭后应该没问题。

希望我能帮上一点忙,你能找到办法。

【讨论】:

【参考方案2】:

感谢您的建议。 我稍微重建了代码,现在它似乎可以工作了。 至少我大约一天没有收到错误(比以前长:D)。

我更改了 writeAudioDataToFile() 方法,因此现在发布等也在那里完成(在 stopRecording() 方法中将 isRecording 设置为 false 之后)。我也不再创建第二个线程,而是在 RecorderThread 中处理所有事情。

所以现在这似乎对我有用:

private void writeAudioDataToFile() 
    byte data[] = new byte[bufferSize];
    String filename = getTempFilename();
    FileOutputStream os = null;

    try 
        os = new FileOutputStream(filename);
     catch (FileNotFoundException e) 
        e.printStackTrace();
    

    int read = 0;

    if (null != os) 
        while (isRecording) 
            read = recorder.read(data, 0, bufferSize);

            if (AudioRecord.ERROR_INVALID_OPERATION != read) 
                try 
                    os.write(data);
                 catch (IOException e) 
                    e.printStackTrace();
                
            
        

        try 
            os.close();
            os.flush();
         catch (IOException e) 
            e.printStackTrace();
        

        recorder.stop();
        recorder.release();
        recorder = null;

        String fileName = getFilename();
        copyWaveFile(getTempFilename(), fileName);

        final TelephonyManager tm = (TelephonyManager) getActivity().getBaseContext().getSystemService(Context.TELEPHONY_SERVICE);

        final String tmDevice, tmSerial, androidId;
        tmDevice = "" + tm.getDeviceId();
        tmSerial = "" + tm.getSimSerialNumber();
        androidId = "" + android.provider.Settings.Secure.getString(getActivity().getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);

        UUID deviceUuid = new UUID(androidId.hashCode(), ((long) tmDevice.hashCode() << 32) | tmSerial.hashCode());
        String deviceId = deviceUuid.toString();
        App.db.insertEntry(deviceId, "Song title", fileName);

        deleteTempFile();

        if (cbox_overdub.isChecked()) 
            phrases.add(playAudio(fileName, true));
        
    


public void stopRecording() 
    if (null != recorder) 
        Toast.makeText(getActivity(), "Stop Recording", Toast.LENGTH_SHORT).show();
        ((ImageView) getActivity().findViewById(R.id.btnMic)).setImageDrawable(getActivity().getResources().getDrawable(R.drawable.btn_mic));
        isRecording = false;
        mRecordingBlocked = true;

        mProgressDialog = RecorderDialog.newInstance();
        mProgressDialog.show(getFragmentManager(), "progressDialog");
    


private void publishProgress(int progress) 
    ProgressDialog pd = ((ProgressDialog) mProgressDialog.getDialog());
    if (pd != null) pd.setProgress(progress);

谢谢你!

【讨论】:

太好了,现在这看起来是线程安全的 :)。大声笑我已经想提上一篇文章了:D。你知道我们怎么称呼它为“if (null != recorder) ”吗?尤达条件:D 大声笑...同意 - 可能有点不寻常:D【参考方案3】:

SIGSEGV 不应该来自 Java 代码。除非这是您的 JNI 代码,否则这表明固件存在缺陷。

如果您正在运行修改后的固件,请将此信息传递给固件修改者。

如果您正在运行设备随附的原始固件,并且这只发生在一台设备上,您可以尝试联系设备制造商。

如果您在具有原始固件的多个设备上看到此问题,并且您可以创建一个重现错误的示例项目,请将项目和堆栈跟踪发布到 the Android issue tracker,假设该问题尚未在此处报告。

【讨论】:

你是对的。我正在使用修改后的固件(Cyanogenmod 7)。如果我发现这不是我的源代码的问题,而是系统的问题,我会按照你的想法去做。【参考方案4】:

文档说明:

为了接收与这些相关的相应回调 监听器,应用程序需要创建 MediaRecorder 对象 在 Looper 运行的线程上(默认情况下是主 UI 线程 已经有一个 Looper 正在运行)。

确保在 UI 线程上创建记录器。或许也可以在 UI 线程上调用它的方法。

【讨论】:

【参考方案5】:

以上解决方案都不适合我。相反,我在 AudioRecord 的构造函数中增加了缓冲区。

根据参数和设备,最小缓冲区大小建议在 1k 到 4k 之间。看来我的算法太慢了,无法在缓冲区满之前从缓冲区中读取数据,所以我得到了 segv(老实说,我只是在猜测后果)。

在我将缓冲区的值提高到两倍于我需要来自 AudioRecord 的数据之后,它就像一个魅力。

            bufferSize = AudioRecord.getMinBufferSize(sampleAudioBitRate,
                    AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);

            if (bufferSize < sampleSize * 2)
                bufferSize = sampleSize * 2;

            audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleAudioBitRate,
                    AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
            if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) 
                Log.w(TAG, "audioRecord not initialized");
                return;
            

【讨论】:

以上是关于录制音频时出现Android SIGSEGV错误的主要内容,如果未能解决你的问题,请参考以下文章

在 Android 2.2 中从麦克风录制时出现奇怪的失真

Camtasia录制时出现video codec open failed错误的解决方法

音频录制和播放环回缓冲区问题,将数据写入音轨时出现问题

尝试将音频文件上传到服务器时出现Android问题

写入时出现SIGSEGV分段错误,但不读取?

在 J2me 中录制音频时出现异常