AudioRecorder录音,报E/AudioRecord-JNI: Error -38 during AudioRecord native read

使用AudioRecorder录音,报E/AudioRecord-JNI: Error -38 during AudioRecord native read

img

尝试过:

1:将// private final static int AUDIO_CHANNEL = AudioFormat.CHANNEL_IN_STEREO;改成AudioFormat.CHANNEL_IN_MONO;没用;
2:可能其他应用程序正在占用录制音视频的硬件资源重新启动手机重新运行,没用;
3:权限加了

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />没用

该怎么解决?求指导

public class AudioRecorder {
    //音频输入-麦克风
    private final static int AUDIO_INPUT = MediaRecorder.AudioSource.MIC;
    //采用频率
    //44100是目前的标准,但是某些设备仍然支持22050,16000,11025
    //采样频率一般共分为22.05KHz、44.1KHz、48KHz三个等级
    // private final static int AUDIO_SAMPLE_RATE = 48000;
    private final static int AUDIO_SAMPLE_RATE = 44100;
    //声道
    // private final static int AUDIO_CHANNEL = AudioFormat.CHANNEL_IN_STEREO;
    private final static int AUDIO_CHANNEL = AudioFormat.CHANNEL_IN_MONO;
    //编码
    private final static int AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
    private static final String TAG = "";
    // 缓冲区字节大小
    private int bufferSizeInBytes = 0;

    //录音对象
    private AudioRecord audioRecord;
    //转码器
    private FdkAacEncode fdkAacEnc = null;

    private RtmpSessionManager rtmpSessionMgr = null;

    private int fdkAacHandle = 0;

    //录音状态
    private Status status = Status.STATUS_NO_READY;


    /**
     * 类级的内部类,也就是静态类的成员式内部类,该内部类的实例与外部类的实例
     * 没有绑定关系,而且只有被调用时才会装载,从而实现了延迟加载
     */
    private static class AudioRecorderHolder {
        /**
         * 静态初始化器,由JVM来保证线程安全
         */
        private static final AudioRecorder instance = new AudioRecorder();
    }

    private AudioRecorder() {
    }

    public static AudioRecorder getInstance() {
        return AudioRecorderHolder.instance;
    }


    /**
     * 创建默认的录音对象
     */
    public void createDefaultAudio() {
        // 获得缓冲区字节大小
        bufferSizeInBytes = AudioRecord.getMinBufferSize(AUDIO_SAMPLE_RATE,
                AUDIO_CHANNEL, AUDIO_ENCODING);
        audioRecord = new AudioRecord(AUDIO_INPUT, AUDIO_SAMPLE_RATE,

                AUDIO_CHANNEL, AUDIO_ENCODING, bufferSizeInBytes);
        status = Status.STATUS_READY;
        fdkAacEnc = new FdkAacEncode();
        fdkAacHandle = fdkAacEnc.FdkAacInit(AUDIO_SAMPLE_RATE, 2);
        rtmpSessionMgr = new RtmpSessionManager();
        rtmpSessionMgr.Start("rtmp://124.220.11.79:1932/live/si");
    }


    /**
     * 开始录音
     *
     * @param listener 音频流的监听
     */
    public void startRecord() {

        if (status == Status.STATUS_NO_READY) {
            throw new IllegalStateException("录音尚未初始化,请检查是否禁止了录音权限~");
        }
        if (status == Status.STATUS_START) {
            throw new IllegalStateException("正在录音");
        }
        Log.d("AudioRecorder", "===startRecord===" + audioRecord.getState());
        audioRecord.startRecording();
        new Thread(() -> sendVoiceData()).start();
    }

    private void sendVoiceData() {
        byte[] audioData = new byte[bufferSizeInBytes];
        int readSize;
        //将录音状态设置成正在录音状态
        status = Status.STATUS_START;
        while (status == Status.STATUS_START) {
            readSize = audioRecord.read(audioData, 0, bufferSizeInBytes);
            if (AudioRecord.ERROR_INVALID_OPERATION != readSize && readSize != 0) {
                if (fdkAacHandle != 0) {
                    byte[] aacBuffer = fdkAacEnc.FdkAacEncode(fdkAacHandle, audioData);
                    if (aacBuffer != null) {
                        rtmpSessionMgr.InsertAudioData(aacBuffer);
                    }
                }
            }
        }
    }

