AndroidFM模块学习之录音功能

来源:互联网 发布:360产品展示软件 编辑:程序博客网 时间:2024/06/13 14:38

前些天分析了一下FM的流程以及主要类,接下来我们分析一下FM的录音功能;

首先看下流程图:

Fm录音时,当点击了录音按钮,会发一个广播出去,源码在FMRadioService.java中

<span style="font-family:KaiTi_GB2312;font-size:18px;"> public void startRecording() {       Log.d(LOGTAG, "In startRecording of Recorder");       if ((true == mSingleRecordingInstanceSupported) &&                               (true == mOverA2DP )) {            Toast.makeText( this,                      "playback on BT in progress,can't record now",                       Toast.LENGTH_SHORT).show();            return;       }       sendRecordIntent(RECORD_START);   }</span>


State状态控制FMRecordingService.javaservice启动与关闭

if (state == 1) {

               Log.d(TAG,"FM ONintent received");

               startService =true;

               context.startService(in);

           }elseif(state == 0){

               Log.d(TAG,"FM OFFintent received");

               startService =false;

               context.stopService(in);

            }

Fm广播接收的action publicstaticfinal StringACTION_FM = "codeaurora.intent.action.FM";

当FMRadioservice类的private void sendRecordServiceIntent(int action)方法发送一个广播并附带一个开关录音的状态int

<span style="font-family:KaiTi_GB2312;font-size:18px;">private void sendRecordServiceIntent(int action) {       Intent intent = new Intent(ACTION_FM);       intent.putExtra("state", action);       intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);       Log.d(LOGTAG, "Sending Recording intent for = " +action);       getApplicationContext().sendBroadcast(intent);   }</span>


 State状态控制FMRecordingService.javaservice启动与关闭

<span style="font-family:KaiTi_GB2312;font-size:18px;">public class FMRecordingReceiver extends BroadcastReceiver {    private static final String TAG = "FMRecordingReceiver";    public static final String ACTION_FM =           "codeaurora.intent.action.FM";    @Override    public void onReceive(Context context, Intent intent) {        String action = intent.getAction();        Log.d(TAG, "Received intent: " + action); if((action != null) && action.equals(ACTION_FM)) {            Log.d(TAG, "FM intent received");            Intent in = new Intent();            in.putExtras(intent);            in.setClass(context, FMRecordingService.class);            int state = intent.getIntExtra("state", 0);            boolean startService = true;            if (state == 1) {Log.d(TAG, "FM ON intent received");                startService = true;                context.startService(in);            } else if(state == 0){                Log.d(TAG, "FM OFF intent received");                startService = false;                context.stopService(in);            }        }   }}</span>

Fm接收广播的action

<span style="font-family:KaiTi_GB2312;font-size:18px;"> public static final String ACTION_FM_RECORDING =                       "codeaurora.intent.action.FM_Recording";    public static final String ACTION_FM_RECORDING_STATUS =                "codeaurora.intent.action.FM.Recording.Status";</span>

 

onCreat()方法里注册广播接受机制,一个广播是录音状态,一个是关闭fm状态

<span style="font-family:KaiTi_GB2312;font-size:18px;">public void onCreate() {        super.onCreate();        Log.d(TAG, "FMRecording Service onCreate");        registerRecordingListner();        registerShutdownListner();        registerStorageMediaListener();            }</span>

 

onDestroy()方法里写了卸载注册停止录音

public void onDestroy() {        Log.d(TAG, "FMRecording Service onDestroy");        if (mFmRecordingOn == true) {            Log.d(TAG, "Still recording on progress, Stoping it");            stopRecord();        }        unregisterBroadCastReceiver(mFmRecordingReceiver);        unregisterBroadCastReceiver(mFmShutdownReceiver);        unregisterBroadCastReceiver(mSdcardUnmountReceiver);        super.onDestroy();    }


stopRecord();停止录音

 private void stopRecord() {        Log.d(TAG, "Enter stopRecord");        mFmRecordingOn = false;        if (mRecorder == null)            return;        try {             mRecorder.stop();             mRecorder.reset();             mRecorder.release();             mRecorder = null;        } catch(Exception e) {             e.printStackTrace();        }        sendRecordingStatusIntent(STOP);        saveFile();        stopForeground(true);        stopClientStatusCheck();    }


registerShutdownListner();注册接受方法中停止fm录音

