Android 多媒體之MediaRecorder MediaPlayer

以前學(xué)了很多東西,都沒怎么做記錄,慢慢的時(shí)間長了也就給忘了,當(dāng)初踩過的坑翻來覆去的踩,由于這些血淋林的教訓(xùn),所以從現(xiàn)在開始,每次學(xué)了新的東西都要來寫個(gè)博客做個(gè)記錄總結(jié)一下了。

最近在學(xué)習(xí)Android多媒體這一塊,現(xiàn)在剛好學(xué)到了MediaRecorder和MediaPlayer。今天也就介紹下這兩個(gè)類吧,也有利于以后初學(xué)者的學(xué)習(xí)。

本文一共分3個(gè)部分:第一和第二部分用來介紹這兩個(gè)類,第3部分做一個(gè)錄音的小Demo(附源碼)

1.MediaRecorder

關(guān)于MediaRecorder 官網(wǎng)給的解釋是

Used to record audio and video. The recording control is based on a simple state machine

大致意思就是,這個(gè)類是用來去錄制音頻和視頻的。錄制基于一種簡單的狀態(tài)機(jī)制。
這個(gè)狀態(tài)機(jī)制是什么呢?Google給了一張圖

mediarecorder_state_diagram.gif

從圖中可以看出MediaRecorder 有這么幾種狀態(tài):
Initial:初始化。也就是MediaRecorder 剛被創(chuàng)建的時(shí)候。在這個(gè)時(shí)候我們?nèi)ピO(shè)置音頻或者視頻的來源了,可能這個(gè)時(shí)候就有人問了 音頻或者視頻的來源是什么意思,舉個(gè)例子吧,比如當(dāng)我們?cè)阡浺舻臅r(shí)候,這個(gè)聲音的來源就可以設(shè)置成手機(jī)的麥克風(fēng)。
Initialized:初始化完成。這里我們已經(jīng)知道音頻或者視頻的來源了,在這里我們就可以設(shè)置一些輸出的屬性了,比如輸出文件的保存格式,編碼什么的。由于水平有限還有很多關(guān)于媒體文件屬性,我也不是太理解。
DataSourceConfigured:數(shù)據(jù)源配置改變。也就是我們改變了一些輸出的屬性,就會(huì)進(jìn)入到這個(gè)狀態(tài)。
Prepered:處于這個(gè)狀態(tài)就說明了我們的配置已經(jīng)完成了,現(xiàn)在就等去錄制了。
Recordeing:這個(gè)不用我說也應(yīng)該明白了。
Released:資源被釋放了。
Error:錄制的時(shí)候發(fā)生了錯(cuò)誤。

圖中已經(jīng)把狀態(tài)之間是怎么切換的已經(jīng)描述的非常清楚了,我也不過多介紹了。

2.MediaPlayer

關(guān)于MediaPlayer官網(wǎng)給的解釋是:

MediaPlayer class can be used to control playback of audio/video files and streams.

簡單翻譯過來就是:這個(gè)類是用來去播放音頻,視頻文件和流文件的。

他也對(duì)應(yīng)了一個(gè)狀態(tài)圖:

mediaplayer_state_diagram.gif

其實(shí)他的大致流程和MediaRecorder差不多,這里我就略過了。

你以為到這就結(jié)束了嗎?肯定不是的,上面的也就為下面我要做的做個(gè)鋪墊而已,有了上面的基礎(chǔ),后面的肯定就輕松了。

3.做一個(gè)錄音的小Demo

先來看一看效果圖吧

未標(biāo)題-1.png

看起來還是很簡單吧。
開始是初始狀態(tài),然后是錄制狀態(tài)(中間有個(gè)波浪形的自定義控件),在這個(gè)狀態(tài)下會(huì)顯示當(dāng)前的錄制時(shí)長,波浪形控件會(huì)顯示,最后就是錄制完成,可以點(diǎn)擊播放了。
最下面是保存和刪除按鈕,可以刪除當(dāng)前的錄音。PS:保存按鈕沒有任何實(shí)現(xiàn),錄音文件會(huì)自動(dòng)保存。

知道了這個(gè)效果現(xiàn)在就來介紹是怎么做的吧。

這個(gè)小Demo我對(duì)MediaRecorder和MediaPlayer做了一下封裝,方便以后使用調(diào)用方便。

MediaRecorderHelper代碼如下:
<pre>
public class MediaRecorderHelper {

private MediaRecorder mMediaRecorder;
private String mSavePath;
private String mCurrentFilePath;


public MediaRecorderHelper(String savePath) {
    mSavePath = savePath;
    File file = new File(mSavePath);
    if (!file.exists()) file.mkdirs();

}


/**
 * 開始錄音
 */
public void startRecord() {
    try {
        mMediaRecorder = new MediaRecorder();
        File file = new File(mSavePath, generateFileName());
        mCurrentFilePath = file.getAbsolutePath();
        // 設(shè)置錄音文件的保存位置
        mMediaRecorder.setOutputFile(mCurrentFilePath);
        // 設(shè)置錄音的來源(從哪里錄音)
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        // 設(shè)置錄音的保存格式
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);
        // 設(shè)置錄音的編碼
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
        mMediaRecorder.prepare();
        mMediaRecorder.start();

    } catch (Exception e) {
        e.printStackTrace();
    }
}

/**
 * 停止錄音
 */
public void stopAndRelease() {
    if (mMediaRecorder == null) return;
    mMediaRecorder.stop();
    mMediaRecorder.release();
    mMediaRecorder = null;
}

/***
 * 取消本次錄音操作
 */
public void cancel() {
    this.stopAndRelease();
    if (mCurrentFilePath != null) {
        File file = new File(mCurrentFilePath);
        file.delete();
        mCurrentFilePath = null;


    }
}

