Android 多媒體之MediaRecorder MediaPlayer

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

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

本文一共分3個(gè)部分:第一和第二部分用來(lái)介紹這兩個(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è)類是用來(lái)去錄制音頻和視頻的篓叶。錄制基于一種簡(jiǎn)單的狀態(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è)置音頻或者視頻的來(lái)源了瘾蛋,可能這個(gè)時(shí)候就有人問(wèn)了 音頻或者視頻的來(lái)源是什么意思俐镐,舉個(gè)例子吧,比如當(dāng)我們?cè)阡浺舻臅r(shí)候哺哼,這個(gè)聲音的來(lái)源就可以設(shè)置成手機(jī)的麥克風(fēng)佩抹。
Initialized:初始化完成叼风。這里我們已經(jīng)知道音頻或者視頻的來(lái)源了,在這里我們就可以設(shè)置一些輸出的屬性了棍苹,比如輸出文件的保存格式无宿,編碼什么的。由于水平有限還有很多關(guān)于媒體文件屬性枢里,我也不是太理解孽鸡。
DataSourceConfigured:數(shù)據(jù)源配置改變。也就是我們改變了一些輸出的屬性栏豺,就會(huì)進(jìn)入到這個(gè)狀態(tài)彬碱。
Prepered:處于這個(gè)狀態(tài)就說(shuō)明了我們的配置已經(jīng)完成了,現(xiàn)在就等去錄制了冰悠。
Recordeing:這個(gè)不用我說(shuō)也應(yīng)該明白了堡妒。
Released:資源被釋放了。
Error:錄制的時(shí)候發(fā)生了錯(cuò)誤溉卓。

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

2.MediaPlayer

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

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

簡(jiǎn)單翻譯過(guò)來(lái)就是:這個(gè)類是用來(lái)去播放音頻桑寨,視頻文件和流文件的伏尼。

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

mediaplayer_state_diagram.gif

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

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

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

先來(lái)看一看效果圖吧

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

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

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

這個(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();

}


/**
 * 開(kāi)始錄音
 */
public void startRecord() {
    try {
        mMediaRecorder = new MediaRecorder();
        File file = new File(mSavePath, generateFileName());
        mCurrentFilePath = file.getAbsolutePath();
        // 設(shè)置錄音文件的保存位置
        mMediaRecorder.setOutputFile(mCurrentFilePath);
        // 設(shè)置錄音的來(lái)源(從哪里錄音)
        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)多余了。如果還是又不懂或者有問(wèn)題需要提出的呕乎,歡迎留言积担。

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

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

對(duì)于這幾種狀態(tài)我定義了對(duì)應(yīng)的五個(gè)常量來(lái)表示。
<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)來(lá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);

        //開(kāi)始錄音
        mMediaRecorderHelper.startRecord();
        mWaveView.setVisibility(View.VISIBLE);
        mIsRecorder = true;
        //開(kāi)啟計(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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末雅镊,一起剝皮案震驚了整個(gè)濱河市襟雷,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌仁烹,老刑警劉巖耸弄,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異卓缰,居然都是意外死亡计呈,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門征唬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)捌显,“玉大人,你說(shuō)我怎么就攤上這事总寒》鐾幔” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵偿乖,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我哲嘲,道長(zhǎng)贪薪,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任眠副,我火速辦了婚禮画切,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘囱怕。我一直安慰自己霍弹,他們只是感情好毫别,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著典格,像睡著了一般岛宦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上耍缴,一...
    開(kāi)封第一講書(shū)人閱讀 49,071評(píng)論 1 285
  • 那天砾肺,我揣著相機(jī)與錄音,去河邊找鬼防嗡。 笑死变汪,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蚁趁。 我是一名探鬼主播裙盾,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼他嫡!你這毒婦竟也來(lái)了番官?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤涮瞻,失蹤者是張志新(化名)和其女友劉穎鲤拿,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體署咽,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡近顷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了宁否。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窒升。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖慕匠,靈堂內(nèi)的尸體忽然破棺而出饱须,到底是詐尸還是另有隱情,我是刑警寧澤台谊,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布蓉媳,位于F島的核電站,受9級(jí)特大地震影響锅铅,放射性物質(zhì)發(fā)生泄漏酪呻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一盐须、第九天 我趴在偏房一處隱蔽的房頂上張望玩荠。 院中可真熱鬧,春花似錦、人聲如沸阶冈。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)女坑。三九已至填具,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間堂飞,已是汗流浹背灌旧。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留绰筛,地道東北人枢泰。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像铝噩,于是被迫代替她去往敵國(guó)和親衡蚂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

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