低版本的MediaRecorder支持暂停

一.场景

如果android api小于24,MediaRecorder不支持暂停功能,如果想实现暂停功能,要借用ffmpeg的相关功能。

二.目的

实现一个兼容新老版本的MediaRecorder

三.UML图

低版本的MediaRecorder支持暂停_第1张图片
image.png

3.1 接口说明
抽象出接口MediaRecordInterface
具有start,pause,resume,release等抽象方法

低版本的MediaRecorder支持暂停_第2张图片
MediaRecordInterface接口

ContactAudioListener接口用于监听临时录音文件的开始拼接和结束拼接


ContactAudioListener

3.2 MediaRecordNew
因为新版api中,MediaRecorder具有暂停功能,所以直接使用系统自带的api即可

3.4 MedailRecordNew
因为旧版api中,MediaRecorder不具有暂停功能,所以要使用ffmpeg
实现概讲

3.4.1 pauseRecord
此方法调用MediaRecorder的stop方法,将录音存储到文件

3.4.2 resumeRecord
此方法调用MediaRecorder的start方法,重新开始录音

3.4.3 stopRecord
此方法将stopRecord产生的临时文件,利用ffmpeg拼接在一起

四.代码实现

4.1 MediaRecordInterface

public interface MediaRecordInterface {

    int CONTACT_SUCCESS =0;
    int CONTACT_FAIL=-1;

    interface ContactAudioListener {
        void onBegin();

        void onEnd(int contactRes, File recordFile);
    }


    void setContactAudioListener(ContactAudioListener contactAudioListener);

    /**
     * 创建默认的录音对象
     * 

* 文件名的格式为 自己需要的目录/文件名(文件名不要写后缀) * * @param fileName 文件名 */ void createAudio(String parentDir, String fileName); /** * 开始录音,与stopRecord相对应 */ void startRecord(); /** * 停止录音,输出文件与startRecord相对应 */ void stopRecord(); /** * 暂停录音,与resumeRecord配合使用 */ void pauseRecord(); /** * 重新开始录音,与pauseRecord配合使用 */ void resumeRecord(); /** * 1.释放硬件资源 */ void releaseResourceAndTemp(); }

4.2 MediaRecordNew

@TargetApi(24)
public class MediaRecordNew implements MediaRecordInterface {
    private static final String TAG = "MediaRecordNew";
    private File mRecDir;
    private MediaRecorder mMediaRecorder;
    private String mResFilePath;//最终的录音文件路径 如 /record/temp.m4a
    private ContactAudioListener mContactAudioListener;

    @Override
    public void setContactAudioListener(ContactAudioListener contactAudioListener) {
        mContactAudioListener = contactAudioListener;
    }