private void registerShutdownListner() {        if (mFmShutdownReceiver == null) {            mFmShutdownReceiver = new BroadcastReceiver() {                 @Override                 public void onReceive(Context context, Intent intent) {                     Log.d(TAG, "Received intent " +intent);                     String action = intent.getAction();                     Log.d(TAG, " action = " +action);                     if (action.equals("android.intent.action.ACTION_SHUTDOWN")) {                         Log.d(TAG, "android.intent.action.ACTION_SHUTDOWN Intent received");                         stopRecord();                     }                 }            };            IntentFilter iFilter = new IntentFilter();            iFilter.addAction("android.intent.action.ACTION_SHUTDOWN");            registerReceiver(mFmShutdownReceiver, iFilter);        }    }


获取sd卡有效空间

private static long getAvailableSpace() {        String state = Environment.getExternalStorageState();        Log.d(TAG, "External storage state=" + state);        if (Environment.MEDIA_CHECKING.equals(state)) {            return PREPARING;        }        if (!Environment.MEDIA_MOUNTED.equals(state)) {            return UNAVAILABLE;        }        try {             File sampleDir = Environment.getExternalStorageDirectory();             StatFs stat = new StatFs(sampleDir.getAbsolutePath());             return stat.getAvailableBlocks() * (long) stat.getBlockSize();        } catch (Exception e) {             Log.i(TAG, "Fail to access external storage", e);        }        return UNKNOWN_SIZE;    }

主要通过以下方法实现:

FilesampleDir = Environment.getExternalStorageDirectory();

             StatFs stat = newStatFs(sampleDir.getAbsolutePath());

             return stat.getAvailableBlocks() *(long) stat.getBlockSize();

有四种值:

Environment.MEDIA_CHECKING检查sd卡准备读取

Environment.MEDIA_MOUNTED没有sd

UNKNOWN_SIZE sd卡有效空间等于UNKNOWN_SIZE

LOW_STORAGE_THRESHOLD低于存储空间极限值

 

更新显示存储判断提示方法

private boolean updateAndShowStorageHint() {        mStorageSpace = getAvailableSpace();        return showStorageHint();    }

 

发送一个录音状态广播

private void sendRecordingStatusIntent(int status) {        Intent intent = new Intent(ACTION_FM_RECORDING_STATUS);        intent.putExtra("state", status);        Log.d(TAG, "posting intent for FM Recording status as = " +status);        getApplicationContext().sendBroadcastAsUser(intent, UserHandle.ALL);    }

getApplicationContext().sendBroadcastAsUser(intent,UserHandle.ALL);

UserHandle.ALL一个类的对象,USER_ALL= -1返回的一个字符串UserHandle{-1}

回调方法,fmradioservice.java回调

mCallbacks.onRecordingStarted();方法

mCallbacks.onRecordingStopped();方法

public void registerFMRecordingStatus()

 

启动录音

