Android 开发音频录播中媒体录制器MediaRecorder和媒体播放器MediaPlayer的讲解及实战(超详细 附源码)

简介: Android 开发音频录播中媒体录制器MediaRecorder和媒体播放器MediaPlayer的讲解及实战(超详细 附源码)

需要源码请点赞关注收藏后评论区留下QQ~~~

一、媒体录制器MediaRecorder

MediaRecorder是Android自带的音视频录制工具,它通过操纵摄像头和麦克风完成媒体录制,既可录制视频又可以单独录制音频  下面是它的常用方法

reset 重置录制资源

prepare 准备录制

start 开始录制

stop 结束录制

release 释放录制资源

setOnErrorListener 设置错误监听器

setOnInfoListener 设置信息监听器

setMaxDuration  设置可录制的最大时长

setMaxFileSize 设置可录制的最大文件大小

setOutputFile 设置输出我呢见的存储路径

媒体输出格式如下

amr 窄带格式

aac 高级的音频传输流格式

mp4 MPEG4格式

3gp 3GP格式

注意 录音之前要再AndroidManifest.xml中添加录音权限

<uses-permission android:name="android.permission.RECORD_AUDIO"/>

二、媒体播放器MediaPlayer

MediaPlayer是Android自带的音视频播放器,可播放MediaRecorder录制的媒体文件,有各种格式

它的常用方法如下

reset 重置播放器

prepareAsync 异步加载媒体文件 播放大文件或者网络媒体时建议使用该方法

setOnPrepareListener 设置准备监听播放器

setOnCompletionListener 设置结束监听播放器

setOnSeekCompleteListener 设置播放拖动监听器

create 创建指定URI的媒体播放器

setDataSource 设置播放数据来源的文件路径

setVolume 设置音量

...

输出效果如下 可在下拉框中选择不同的编码格式和输出格式 录制时长等等

此处连接真机测试更佳  因为模拟机没有麦克风~~

代码如下

Java类