    /**
     * 创建默认的录音对象
     * 

* 文件名的格式为 自己需要的目录/文件名(文件名不要写后缀) * * @param fileName 文件名 */ @Override public void createAudio(String parentDir, String fileName) { mRecDir = new File(parentDir); if (!mRecDir.exists()) { mRecDir.mkdirs(); } mResFilePath = mRecDir.getAbsolutePath() + File.separator + fileName + ".m4a"; clearTempFile(); } /** * 开始录音 */ @Override public void startRecord() { // 开始录音 /* ①Initial:实例化MediaRecorder对象 */ if (mMediaRecorder == null) { mMediaRecorder = new MediaRecorder(); } try { /* ②setAudioSource/setVedioSource */ mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION);// 设置麦克风 mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); /* * ②设置输出文件的格式:THREE_GPP/MPEG-4/RAW_AMR/Default THREE_GPP(3gp格式 * ,H263视频/ARM音频编码)、MPEG-4、RAW_AMR(只支持音频且音频编码要求为AMR_NB) */ /* ②设置音频文件的编码:AAC/AMR_NB/AMR_MB/Default 声音的(波形)的采样 */ mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); /* ③准备 */ mMediaRecorder.setOutputFile(mResFilePath); mMediaRecorder.prepare(); /* ④开始 */ mMediaRecorder.start(); } catch (IllegalStateException e) { Log.i(TAG, "call startAmr(File mRecAudioFile) failed!" + e.getMessage()); } catch (IOException e) { Log.i(TAG, "call startAmr(File mRecAudioFile) failed!" + e.getMessage()); } } /** * 暂停录音,与resumeRecord配合使用 */ @Override public void pauseRecord() { try { if (mMediaRecorder != null) { mMediaRecorder.pause(); } } catch (IllegalStateException e) { Log.e(TAG, "pauseRecord: " + ExceptionUtil.collectionExceptionInfo(e)); } catch (RuntimeException e) { Log.e(TAG, "pauseRecord: " + ExceptionUtil.collectionExceptionInfo(e)); } } /** * 重新开始录音,与pauseRecord配合使用 */ @Override public void resumeRecord() { if (mMediaRecorder != null) { try { mMediaRecorder.resume(); }catch (IllegalStateException e){ Log.e(TAG, "resumeRecord" ); } } } /** * 停止录音,调用后开始合并 */ @Override public void stopRecord() { if (mMediaRecorder != null) { try { mMediaRecorder.stop(); mMediaRecorder.release(); mMediaRecorder = null; } catch (IllegalStateException e) { Log.e(TAG, "stopRecord: " + ExceptionUtil.collectionExceptionInfo(e)); } catch (RuntimeException e) { Log.e(TAG, "stopRecord: " + ExceptionUtil.collectionExceptionInfo(e)); } } startContact(); } /** * sdk>=24因为使用的是MediarPlayer.pause方法,不会生产多个文件,所以不用拼接 */ private void startContact() { MediaMetadataRetriever retriever = new MediaMetadataRetriever(); File resFile = new File(mResFilePath); mContactAudioListener.onBegin(); if (mContactAudioListener != null) { if (MediaMetadataUtil.isDamaged(retriever, mResFilePath)) { mContactAudioListener.onEnd(CONTACT_FAIL, resFile); } else { mContactAudioListener.onEnd(CONTACT_SUCCESS, resFile); } } } /** * 1.释放硬件资源 */ @Override public void releaseResourceAndTemp() { if (mMediaRecorder != null) { try { mMediaRecorder.stop(); mMediaRecorder.release(); } catch (IllegalStateException illegalStateException) { LogProxy.e(TAG, "releaseResourceAndTemp: " + illegalStateException.getMessage()); }catch (RuntimeException runtimeException){ LogProxy.e(TAG, "releaseResourceAndTemp: " + ExceptionUtil.collectionExceptionInfo(runtimeException)); } mMediaRecorder = null; } clearTempFile(); } /** * 清空录音文件夹 */ private void clearTempFile() { if (mRecDir == null) { return; } File[] files = mRecDir.listFiles(); if (files == null) { return; } for (File recordFile : files) { recordFile.delete(); } } }

4.3 MediaRecordOld

public class MediaRecordOld implements MediaRecordInterface {
    private static final String TAG = "MediaRecordOld";
    private File mRecDir;
    private MediaRecorder mMediaRecorder;
    private List mTempFileNames = new ArrayList<>();
    private String mFileName;//录音文件名 如temp
    private String mResFilePath;//最终的录音文件路径 如 /record/temp.m4a
    private MediaRecordInterface.ContactAudioListener mContactAudioListener;

    @Override
    public void setContactAudioListener(MediaRecordInterface.ContactAudioListener contactAudioListener) {
        mContactAudioListener = contactAudioListener;
    }



    @Override
    public void createAudio(String parentDir, String fileName) {
        mRecDir = new File(parentDir);
        if (!mRecDir.exists()) {
            mRecDir.mkdirs();
        }
        mFileName = fileName;
        mResFilePath = mRecDir.getAbsolutePath() + File.separator + mFileName + ".m4a";
        clearTempFile();
    }


    @Override
    public void startRecord() {
        // 开始录音
        /* ①Initial:实例化MediaRecorder对象 */
        if (mMediaRecorder == null) {
            mMediaRecorder = new MediaRecorder();
        }
        try {

            /* ②setAudioSource/setVedioSource */
            mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION);// 设置麦克风
            mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
            /*
             * ②设置输出文件的格式:THREE_GPP/MPEG-4/RAW_AMR/Default THREE_GPP(3gp格式
             * ,H263视频/ARM音频编码)、MPEG-4、RAW_AMR(只支持音频且音频编码要求为AMR_NB)
             */
            /* ②设置音频文件的编码:AAC/AMR_NB/AMR_MB/Default 声音的(波形)的采样 */
            mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);

            String tempFilePath = mRecDir + File.separator + mFileName + "" + mTempFileNames.size() + ".m4a";
            mTempFileNames.add(tempFilePath);
            /* ③准备 */
            mMediaRecorder.setOutputFile(tempFilePath);
            mMediaRecorder.prepare();
            /* ④开始 */
            mMediaRecorder.start();
        } catch (IllegalStateException e) {
            Log.e(TAG, "call startAmr(File mRecAudioFile) failed!" + e.getMessage());
        } catch (IOException e) {
            Log.e(TAG, "call startAmr(File mRecAudioFile) failed!" + e.getMessage());
        } catch (RuntimeException e){
            Log.e(TAG, "call startAmr(File mRecAudioFile) failed!" + e.getMessage());
        }
    }


    @Override
    public void pauseRecord() {
        try {
            mMediaRecorder.stop();
        } catch (IllegalStateException e) {
            Log.e(TAG, "pauseRecord: " +  ExceptionUtil.collectionExceptionInfo(e));
        } catch (RuntimeException e) {
            Log.e(TAG, "pauseRecord: " +  ExceptionUtil.collectionExceptionInfo(e));
        }
    }


    @Override
    public void resumeRecord(){
        startRecord();//
    }



