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>

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

相关文章
|
27天前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
29天前
|
缓存 前端开发 Android开发
安卓开发中的自定义视图:从零到英雄
【10月更文挑战第42天】 在安卓的世界里,自定义视图是一块画布,让开发者能够绘制出独一无二的界面体验。本文将带你走进自定义视图的大门,通过深入浅出的方式,让你从零基础到能够独立设计并实现复杂的自定义组件。我们将探索自定义视图的核心概念、实现步骤,以及如何优化你的视图以提高性能和兼容性。准备好了吗?让我们开始这段创造性的旅程吧!
26 1
|
1月前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
14天前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
40 19
|
27天前
|
IDE Java 开发工具
移动应用与系统:探索Android开发之旅
在这篇文章中,我们将深入探讨Android开发的各个方面,从基础知识到高级技术。我们将通过代码示例和案例分析,帮助读者更好地理解和掌握Android开发。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧。让我们一起开启Android开发的旅程吧!
|
14天前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
41 14
|
17天前
|
Java Linux 数据库
探索安卓开发:打造你的第一款应用
在数字时代的浪潮中,每个人都有机会成为创意的实现者。本文将带你走进安卓开发的奇妙世界,通过浅显易懂的语言和实际代码示例,引导你从零开始构建自己的第一款安卓应用。无论你是编程新手还是希望拓展技术的开发者,这篇文章都将为你打开一扇门,让你的创意和技术一起飞扬。
|
15天前
|
XML 存储 Java
探索安卓开发之旅:从新手到专家
在数字时代,掌握安卓应用开发技能是进入IT行业的关键。本文将引导读者从零基础开始,逐步深入安卓开发的世界,通过实际案例和代码示例,展示如何构建自己的第一个安卓应用。我们将探讨基本概念、开发工具设置、用户界面设计、数据处理以及发布应用的全过程。无论你是编程新手还是有一定基础的开发者,这篇文章都将为你提供宝贵的知识和技能,帮助你在安卓开发的道路上迈出坚实的步伐。
29 5
|
14天前
|
开发框架 Android开发 iOS开发
安卓与iOS开发中的跨平台策略:一次编码,多平台部署
在移动应用开发的广阔天地中,安卓和iOS两大阵营各占一方。随着技术的发展,跨平台开发框架应运而生,它们承诺着“一次编码,到处运行”的便捷。本文将深入探讨跨平台开发的现状、挑战以及未来趋势,同时通过代码示例揭示跨平台工具的实际运用。
|
15天前
|
XML 搜索推荐 前端开发
安卓开发中的自定义视图:打造个性化UI组件
在安卓应用开发中,自定义视图是一种强大的工具,它允许开发者创造独一无二的用户界面元素,从而提升应用的外观和用户体验。本文将通过一个简单的自定义视图示例,引导你了解如何在安卓项目中实现自定义组件,并探讨其背后的技术原理。我们将从基础的View类讲起,逐步深入到绘图、事件处理以及性能优化等方面。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和技巧。