package com.example.audio;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.example.audio.util.DateUtil;
import com.example.audio.util.MediaUtil;
import java.util.Timer;
import java.util.TimerTask;
public class AudioCommonActivity extends AppCompatActivity {
    private static final String TAG = "AudioCommonActivity";
    private Button btn_record; // 声明一个按钮对象
    private LinearLayout ll_record_progress; // 声明一个线性布局对象
    private ProgressBar pb_record_progress; // 声明一个进度条对象
    private TextView tv_record_progress; // 声明一个文本视图对象
    private Button btn_play; // 声明一个按钮对象
    private LinearLayout ll_play_progress; // 声明一个线性布局对象
    private ProgressBar pb_play_progress; // 声明一个进度条对象
    private TextView tv_play_progress; // 声明一个文本视图对象
    private int mAudioEncoder; // 音频编码
    private int mOutputFormat; // 输出格式
    private int mDuration; // 录制时长
    private String mRecordFilePath; // 录音文件的保存路径
    private MediaRecorder mMediaRecorder = new MediaRecorder(); // 媒体录制器
    private boolean isRecording = false; // 是否正在录音
    private Timer mRecordTimer = new Timer(); // 录音计时器
    private int mRecordTimeCount; // 录音时间计数
    private MediaPlayer mMediaPlayer = new MediaPlayer(); // 媒体播放器
    private boolean isPlaying = false; // 是否正在播音
    private Timer mPlayTimer = new Timer(); // 录音计时器
    private int mPlayTimeCount; // 录音时间计数
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_audio_common);
        btn_record = findViewById(R.id.btn_record);
        ll_record_progress = findViewById(R.id.ll_record_progress);
        pb_record_progress = findViewById(R.id.pb_record_progress);
        tv_record_progress = findViewById(R.id.tv_record_progress);
        btn_play = findViewById(R.id.btn_play);
        ll_play_progress = findViewById(R.id.ll_play_progress);
        pb_play_progress = findViewById(R.id.pb_play_progress);
        tv_play_progress = findViewById(R.id.tv_play_progress);
        btn_record.setOnClickListener(v -> {
            if (!isRecording) { // 未在录音
                startRecord(); // 开始录音
            } else { // 正在录音
                stopRecord(); // 停止录音
            }
        });
        btn_play.setOnClickListener(v -> {
            if (!isPlaying) { // 未在播音
                startPlay(); // 开始播音
            } else { // 正在播音
                stopPlay(); // 停止播音
            }
        });
        initEncoderSpinner(); // 初始化音频编码的下拉框
        initFormatSpinner(); // 初始化输出格式的下拉框
        initDurationSpinner(); // 初始化录制时长的下拉框
    }
    // 初始化音频编码的下拉框
    private void initEncoderSpinner() {
        ArrayAdapter<String> encoderAdapter = new ArrayAdapter<>(this,
                R.layout.item_select, encoderDescArray);
        Spinner sp_encoder = findViewById(R.id.sp_encoder);
        sp_encoder.setPrompt("请选择音频编码");
        sp_encoder.setAdapter(encoderAdapter);
        sp_encoder.setOnItemSelectedListener(new EncoderSelectedListener());
        sp_encoder.setSelection(0);
    }
    private String[] encoderDescArray = {
            "默认编码",
            "窄带编码",
            "宽带编码",
            "低复杂度的高级编码",
            "高效率的高级编码",
            "增强型低延时的高级编码"
    };
    private int[] encoderArray = {
            MediaRecorder.AudioEncoder.DEFAULT,
            MediaRecorder.AudioEncoder.AMR_NB,
            MediaRecorder.AudioEncoder.AMR_WB,
            MediaRecorder.AudioEncoder.AAC,
            MediaRecorder.AudioEncoder.HE_AAC,
            MediaRecorder.AudioEncoder.AAC_ELD
    };
    class EncoderSelectedListener implements AdapterView.OnItemSelectedListener {
        public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
            mAudioEncoder = encoderArray[arg2];
        }
        public void onNothingSelected(AdapterView<?> arg0) {}
    }
    // 初始化输出格式的下拉框
    private void initFormatSpinner() {
        ArrayAdapter<String> formatAdapter = new ArrayAdapter<>(this,
                R.layout.item_select, formatDescArray);
        Spinner sp_format = findViewById(R.id.sp_format);
        sp_format.setPrompt("请选择输出格式");
        sp_format.setAdapter(formatAdapter);
        sp_format.setSelection(0);
        sp_format.setOnItemSelectedListener(new FormatSelectedListener());
    }
    private String[] formatDescArray = {
            "默认格式",
            "窄带格式",
            "宽带格式",
            "高级的音频传输流格式"
    };
    private int[] formatArray = {
            MediaRecorder.OutputFormat.DEFAULT,
            MediaRecorder.OutputFormat.AMR_NB,
            MediaRecorder.OutputFormat.AMR_WB,
            MediaRecorder.OutputFormat.AAC_ADTS
    };
    class FormatSelectedListener implements AdapterView.OnItemSelectedListener {
        public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
            mOutputFormat = formatArray[arg2];
        }
        public void onNothingSelected(AdapterView<?> arg0) {}
    }
    // 初始化录制时长的下拉框
    private void initDurationSpinner() {
        ArrayAdapter<String> durationAdapter = new ArrayAdapter<>(this,
                R.layout.item_select, durationDescArray);
        Spinner sp_duration = findViewById(R.id.sp_duration);
        sp_duration.setPrompt("请选择录制时长");
        sp_duration.setAdapter(durationAdapter);
        sp_duration.setSelection(0);
        sp_duration.setOnItemSelectedListener(new DurationSelectedListener());
    }
    private String[] durationDescArray = {"5秒", "10秒", "20秒", "30秒", "60秒"};
    private int[] durationArray = {5, 10, 20, 30, 60};
    class DurationSelectedListener implements AdapterView.OnItemSelectedListener {
        public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
            mDuration = durationArray[arg2];
        }
        public void onNothingSelected(AdapterView<?> arg0) {}
    }
    // 开始录音
    private void startRecord() {
        Log.d(TAG, "startRecord mAudioEncoder="+mAudioEncoder+", mOutputFormat="+mOutputFormat+", mDuration="+mDuration);
        ll_record_progress.setVisibility(View.VISIBLE);
        isRecording = !isRecording;
        btn_record.setText("停止录音");
        pb_record_progress.setMax(mDuration); // 设置进度条的最大值
        mRecordTimeCount = 0; // 录音时间计数清零
        mRecordTimer = new Timer(); // 创建一个录音计时器
        mRecordTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                pb_record_progress.setProgress(mRecordTimeCount); // 设置进度条的当前进度
                tv_record_progress.setText(MediaUtil.formatDuration(mRecordTimeCount*1000));
                mRecordTimeCount++;
            }
        }, 0, 1000); // 计时器每隔一秒就更新进度条上的录音进度
        // 获取本次录制的媒体文件路径
        mRecordFilePath = String.format("%s/%s.amr",
                getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString(),
                DateUtil.getNowDateTime());
        // 下面是媒体录制器的处理代码
        mMediaRecorder.reset(); // 重置媒体录制器
        // 设置媒体录制器的信息监听器
        mMediaRecorder.setOnInfoListener((mr, what, extra) -> {
            // 录制达到最大时长,或者达到文件大小限制,都停止录制
            if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
                    || what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
                stopRecord(); // 停止录音
            }
        });
        // 设置音频源为麦克风
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        mMediaRecorder.setOutputFormat(mOutputFormat); // 设置媒体的输出格式。该方法要先于setAudioEncoder调用
        mMediaRecorder.setAudioEncoder(mAudioEncoder); // 设置媒体的音频编码器
        mMediaRecorder.setAudioSamplingRate(8000); // 设置媒体的音频采样率。可选
        // mMediaRecorder.setAudioChannels(2); // 设置媒体的音频声道数。可选
        // mMediaRecorder.setAudioEncodingBitRate(1024); // 设置音频每秒录制的字节数。可选
        mMediaRecorder.setMaxDuration(mDuration * 1000); // 设置媒体的最大录制时长
        // mMediaRecorder.setMaxFileSize(1024*1024*10); // 设置媒体的最大文件大小
        // setMaxFileSize与setMaxDuration设置其一即可
        mMediaRecorder.setOutputFile(mRecordFilePath); // 设置媒体文件的保存路径
        try {
            mMediaRecorder.prepare(); // 媒体录制器准备就绪
            mMediaRecorder.start(); // 媒体录制器开始录制
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // 停止录音
    private void stopRecord() {
        btn_record.setText("开始录音");
        mRecordTimer.cancel(); // 取消录音定时器
        if (isRecording) {
            isRecording = !isRecording;
            mMediaRecorder.stop(); // 媒体录制器停止录制
            Toast.makeText(this, "已结束录音,音频文件路径为"+mRecordFilePath, Toast.LENGTH_LONG).show();
            btn_play.setVisibility(View.VISIBLE);
        }
    }
    // 开始播音
    private void startPlay() {
        ll_play_progress.setVisibility(View.VISIBLE);
        isPlaying = !isPlaying;
        btn_play.setText("停止播音");
        mMediaPlayer.reset(); // 重置媒体播放器
        // 设置媒体播放器的完成监听器
        mMediaPlayer.setOnCompletionListener(mp -> stopPlay());
        mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); // 设置音频流的类型为音乐
        try {
            mMediaPlayer.setDataSource(mRecordFilePath); // 设置媒体数据的文件路径
            mMediaPlayer.prepare(); // 媒体播放器准备就绪
            mMediaPlayer.start(); // 媒体播放器开始播放
        } catch (Exception e) {
            e.printStackTrace();
        }
        pb_play_progress.setMax(mMediaPlayer.getDuration()/1000); // 设置进度条的最大值
        mPlayTimeCount = 0; // 播音时间计数清零
        mPlayTimer = new Timer(); // 创建一个播音计时器
        mPlayTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                pb_play_progress.setProgress(mPlayTimeCount); // 设置进度条的当前进度
                tv_play_progress.setText(MediaUtil.formatDuration(mPlayTimeCount*1000));
                mPlayTimeCount++;
            }
        }, 0, 1000); // 计时器每隔一秒就更新进度条上的播音进度
    }
    // 停止播音
    private void stopPlay() {
        btn_play.setText("开始播音");
        mPlayTimer.cancel(); // 取消播音定时器
        if (mMediaPlayer.isPlaying() || isPlaying) { // 如果正在播放
            isPlaying = !isPlaying;
            mMediaPlayer.stop(); // 停止播放
            Toast.makeText(this, "已结束播音", Toast.LENGTH_LONG).show();
        }
    }
    @Override
    protected void onStop() {
        super.onStop();
        stopRecord(); // 停止录音
        stopPlay(); // 停止播音
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mMediaRecorder.release(); // 释放媒体录制器
        mMediaPlayer.release(); // 释放媒体播放器
    }
}

XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:paddingLeft="5dp">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="音频编码:"
            android:textColor="@color/black"
            android:textSize="17sp" />
        <Spinner
            android:id="@+id/sp_encoder"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="left|center"
            android:spinnerMode="dialog" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:paddingLeft="5dp">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="输出格式:"
            android:textColor="@color/black"
            android:textSize="17sp" />
        <Spinner
            android:id="@+id/sp_format"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="left|center"
            android:spinnerMode="dialog" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:paddingLeft="5dp">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="录制时长:"
            android:textColor="@color/black"
            android:textSize="17sp" />
        <Spinner
            android:id="@+id/sp_duration"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="left|center"
            android:spinnerMode="dialog" />
    </LinearLayout>
    <Button
        android:id="@+id/btn_record"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="开始录音"
        android:textColor="@color/black"
        android:textSize="17sp" />
    <LinearLayout
        android:id="@+id/ll_record_progress"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:paddingLeft="5dp"
        android:paddingRight="5dp"
        android:orientation="horizontal"
        android:visibility="gone">
        <ProgressBar
            android:id="@+id/pb_record_progress"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="4" />
        <TextView
            android:id="@+id/tv_record_progress"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="right|center"
            android:textColor="@color/black"
            android:textSize="15sp" />
    </LinearLayout>
    <Button
        android:id="@+id/btn_play"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="开始播音"
        android:textColor="@color/black"
        android:textSize="17sp"
        android:visibility="gone"/>
    <LinearLayout
        android:id="@+id/ll_play_progress"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:paddingLeft="5dp"
        android:paddingRight="5dp"
        android:orientation="horizontal"
        android:visibility="gone">
        <ProgressBar
            android:id="@+id/pb_play_progress"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="4" />
        <TextView
            android:id="@+id/tv_play_progress"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="right|center"
            android:textColor="@color/black"
            android:textSize="15sp" />
    </LinearLayout>