private String generateFileName() {
    return UUID.randomUUID().toString() + ".amr";
}

/**
 * 得到錄音文件的路徑
 *
 * @return
 */
public String getCurrentFilePath() {
    return mCurrentFilePath;
}

}

</pre>
MediaPlayerHelper代碼如下:
<pre>
public class MediaPlayerHelper {

private static MediaPlayer mMediaPlayer;
private static boolean isPause = false;

public static void playSound(String filePath) {
    playSound(filePath, null);
}

public static void playSound(String filePath, MediaPlayer.OnCompletionListener onCompletionListener) {
    if (mMediaPlayer == null) {
        mMediaPlayer = new MediaPlayer();
        mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
            @Override
            public boolean onError(MediaPlayer mp, int what, int extra) {
                mMediaPlayer.reset();
                return false;
            }
        });
    } else {
        mMediaPlayer.reset();
    }
    try {
        mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        if (onCompletionListener != null) {
            mMediaPlayer.setOnCompletionListener(onCompletionListener);
        }


        mMediaPlayer.setDataSource(filePath);
        mMediaPlayer.prepare();
        mMediaPlayer.start();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (SecurityException e) {
        e.printStackTrace();
    } catch (IllegalStateException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }


}

/**
 * 暫停播放
 */
public static void pause() {
    if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
        mMediaPlayer.pause();
        isPause = true;
    }
}


/**
 * 繼續(xù)播放
 */
public static void resume() {
    if (mMediaPlayer != null && isPause) {
        mMediaPlayer.start();
        isPause = false;
    }
}

/**
 * 釋放資源
 */
public static void realese() {
    if (mMediaPlayer != null) {
        mMediaPlayer.release();
        mMediaPlayer = null;
        isPause = true;
    }
}

}

</pre>

這兩個(gè)類的代碼注釋的已經(jīng)非常詳細(xì)了,我再在這里介紹也就顯得有點(diǎn)多余了。如果還是又不懂或者有問題需要提出的,歡迎留言。

到這里我們的核心東西已經(jīng)準(zhǔn)備好了,現(xiàn)在就剩下一個(gè)Activity讓我們?nèi)?shí)現(xiàn)剛才效果圖上的小Demo了。

我們來分析一下我們的小Demo會(huì)有哪些狀態(tài):
1.正常狀態(tài),也就是沒有錄制的狀態(tài)
2.錄制中
3.錄制完成
4.播放狀態(tài)
5.暫停狀態(tài)

對(duì)于這幾種狀態(tài)我定義了對(duì)應(yīng)的五個(gè)常量來表示。
<pre>
private static final int ACTION_NORMAL = 0;
private static final int ACTION_RECORDING = 1;
private static final int ACTION_COMMPLETE = 2;
private static final int ACTION_PLAYING = 3;
private static final int ACTION_PAUSE = 4;
</pre>

在中間的錄制按鈕在點(diǎn)擊的時(shí)候,我們要根據(jù)當(dāng)前的狀態(tài)來進(jìn)行狀態(tài)之間的切換:
依然是附代碼,就是這么任性~~~~
<pre>
/**
* 切換ACTION狀態(tài)
*/
private void switchActionState() {
mIsRecorder = false;
if (mCurrentActionState == ACTION_NORMAL) {
mCurrentActionState = ACTION_RECORDING;
mIvAction.setImageResource(R.drawable.pause);

        //開始錄音
        mMediaRecorderHelper.startRecord();
        mWaveView.setVisibility(View.VISIBLE);
        mIsRecorder = true;
        //開啟計(jì)時(shí)線程
        mCountTimeThread = new Thread(this);
        mCountTimeThread.start();

    } else if (mCurrentActionState == ACTION_RECORDING) {//錄制中
        mCurrentActionState = ACTION_COMMPLETE;
        mIvAction.setImageResource(R.drawable.icon_audio_state_uploaded);
        //停止錄音
        mMediaRecorderHelper.stopAndRelease();
        mRlBottom.setVisibility(View.VISIBLE);
        mWaveView.setVisibility(View.INVISIBLE);

    } else if (mCurrentActionState == ACTION_COMMPLETE) {//錄制完成
        mCurrentActionState = ACTION_PLAYING;
        mIvAction.setImageResource(R.drawable.icon_audio_state_uploaded_play);

        //播放錄音
        MediaPlayerHelper.playSound(mMediaRecorderHelper.getCurrentFilePath(), new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                //當(dāng)播放完了之后切換到錄制完成的狀態(tài)
                mCurrentActionState = ACTION_COMMPLETE;
                mIvAction.setImageResource(R.drawable.icon_audio_state_uploaded);
            }
        });

    } else if (mCurrentActionState == ACTION_PLAYING) {//播放中
        mCurrentActionState = ACTION_PAUSE;
        mIvAction.setImageResource(R.drawable.icon_audio_state_uploaded);
        //暫停播放
        MediaPlayerHelper.pause();
    } else if (mCurrentActionState == ACTION_PAUSE) {//暫停
        mCurrentActionState = ACTION_PLAYING;
        mIvAction.setImageResource(R.drawable.icon_audio_state_uploaded_play);
        //繼續(xù)播放
        MediaPlayerHelper.resume();
    }
}

</pre>

上面的就是整個(gè)Demo的核心思想。

需要源碼的可以點(diǎn)擊下面的鏈接下載。
MediaRecorderDemo

如果在閱讀的過程中又發(fā)現(xiàn)錯(cuò)誤,也歡迎留言糾錯(cuò)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,494評(píng)論 3 416
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評(píng)論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,714評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,186評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,410評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,940評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,776評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,976評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,210評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評(píng)論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,654評(píng)論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,958評(píng)論 2 373

推薦閱讀更多精彩內(nèi)容