Android 多媒体之MediaRecorder MediaPlayer

以前学了很多东西,都没怎么做记录,慢慢的时间长了也就给忘了,当初踩过的坑翻来覆去的踩,由于这些血淋林的教训,所以从现在开始,每次学了新的东西都要来写个博客做个记录总结一下了。

最近在学习Android多媒体这一块,现在刚好学到了MediaRecorder和MediaPlayer。今天也就介绍下这两个类吧,也有利于以后初学者的学习。

本文一共分3个部分:第一和第二部分用来介绍这两个类,第3部分做一个录音的小Demo(附源码)

1.MediaRecorder

关于MediaRecorder 官网给的解释是

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

大致意思就是,这个类是用来去录制音频和视频的。录制基于一种简单的状态机制。
这个状态机制是什么呢?Google给了一张图

mediarecorder_state_diagram.gif

从图中可以看出MediaRecorder 有这么几种状态:
Initial:初始化。也就是MediaRecorder 刚被创建的时候。在这个时候我们去设置音频或者视频的来源了,可能这个时候就有人问了 音频或者视频的来源是什么意思,举个例子吧,比如当我们在录音的时候,这个声音的来源就可以设置成手机的麦克风。
Initialized:初始化完成。这里我们已经知道音频或者视频的来源了,在这里我们就可以设置一些输出的属性了,比如输出文件的保存格式,编码什么的。由于水平有限还有很多关于媒体文件属性,我也不是太理解。
DataSourceConfigured:数据源配置改变。也就是我们改变了一些输出的属性,就会进入到这个状态。
Prepered:处于这个状态就说明了我们的配置已经完成了,现在就等去录制了。
Recordeing:这个不用我说也应该明白了。
Released:资源被释放了。
Error:录制的时候发生了错误。

图中已经把状态之间是怎么切换的已经描述的非常清楚了,我也不过多介绍了。

2.MediaPlayer

关于MediaPlayer官网给的解释是:

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

简单翻译过来就是:这个类是用来去播放音频,视频文件和流文件的。

他也对应了一个状态图:

mediaplayer_state_diagram.gif

其实他的大致流程和MediaRecorder差不多,这里我就略过了。

你以为到这就结束了吗?肯定不是的,上面的也就为下面我要做的做个铺垫而已,有了上面的基础,后面的肯定就轻松了。

3.做一个录音的小Demo

先来看一看效果图吧

未标题-1.png

看起来还是很简单吧。
开始是初始状态,然后是录制状态(中间有个波浪形的自定义控件),在这个状态下会显示当前的录制时长,波浪形控件会显示,最后就是录制完成,可以点击播放了。
最下面是保存和删除按钮,可以删除当前的录音。PS:保存按钮没有任何实现,录音文件会自动保存。

知道了这个效果现在就来介绍是怎么做的吧。

这个小Demo我对MediaRecorder和MediaPlayer做了一下封装,方便以后使用调用方便。

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();
        // 设置录音文件的保存位置
        mMediaRecorder.setOutputFile(mCurrentFilePath);
        // 设置录音的来源(从哪里录音)
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        // 设置录音的保存格式
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);
        // 设置录音的编码
        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;
    }
}


/**
 * 继续播放
 */
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>

这两个类的代码注释的已经非常详细了,我再在这里介绍也就显得有点多余了。如果还是又不懂或者有问题需要提出的,欢迎留言。

到这里我们的核心东西已经准备好了,现在就剩下一个Activity让我们去实现刚才效果图上的小Demo了。

我们来分析一下我们的小Demo会有哪些状态:
1.正常状态,也就是没有录制的状态
2.录制中
3.录制完成
4.播放状态
5.暂停状态

对于这几种状态我定义了对应的五个常量来表示。
<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>

在中间的录制按钮在点击的时候,我们要根据当前的状态来进行状态之间的切换:
依然是附代码,就是这么任性~~~~
<pre>
/**
* 切换ACTION状态
*/
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;
        //开启计时线程
        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) {
                //当播放完了之后切换到录制完成的状态
                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);
        //继续播放
        MediaPlayerHelper.resume();
    }
}

</pre>

上面的就是整个Demo的核心思想。

需要源码的可以点击下面的链接下载。
MediaRecorderDemo

如果在阅读的过程中又发现错误,也欢迎留言纠错。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容