</LinearLayout>

创作不易 觉得有帮助请点赞关注收藏~~~

相关文章
|
19天前
|
Java 程序员 开发工具
Android|修复阿里云播放器下载不回调的问题
虽然 GC 带来了很多便利,但在实际编码时,我们也需要注意对象的生命周期管理,该存活的存活,该释放的释放,避免因为 GC 导致的问题。
27 2
|
3月前
|
开发工具 git 索引
repo sync 更新源码 android-12.0.0_r34, fatal: 不能重置索引文件至版本 ‘v2.27^0‘。
本文描述了在更新AOSP 12源码时遇到的repo同步错误,并提供了通过手动git pull更新repo工具来解决这一问题的方法。
123 1
|
3月前
|
开发工具 uml git
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
本文分享了下载AOSP源码的方法,包括如何使用repo工具和处理常见的repo sync错误,以及配置Python环境以确保顺利同步特定版本的AOSP代码。
413 0
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
|
3月前
|
Java Android开发 芯片
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
本文介绍了如何将基于全志H713芯片的AOSP Android源码导入Android Studio以解决编译和编码问题,通过操作步骤的详细说明,展示了在Android Studio中利用代码提示和补全功能快速定位并修复编译错误的方法。
123 0
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
|
3月前
|
开发工具 Android开发 git
全志H713 Android 11 :给AOSP源码,新增一个Product
本文介绍了在全志H713 Android 11平台上新增名为myboard的产品的步骤,包括创建新的device目录、编辑配置文件、新增内核配置、记录差异列表以及编译kernel和Android系统的详细过程。
104 0
|
3月前
|
Ubuntu 开发工具 Android开发
Repo下载、编译AOSP源码:基于Ubuntu 21.04,android-12.1.0_r27
文章记录了作者在Ubuntu 21.04服务器上配置环境、下载并编译基于Android 12.1.0_r27版本的AOSP源码的过程,包括解决编译过程中遇到的问题和错误处理方法。
179 0
|
1天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
3天前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异和挑战
【10月更文挑战第37天】在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统扮演着主角。它们各自拥有独特的特性、优势以及面临的开发挑战。本文将深入探讨这两个平台在开发过程中的主要差异,从编程语言到用户界面设计,再到市场分布的不同影响,旨在为开发者提供一个全面的视角,帮助他们更好地理解并应对在不同平台上进行应用开发时可能遇到的难题和机遇。
|
5天前
|
XML 存储 Java
探索安卓开发之旅:从新手到专家
【10月更文挑战第35天】在数字化时代,安卓应用的开发成为了一个热门话题。本文旨在通过浅显易懂的语言,带领初学者了解安卓开发的基础知识,同时为有一定经验的开发者提供进阶技巧。我们将一起探讨如何从零开始构建第一个安卓应用,并逐步深入到性能优化和高级功能的实现。无论你是编程新手还是希望提升技能的开发者,这篇文章都将为你提供有价值的指导和灵感。
|
3天前
|
存储 API 开发工具
探索安卓开发:从基础到进阶
【10月更文挑战第37天】在这篇文章中,我们将一起探索安卓开发的奥秘。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和建议。我们将从安卓开发的基础开始,逐步深入到更复杂的主题,如自定义组件、性能优化等。最后,我们将通过一个代码示例来展示如何实现一个简单的安卓应用。让我们一起开始吧!