    @Override
    public void stopRecord() {
        if (mMediaRecorder != null) {
            try {
                mMediaRecorder.stop();
                mMediaRecorder.release();
                mMediaRecorder = null;
            } catch (IllegalStateException e) {
                Log.e(TAG, "stopRecord: " + ExceptionUtil.collectionExceptionInfo(e));
            } catch (RuntimeException e) {
                Log.e(TAG, "stopRecord: " +  ExceptionUtil.collectionExceptionInfo(e));
            }
        }
        startContact();
    }

    /**
     * 开始拼接
     * ffmpeg的拼接音频命令
     * ffmpeg -i file1.mp3 -i file2.mp3 -filter_complex[0:0] [1:0] concat=n=2:v=0:a=1 [a]-map [a] des.file
     */
    private void startContact() {

        MediaMetadataRetriever retriever = new MediaMetadataRetriever();

        Iterator iterator = mTempFileNames.iterator();

        //检察音频文件是否存在异常
        while (iterator.hasNext()) {
            String checkedFilePath = iterator.next();
            File checkedFile = new File(checkedFilePath);
            if (!checkedFile.exists()) {
                Log.e(TAG, "startConcat " + checkedFile + " is not exit ");
                mContactAudioListener.onEnd(-1, null);
                Application instance = ApplicationProxy.getAppInstance();
                ToastUtils.show(instance, instance.getResources().getString(R.string.lib_file_lost));
                return;
            }

            if (MediaMetadataUtil.isDamaged(retriever, checkedFilePath)) {
                iterator.remove();
                checkedFile.delete();
                LogProxy.e(TAG, checkedFilePath + " is damaged,discard it");
            }
        }


        if (mTempFileNames.size()==0){
            mContactAudioListener.onEnd(-1, null);
            Application instance = ApplicationProxy.getAppInstance();
            ToastUtils.show(instance, instance.getResources().getString(R.string.lib_file_damaged));
            LogProxy.e(TAG,  "all file is damaged");
            return;
        }

        String concatCmd1 = "ffmpeg";
        for (String concatFile : mTempFileNames) {
            concatCmd1 += " -i " + concatFile;
            Log.i(TAG, "concat file : " + concatFile);
        }
        concatCmd1 += " -filter_complex";

        String concatCmd2 = "";
        for (int i = 0; i < mTempFileNames.size(); i++) {
            concatCmd2 += "[" + i + ":0] ";
        }
        concatCmd2 += "concat=n=" + mTempFileNames.size() + ":v=0:a=1 [a]";
        String concatCmd3 = "-map [a] " + mResFilePath;

        LogProxy.i(TAG, "cmd is " + concatCmd1 + concatCmd2 + concatCmd3);

        ArrayList arrs = new ArrayList<>();
        arrs.addAll(Arrays.asList(concatCmd1.split(" ")));
        arrs.add(concatCmd2);
        arrs.addAll(Arrays.asList(concatCmd3.split(" ")));
        String[] cmds = new String[arrs.size()];
        int index = 0;
        for (String ele : arrs) {
            cmds[index++] = ele;
        }

        FFmpegCmd.execute(cmds, new FFmpegCmd.OnHandleListener() {
            @Override
            public void onBegin() {
                Log.i(TAG, "startConcat");
                if (mContactAudioListener != null) {
                 Observable.just(1)
                            .observeOn(AndroidSchedulers.mainThread())
                            .subscribe(new Consumer() {
                                @Override
                                public void accept(Integer s) {
                                    mContactAudioListener.onBegin();
                                }
                            });
                }
            }

            @Override
            public void onEnd(final int i) {//i==0表示成功
                Log.i(TAG, "endConcat " + i);
                Observable.just(1)
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribe(new Consumer() {
                            @Override
                            public void accept(Integer s) {
                                mContactAudioListener.onEnd(i, new File(mResFilePath));
                            }
                        });
            }
        });
    }



    @Override
    public void releaseResourceAndTemp() {
        if (mMediaRecorder != null) {
            try {
                mMediaRecorder.stop();
                mMediaRecorder.release();
            } catch (IllegalStateException illegalStateException) {
                LogProxy.e(TAG, "releaseResourceAndTemp: " + illegalStateException.getMessage());
            }catch (RuntimeException runtimeException){
                LogProxy.e(TAG, "releaseResourceAndTemp: " + ExceptionUtil.collectionExceptionInfo(runtimeException));
            }
            mMediaRecorder = null;
        }
        clearTempFile();

    }

    /**
     * 清空录音文件夹
     */
    private void clearTempFile() {
        if (mRecDir == null) {
            return;
        }

        File[] files = mRecDir.listFiles();

        if (files == null) {
            return;
        }
        for (File recordFile : files) {
            recordFile.delete();
        }
        mTempFileNames.clear();
    }


}

你可能感兴趣的:(低版本的MediaRecorder支持暂停)