Android Mediarecorder setNextOutputFile IllegalStateException

标签 android mediarecorder

我正在尝试使用 android mediarecorder 将我的录音拆分为多个文件,但每当我设置下一个输出文件时,我都会遇到 illegalstate 异常。

代码如下:

    private void getMediaRecorderReady(String filePath, FileDescriptor nextFile) {
    bufferSize = 88200;
    recorder = new MediaRecorder();
    recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    recorder.setOutputFormat(MediaRecorder.OutputFormat.AAC_ADTS);
    recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
    recorder.setAudioEncodingBitRate(64000);
    recorder.setAudioSamplingRate(44100);
    recorder.setAudioChannels(1);
    recorder.setOutputFile(filePath);
    recorder.setMaxDuration(1000);
    recorder.setMaxFileSize(100000);
    try {
        recorder.prepare();
        recorder.setNextOutputFile(nextFile);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

这里是错误:

E/MediaRecorder: setNextOutputFile failed: -38
E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #1
              Process: com.example.a49164.teachmi, PID: 31554
              java.lang.RuntimeException: An error occurred while executing doInBackground()
                  at android.os.AsyncTask$3.done(AsyncTask.java:353)
                  at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
                  at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
                  at java.util.concurrent.FutureTask.run(FutureTask.java:271)
                  at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
                  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
                  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
                  at java.lang.Thread.run(Thread.java:764)
               Caused by: java.lang.IllegalStateException
                  at android.media.MediaRecorder._setNextOutputFile(Native Method)
                  at android.media.MediaRecorder.setNextOutputFile(MediaRecorder.java:878)
                  at com.example.a49164.teachmi.ExComThread.getMediaRecorderReady(ExComThread.java:189)
                  at com.example.a49164.teachmi.ExComThread.soundTransfer(ExComThread.java:211)
                  at com.example.a49164.teachmi.ExComThread.doInBackground(ExComThread.java:82)
                  at com.example.a49164.teachmi.ExComThread.doInBackground(ExComThread.java:46)
                  at android.os.AsyncTask$2.call(AsyncTask.java:333)
                  at java.util.concurrent.FutureTask.run(FutureTask.java:266)
                  at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245) 
                  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162) 
                  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636) 
                  at java.lang.Thread.run(Thread.java:764) 

所以,在做了一些挖掘之后,错误 -38 等于“-ENOSYS”或“INVALID_OPERATION”,这似乎发生在记录器为空或未调用 prepare 的情况下(但我确实先调用了 prepare() ).

这里是一些原生的android代码:

enum {
OK                = 0,    // Everything's swell.
NO_ERROR          = 0,    // No errors.

UNKNOWN_ERROR       = 0x80000000,
NO_MEMORY           = -ENOMEM,
INVALID_OPERATION   = -ENOSYS,
BAD_VALUE           = -EINVAL,
BAD_TYPE            = 0x80000001,
NAME_NOT_FOUND      = -ENOENT,
PERMISSION_DENIED   = -EPERM,
NO_INIT             = -ENODEV,
ALREADY_EXISTS      = -EEXIST,
DEAD_OBJECT         = -EPIPE,
FAILED_TRANSACTION  = 0x80000002,
JPARKS_BROKE_IT     = -EPIPE,
#if !defined(HAVE_MS_C_RUNTIME)
BAD_INDEX           = -EOVERFLOW,
NOT_ENOUGH_DATA     = -ENODATA,
WOULD_BLOCK         = -EWOULDBLOCK, 
TIMED_OUT           = -ETIMEDOUT,
UNKNOWN_TRANSACTION = -EBADMSG,
#else    
BAD_INDEX           = -E2BIG,
NOT_ENOUGH_DATA     = 0x80000003,
WOULD_BLOCK         = 0x80000004,
TIMED_OUT           = 0x80000005,
UNKNOWN_TRANSACTION = 0x80000006,
#endif    
FDS_NOT_ALLOWED     = 0x80000007,
};

native 下一个输出文件:

status_t MediaRecorder::setNextOutputFile(int fd)
{
ALOGV("setNextOutputFile(%d)", fd);
if (mMediaRecorder == NULL) {
    ALOGE("media recorder is not initialized yet");
    return INVALID_OPERATION;
}
// It appears that if an invalid file descriptor is passed through
// binder calls, the server-side of the inter-process function call
// is skipped. As a result, the check at the server-side to catch
// the invalid file descritpor never gets invoked. This is to workaround
// this issue by checking the file descriptor first before passing
// it through binder call.
int flags = fcntl(fd, F_GETFL);
if (flags == -1) {
    ALOGE("Fail to get File Status Flags err: %s", strerror(errno));
}
// fd must be in read-write mode or write-only mode.
if ((flags & (O_RDWR | O_WRONLY)) == 0) {
    ALOGE("File descriptor is not in read-write mode or write-only mode");
    return BAD_VALUE;
}
status_t ret = mMediaRecorder->setNextOutputFile(fd);
if (OK != ret) {
    ALOGE("setNextOutputFile failed: %d", ret);
}
return ret;
}

我可能对 -38 等于 -ENOSYS 这一事实有误,但我仍然不知道为什么它不起作用,因为我正在传递一个文件描述符并在准备后调用它,如 android 文档中所述.

非常欢迎您的帮助,谢谢!

编辑:我试图在准备后立即删除 setNextOutputFile(nextFile),只保留一个:

    recorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {
        @Override
        public void onInfo(MediaRecorder mr, int infoCode, int extra) {
            System.out.println("----------------------------------------onMaxFileSizeApproaching " + infoCode);
            if (infoCode == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
                System.out.println("max sized reached");
                String path = Environment.getExternalStorageDirectory().getPath() + "/dataSound" + currFile + ".aac";
                File nextFile = new File(path);
                try {
                    FileOutputStream fos = new FileOutputStream(nextFile);
                    recorder.setNextOutputFile(fos.getFD());
                } catch (IOException e) {
                    e.printStackTrace();
                }
                String currPath = Environment.getExternalStorageDirectory().getPath() + "/dataSound" + (currFile - 1) + ".aac";
                sendDataPath = currPath;
                currFile++;
            }
        }
    });

但是没有用...

最佳答案

这意味着 setNextOutputFile()prepare() 之前被调用 finish it's own work .

所以你应该等到你的文件按照documentation中的讨论准备好.

在切换到该输出之前不会使用该文件。申请会收到MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED

您应该像这样收听文件信息:

recorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {
        @Override
        public void onInfo(MediaRecorder mediaRecorder, int what, int extra) {
            if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED)
                recorder.setNextOutputFile(nextFile);
        }
    });

还有在使用下一个输出文件时。如果之前的输出文件未被使用,应用程序将无法设置新的输出文件。调用 stop() 后,应用程序负责清理未使用的文件。

关于Android Mediarecorder setNextOutputFile IllegalStateException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51127260/

相关文章:

android - Kotlin 合成 : Referencing views with the same ids in several layouts with dynamic inflating

java - 来自 url 的 MediaPlayer 流

java - 如何在没有 xml 的情况下向条形图添加按钮?

python - 将录制的音频从浏览器持久保存为WAV文件到Python API

android - 使用 MediaRecorder 类时如何更改帧率

android - 如何在 Eclipse 中过滤消息到 Logcat?

android - BLE - 我无法同时读取 2 个特性,一个是温度服务,另一个是电池服务

Android 计时器似乎忽略了 .cancel

android - 使用MediaRecorder android时如何在前后摄像头之间切换

android - MediaRecorder 不将音频保存到文件