private boolean startRecord() {        Log.d(TAG, "Enter startRecord");        if (mRecorder != null) { /* Stop existing recording if any */            Log.d(TAG, "Stopping existing record");            try {                 mRecorder.stop();                 mRecorder.reset();                 mRecorder.release();                 mRecorder = null;            } catch(Exception e) {                 e.printStackTrace();            }        } if (!updateAndShowStorageHint())            return false;        long maxFileSize = mStorageSpace - LOW_STORAGE_THRESHOLD;        mRecorder = new MediaRecorder();        try {             mRecorder.setMaxFileSize(maxFileSize);             if(mRecordDuration >= 0)                mRecorder.setMaxDuration(mRecordDuration);        } catch (RuntimeException exception) {        }        mSampleFile = null;                File sampleDir;<span style="font-family:KaiTi_GB2312;">        </span>if((Environment.getExternalSDStorageState(this).equals(Environment.MEDIA_MOUNTED))){            sampleDir = new File(Environment.getExternalSDStorageDirectory(), "/FMRecording");        }else{            sampleDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +"/FMRecording");        }                 if(!(sampleDir.mkdirs() || sampleDir.isDirectory()))            return false;        try {             mSampleFile = File.createTempFile("FMRecording", ".3gpp", sampleDir);        } catch (IOException e) {             Log.e(TAG, "Not able to access SD Card");             Toast.makeText(this, "Not able to access SD Card", Toast.LENGTH_SHORT).show();        }        try {             Log.d(TAG, "AudioSource.FM_RX" +MediaRecorder.AudioSource.FM_RX);             mRecorder.setAudioSource(MediaRecorder.AudioSource.FM_RX);             mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);             mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);             mAudioType = "audio/3gpp";        } catch (RuntimeException exception) {             Log.d(TAG, "RuntimeException while settings");             mRecorder.reset();             mRecorder.release();             mRecorder = null;             return false;        }        Log.d(TAG, "setOutputFile");        mRecorder.setOutputFile(mSampleFile.getAbsolutePath());        try {mRecorder.prepare();             Log.d(TAG, "start");             mRecorder.start();        } catch (IOException e) {             Log.d(TAG, "IOException while start");             mRecorder.reset();             mRecorder.release();             mRecorder = null;             return false;        } catch (RuntimeException e) {             Log.d(TAG, "RuntimeException while start");             mRecorder.reset();             mRecorder.release();             mRecorder = null;             return false;        }        mFmRecordingOn = true; Log.d(TAG, "mSampleFile.getAbsolutePath() " +mSampleFile.getAbsolutePath());        mRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {             public void onInfo(MediaRecorder mr, int what, int extra) {                 if ((what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) ||                     (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED)) {                     if (mFmRecordingOn) {                         Log.d(TAG, "Maximum file size/duration reached, stopping the recording");                         stopRecord();                     }                     if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {                         // Show the toast.                         Toast.makeText(FMRecordingService.this,                                        R.string.FMRecording_reach_size_limit,                                        Toast.LENGTH_LONG).show();                     }                 }             }             // from MediaRecorder.OnErrorListenerpublic void onError(MediaRecorder mr, int what, int extra) {                 Log.e(TAG, "MediaRecorder error. what=" + what + ". extra=" + extra);                 if (what == MediaRecorder.MEDIA_RECORDER_ERROR_UNKNOWN) {                     // We may have run out of space on the sdcard.                     if (mFmRecordingOn) {                         stopRecord();                     }                     updateAndShowStorageHint();                 }             }        });        mSampleStart = System.currentTimeMillis();        sendRecordingStatusIntent(START);        startNotification();        return true;    }

录音不为空先设置为停止录音从新设置释放资源

mRecorder!= null

mRecorder.stop();

                 mRecorder.reset();

                 mRecorder.release();

                 mRecorder = null;

 

录音判断存储空间够用不

 if (!updateAndShowStorageHint())

            return false;

 

录音最大值为当前值前去低于存储极限值。

longmaxFileSize = mStorageSpace - LOW_STORAGE_THRESHOLD;

 

设置录音最大值

mRecorder.setMaxFileSize(maxFileSize);

设置录音持续

mRecorder.setMaxDuration(mRecordDuration);

 

设置录音来源,放置的位置,录音audio格式,audio写入音源编码格式

mRecorder.setAudioSource(MediaRecorder.AudioSource.FM_RX);

mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);

mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);

mRecorder.setOutputFile(mSampleFile.getAbsolutePath());

 

录音监听mediaRecorder监听

mRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener()

what ==MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED

what ==MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED

mFmRecordingOn 为true 停止录音

stopRecord();

录音过程报错停止录音

stopRecord();弹出提示

updateAndShowStorageHint();

 

获取当前时间,发送录音状态广播,启动通知

mSampleStart= System.currentTimeMillis();

        sendRecordingStatusIntent(START);

        startNotification();

 

发送通知,设置远程控制,startForeground(102,status);设置sevice至于前台

 private void startNotification() {        RemoteViews views = new RemoteViews(getPackageName(), R.layout.record_status_bar);        Notification status = new Notification();        status.contentView = views;        status.flags |= Notification.FLAG_ONGOING_EVENT;        status.icon = R.drawable.ic_menu_record;        startForeground(102, status);    }

停止录音,保存录音文件,停止录音之前台,停止状体切换

 private void stopRecord()

sendRecordingStatusIntent(STOP);

        saveFile();

        stopForeground(true);

        stopClientStatusCheck();

 

保存录音方法(录音一开录,就在往默认内置T中写数据以字节方式写入数据)

private void saveFile() {        int sampleLength = (int)((System.currentTimeMillis() - mSampleStart)/1000 );        Log.d(TAG, "Enter saveFile");        if (sampleLength == 0)            return;        String state = Environment.getExternalStorageState();        Log.d(TAG, "storage state is " + state);        if (Environment.MEDIA_MOUNTED.equals(state)) {            try {                 this.addToMediaDB(mSampleFile);                 Toast.makeText(this,getString(R.string.save_record_file,                                               mSampleFile.getAbsolutePath( )),                                               Toast.LENGTH_LONG).show();            } catch(Exception e) {                 e.printStackTrace();            }        } else {            Log.e(TAG, "SD card must have removed during recording. ");            Toast.makeText(this, "Recording aborted", Toast.LENGTH_SHORT).show();        }        return;    }

 

获取当时减去录音起始时间如果为零就跳出保存方法不保存数据

intsampleLength = (int)((System.currentTimeMillis() - mSampleStart)/1000 );

 

Environment.MEDIA_MOUNTED.equals(state)sd卡可用就将录音路径添加到多媒体数据库中

this.addToMediaDB(mSampleFile);

 

将信息添加到多媒体数据库,音频的audio格式存入数据库中

 private Uri addToMediaDB(File file) {        Log.d(TAG, "In addToMediaDB");        Resources res = getResources();        ContentValues cv = new ContentValues();        long current = System.currentTimeMillis();        long modDate = file.lastModified();        Date date = new Date(current);        SimpleDateFormat formatter = new SimpleDateFormat(                  res.getString(R.string.audio_db_title_format));        String title = formatter.format(date);        // Lets label the recorded audio file as NON-MUSIC so that the file        // won't be displayed automatically, except for in the playlist.        cv.put(MediaStore.Audio.Media.IS_MUSIC, "1");cv.put(MediaStore.Audio.Media.TITLE, title);        cv.put(MediaStore.Audio.Media.DATA, file.getAbsolutePath());        cv.put(MediaStore.Audio.Media.DATE_ADDED, (int) (current / 1000));        cv.put(MediaStore.Audio.Media.DATE_MODIFIED, (int) (modDate / 1000));        cv.put(MediaStore.Audio.Media.MIME_TYPE, mAudioType);        cv.put(MediaStore.Audio.Media.ARTIST,                res.getString(R.string.audio_db_artist_name));        cv.put(MediaStore.Audio.Media.ALBUM,                res.getString(R.string.audio_db_album_name));        Log.d(TAG, "Inserting audio record: " + cv.toString());ContentResolver resolver = getContentResolver();        Uri base = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;        Log.d(TAG, "ContentURI: " + base);        Uri result = resolver.insert(base, cv);        if (result == null) {            Toast.makeText(this, R.string.unable_to_store, Toast.LENGTH_SHORT).show();            return null;        }        if (getPlaylistId(res) == -1) {            createPlaylist(res, resolver);        }        int audioId = Integer.valueOf(result.getLastPathSegment());        addToPlaylist(resolver, audioId, getPlaylistId(res));        // Notify those applications such as Music listening to the        // scanner events that a recorded audio file just created.        sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, result));        return result;    }

获取audiouri

Uri base =MediaStore.Audio.Media.EXTERNAL_CONTENT_URI

 

添加到播放列表

addToPlaylist(resolver,audioId, getPlaylistId(res));

可以存入music数据

cv.put(MediaStore.Audio.Media.IS_MUSIC,"1")

发送广播扫描

sendBroadcast(newIntent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, result));

 

 

privatevoid registerRecordingListner()广播接收者

private void registerRecordingListner() {        if (mFmRecordingReceiver == null) {            mFmRecordingReceiver = new BroadcastReceiver() {                 @Override                 public void onReceive(Context context, Intent intent) {                     Log.d(TAG, "Received intent " +intent);                     String action = intent.getAction();                     Log.d(TAG, " action = " +action);                     if (action.equals(ACTION_FM_RECORDING)) {                         int state = intent.getIntExtra("state", STOP);                         Log.d(TAG, "ACTION_FM_RECORDING Intent received" + state);                         if (state == START) {                             Log.d(TAG, "Recording start");                             mRecordDuration = intent.getIntExtra("record_duration", mRecordDuration);                             if(startRecord()) {                                clientProcessName = intent.getStringExtra("process_name");                                clientPid = intent.getIntExtra("process_id", -1);                                startClientStatusCheck();                             }} else if (state == STOP) {                             Log.d(TAG, "Stop recording");                             stopRecord();                         }                     }                 }            };            IntentFilter iFilter = new IntentFilter();            iFilter.addAction(ACTION_FM_RECORDING);            registerReceiver(mFmRecordingReceiver, iFilter);        }    }


广播状态为1的时候就开始录音并检查线程

 private void startClientStatusCheck()

 

获取客户端所有进程信息进行匹配如果启动的app没有被杀死就继续录音否则停止

privateboolean getClientStatus(int pid, String processName)

ActivityManageractvityManager =

                   (ActivityManager)this.getSystemService(

                                                          this.ACTIVITY_SERVICE);

 

      List<RunningAppProcessInfo>procInfos =

                                     actvityManager.getRunningAppProcesses();

 

      for(RunningAppProcessInfo procInfo :procInfos) {

         if ((pid == procInfo.pid)

 &&

             (procInfo.processName.equals(processName))) {

              status = true;

              break;

         }

      }

      procInfos.clear();

 

privateRunnable clientStatusCheckThread = new Runnable()

停止录音后睡眠500毫秒

 

privatevoid stopClientStatusCheck()中断线程mStatusCheckThread

private void stopClientStatusCheck() {       if(mStatusCheckThread != null) {          mStatusCheckThread.interrupt();       }   }


 


0 0
原创粉丝点击