    //test,如此拼接?
    private void sendVoiceDataTest() {
        byte[] audioData = new byte[bufferSizeInBytes];
        int bufferCount = 0; // 当前已经拼接的次数
        int bufferSize = 0; // 当前已经拼接的数据长度
        byte[] outputBuffer = new byte[2000]; // 输出缓冲区,假设最多拼接四次,每次输出1000字节

        //将录音状态设置成正在录音状态
        status = Status.STATUS_START;
        while (status == Status.STATUS_START) {
            int readSize = audioRecord.read(audioData, 0, bufferSizeInBytes);
            if (AudioRecord.ERROR_INVALID_OPERATION != readSize && readSize != 0) {
                if (fdkAacHandle != 0) {
                    byte[] aacBuffer = fdkAacEnc.FdkAacEncode(fdkAacHandle, audioData);
                    if (aacBuffer != null) {
                        // 将当前的 aacBuffer 拼接到输出缓冲区中
                        System.arraycopy(aacBuffer, 0, outputBuffer, bufferSize, aacBuffer.length);
                        bufferSize += aacBuffer.length;
                        bufferCount++;
                        // 如果已经拼接了四次,则输出前四次的数据并执行 rtmpSessionMgr.InsertAudioData(aacBuffer)
                        if (bufferCount >= 4) {
                            System.out.println("输出前四次的数据:" + Arrays.toString(outputBuffer));
                            System.out.println(outputBuffer.length);
                            rtmpSessionMgr.InsertAudioData(outputBuffer);
                            // 重置缓冲区和拼接次数和拼接长度
                            bufferCount = 0;
                            bufferSize = 0;
                            outputBuffer = new byte[2000];
                        }
                    }
                }
            }
        }
    }

    /**
     * 停止录音
     */
    public void stopRecord() {
        Log.d("AudioRecorder", "===stopRecord===");
        if (status == Status.STATUS_NO_READY || status == Status.STATUS_READY) {
            throw new IllegalStateException("录音尚未开始");
        } else {
            audioRecord.stop();
            status = Status.STATUS_STOP;
            release();
        }
    }

    /**
     * 释放资源
     */
    public void release() {
        Log.d("AudioRecorder", "===release===");
        //假如有暂停录音
        if (audioRecord != null) {
            audioRecord.release();
            audioRecord = null;

        }
        if (rtmpSessionMgr != null) {
            rtmpSessionMgr.Stop();
        }
        status = Status.STATUS_NO_READY;
    }
}

该错误通常是由于录音的缓冲区大小不足或录音设备被占用而引起的。您可以尝试增加缓冲区大小或释放其他占用录音设备的应用程序。

另外,您可以尝试使用不同的音频源,例如默认的音频源或通话音频源,以查看是否出现相同的错误。您可以使用以下代码更改音频源:



private final static int AUDIO_INPUT = MediaRecorder.AudioSource.DEFAULT; // 默认音频源
// 或
private final static int AUDIO_INPUT = MediaRecorder.AudioSource.VOICE_COMMUNICATION; // 通话音频源
此外,您可以尝试在录音之前检查录音设备是否可用,以避免其他应用程序占用设备。您可以使用以下代码检查录音设备的可用性:


AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
if (audioManager.getMode() == AudioManager.MODE_IN_CALL || audioManager.getMode() == AudioManager.MODE_IN_COMMUNICATION) {
    // 录音设备被占用
} else {
    // 录音设备可用
}

嗯,题意上你已经检查过权限问题,设置问题,那录音设备、录音缓冲区大小,你是否有考虑呢?
录音缓冲区过小:请检查录音缓冲区的大小是否足够大,以确保可以捕获足够的音频数据。
音频设备损坏或配置不当:请检查音频设备是否损坏或配置不当,以确保音频数据能够正常传输。

录音权限设置了的话,可在应用管理、对应应用,权限中再次确认下是否成功开启录音权限,其次,除了录音权限,还需要文件读取和写入权限:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>

另外录音的播放和录制要用线程来做。


明当前MIC被占用,检查系统中是否有其他应用在占用MIC

可以将系统中使用MIC的应用改名验证,但是验证问题后,记得改回来

检查录音格式、采样率参数、通道数是否设置正确

这个问题应该不难吧,可以参考:
https://blog.csdn.net/qq_43556200/article/details/108569409