多媒体——音频——利用MediaRecorder录制音频

梦里梦外; 2024-04-03 09:16 211阅读 0赞

8ca20e3968d9b11068162df5e8465af6.png

3dc1eff37b8e7de1f3d83f742152476e.png

布局:

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:layout_width="match_parent"
  3. android:layout_height="match_parent"
  4. android:orientation="vertical">
  5. <LinearLayout
  6. android:layout_width="match_parent"
  7. android:layout_height="40dp"
  8. android:paddingLeft="5dp">
  9. <TextView
  10. android:layout_width="wrap_content"
  11. android:layout_height="match_parent"
  12. android:gravity="center"
  13. android:text="音频编码:"
  14. android:textColor="@color/black"
  15. android:textSize="17sp" />
  16. <Spinner
  17. android:id="@+id/sp_encoder"
  18. android:layout_width="0dp"
  19. android:layout_height="match_parent"
  20. android:layout_weight="1"
  21. android:gravity="left|center"
  22. android:spinnerMode="dialog" />
  23. </LinearLayout>
  24. <LinearLayout
  25. android:layout_width="match_parent"
  26. android:layout_height="40dp"
  27. android:paddingLeft="5dp">
  28. <TextView
  29. android:layout_width="wrap_content"
  30. android:layout_height="match_parent"
  31. android:gravity="center"
  32. android:text="输出格式:"
  33. android:textColor="@color/black"
  34. android:textSize="17sp" />
  35. <Spinner
  36. android:id="@+id/sp_format"
  37. android:layout_width="0dp"
  38. android:layout_height="match_parent"
  39. android:layout_weight="1"
  40. android:gravity="left|center"
  41. android:spinnerMode="dialog" />
  42. </LinearLayout>
  43. <LinearLayout
  44. android:layout_width="match_parent"
  45. android:layout_height="40dp"
  46. android:paddingLeft="5dp">
  47. <TextView
  48. android:layout_width="wrap_content"
  49. android:layout_height="match_parent"
  50. android:gravity="center"
  51. android:text="录制时长:"
  52. android:textColor="@color/black"
  53. android:textSize="17sp" />
  54. <Spinner
  55. android:id="@+id/sp_duration"
  56. android:layout_width="0dp"
  57. android:layout_height="match_parent"
  58. android:layout_weight="1"
  59. android:gravity="left|center"
  60. android:spinnerMode="dialog" />
  61. </LinearLayout>
  62. <Button
  63. android:id="@+id/btn_record"
  64. android:layout_width="match_parent"
  65. android:layout_height="wrap_content"
  66. android:text="开始录音"
  67. android:textColor="@color/black"
  68. android:textSize="17sp" />
  69. <LinearLayout
  70. android:id="@+id/ll_progress"
  71. android:layout_width="match_parent"
  72. android:layout_height="30dp"
  73. android:paddingLeft="5dp"
  74. android:paddingRight="5dp"
  75. android:orientation="horizontal"
  76. android:visibility="gone">
  77. <ProgressBar
  78. android:id="@+id/pb_record"
  79. style="?android:attr/progressBarStyleHorizontal"
  80. android:layout_width="0dp"
  81. android:layout_height="match_parent"
  82. android:layout_weight="4" />
  83. <TextView
  84. android:id="@+id/tv_progress"
  85. android:layout_width="0dp"
  86. android:layout_height="match_parent"
  87. android:layout_weight="1"
  88. android:gravity="right|center"
  89. android:textColor="@color/black"
  90. android:textSize="15sp" />
  91. </LinearLayout>
  92. <ImageView
  93. android:id="@+id/iv_audio"
  94. android:layout_width="match_parent"
  95. android:layout_height="50dp"
  96. android:scaleType="fitCenter"
  97. android:src="@drawable/play_audio"
  98. android:visibility="gone" />
  99. </LinearLayout>

fc380d98b9092a624085aa7cd498af8d.png

  1. MediaUtil
  2. package com.example.myapplication.util;
  3. import android.annotation.SuppressLint;
  4. import android.content.Context;
  5. import android.graphics.Bitmap;
  6. import android.media.MediaMetadataRetriever;
  7. import android.net.Uri;
  8. import android.os.Environment;
  9. import android.util.Log;
  10. import java.io.File;
  11. @SuppressLint("DefaultLocale")
  12. public class MediaUtil {
  13. private final static String TAG = "MediaUtil";
  14. // 格式化播放时长(mm:ss)
  15. public static String formatDuration(int milliseconds) {
  16. int seconds = milliseconds / 1000;
  17. int hour = seconds / 3600;
  18. int minute = seconds / 60;
  19. int second = seconds % 60;
  20. String str;
  21. if (hour > 0) {
  22. str = String.format("%02d:%02d:%02d", hour, minute, second);
  23. } else {
  24. str = String.format("%02d:%02d", minute, second);
  25. }
  26. return str;
  27. }
  28. // 获得音视频文件的缓存路径
  29. public static String getRecordFilePath(Context context, String dir_name, String extend_name) {
  30. String path = "";
  31. File recordDir = new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString() + "/" + dir_name + "/");
  32. if (!recordDir.exists()) {
  33. recordDir.mkdirs();
  34. }
  35. try {
  36. File recordFile = File.createTempFile(DateUtil.getNowDateTime(), extend_name, recordDir);
  37. path = recordFile.getAbsolutePath();
  38. Log.d(TAG, "dir_name=" + dir_name + ", extend_name=" + extend_name + ", path=" + path);
  39. } catch (Exception e) {
  40. e.printStackTrace();
  41. }
  42. return path;
  43. }
  44. // 获取视频文件中的某帧图片
  45. public static Bitmap getOneFrame(Context ctx, Uri uri) {
  46. MediaMetadataRetriever retriever = new MediaMetadataRetriever();
  47. retriever.setDataSource(ctx, uri);
  48. // 获得视频的播放时长,大于1秒的取第1秒处的帧图,不足1秒的取第0秒处的帧图
  49. String duration = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
  50. Log.d(TAG, "duration="+duration);
  51. int pos = (Integer.parseInt(duration)/1000)>1 ? 1 : 0;
  52. // 获取指定时间的帧图,注意getFrameAtTime方法的时间单位是微秒
  53. return retriever.getFrameAtTime(pos * 1000 * 1000);
  54. }
  55. }
  56. item_select.xml
  57. <TextView xmlns:android="http://schemas.android.com/apk/res/android"
  58. android:layout_width="match_parent"
  59. android:layout_height="50dp"
  60. android:singleLine="true"
  61. android:gravity="center"
  62. android:textSize="17sp"
  63. android:textColor="#0000ff" />

052a4ebc84936231e9be18d720f526ea.png

主代码:

  1. package com.example.myapplication;
  2. import android.media.AudioManager;
  3. import android.media.MediaPlayer;
  4. import android.media.MediaRecorder;
  5. import android.os.Bundle;
  6. import android.text.TextUtils;
  7. import android.util.Log;
  8. import android.view.View;
  9. import android.widget.AdapterView;
  10. import android.widget.ArrayAdapter;
  11. import android.widget.Button;
  12. import android.widget.ImageView;
  13. import android.widget.LinearLayout;
  14. import android.widget.ProgressBar;
  15. import android.widget.Spinner;
  16. import android.widget.TextView;
  17. import android.widget.Toast;
  18. import com.example.myapplication.util.MediaUtil;
  19. import androidx.appcompat.app.AppCompatActivity;
  20. import java.util.Timer;
  21. import java.util.TimerTask;
  22. public class MainActivity extends AppCompatActivity implements View.OnClickListener, MediaRecorder.OnInfoListener {
  23. private static final String TAG = "MediaRecorderActivity";
  24. private Button btn_record;
  25. private LinearLayout ll_progress;
  26. private ProgressBar pb_record; // 声明一个进度条对象
  27. private TextView tv_progress;
  28. private ImageView iv_audio; // 该图标充当播放按钮
  29. private MediaRecorder mMediaRecorder = new MediaRecorder(); // 媒体录制器
  30. private boolean isRecording = false; // 是否正在录制
  31. private int mAudioEncoder; // 音频编码
  32. private int mOutputFormat; // 输出格式
  33. private int mDuration; // 录制时长
  34. private String mRecordFilePath; // 录制文件的保存路径
  35. private Timer mTimer = new Timer(); // 计时器
  36. private int mTimeCount; // 时间计数
  37. private MediaPlayer mMediaPlayer = new MediaPlayer(); // 媒体播放器
  38. @Override
  39. protected void onCreate(Bundle savedInstanceState) {
  40. super.onCreate(savedInstanceState);
  41. setContentView(R.layout.activity_main);
  42. btn_record = findViewById(R.id.btn_record);
  43. ll_progress = findViewById(R.id.ll_progress);
  44. pb_record = findViewById(R.id.pb_record);
  45. tv_progress = findViewById(R.id.tv_progress);
  46. iv_audio = findViewById(R.id.iv_audio);
  47. btn_record.setOnClickListener(this);
  48. iv_audio.setOnClickListener(this);
  49. initEncoderSpinner(); // 初始化音频编码的下拉框
  50. initFormatSpinner(); // 初始化输出格式的下拉框
  51. initDurationSpinner(); // 初始化录制时长的下拉框
  52. }
  53. // 初始化音频编码的下拉框
  54. private void initEncoderSpinner() {
  55. ArrayAdapter<String> encoderAdapter = new ArrayAdapter<String>(this,
  56. R.layout.item_select, encoderDescArray);
  57. Spinner sp_encoder = findViewById(R.id.sp_encoder);
  58. sp_encoder.setPrompt("请选择音频编码"); // 设置下拉框的标题
  59. sp_encoder.setAdapter(encoderAdapter); // 设置下拉框的数组适配器
  60. // 给下拉框设置选择监听器,一旦用户选中某一项,就触发监听器的onItemSelected方法
  61. sp_encoder.setOnItemSelectedListener(new EncoderSelectedListener());
  62. sp_encoder.setSelection(0); // 设置下拉框默认显示第一项
  63. }
  64. private String[] encoderDescArray = {
  65. "默认编码",
  66. "窄带编码",
  67. "宽带编码",
  68. "低复杂度的高级编码",
  69. "高效率的高级编码",
  70. "增强型低延时的高级编码"
  71. };
  72. private int[] encoderArray = {
  73. MediaRecorder.AudioEncoder.DEFAULT,
  74. MediaRecorder.AudioEncoder.AMR_NB,
  75. MediaRecorder.AudioEncoder.AMR_WB,
  76. MediaRecorder.AudioEncoder.AAC,
  77. MediaRecorder.AudioEncoder.HE_AAC,
  78. MediaRecorder.AudioEncoder.AAC_ELD
  79. };
  80. class EncoderSelectedListener implements AdapterView.OnItemSelectedListener {
  81. public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
  82. mAudioEncoder = encoderArray[arg2];
  83. }
  84. public void onNothingSelected(AdapterView<?> arg0) {}
  85. }
  86. // 初始化输出格式的下拉框
  87. private void initFormatSpinner() {
  88. ArrayAdapter<String> formatAdapter = new ArrayAdapter<String>(this,
  89. R.layout.item_select, formatDescArray);
  90. Spinner sp_format = findViewById(R.id.sp_format);
  91. sp_format.setPrompt("请选择输出格式"); // 设置下拉框的标题
  92. sp_format.setAdapter(formatAdapter); // 设置下拉框的数组适配器
  93. sp_format.setSelection(0); // 设置下拉框默认显示第一项
  94. // 给下拉框设置选择监听器,一旦用户选中某一项,就触发监听器的onItemSelected方法
  95. sp_format.setOnItemSelectedListener(new FormatSelectedListener());
  96. }
  97. private String[] formatDescArray = {
  98. "默认格式",
  99. "窄带格式",
  100. "宽带格式",
  101. "高级的音频传输流格式"
  102. };
  103. private int[] formatArray = {
  104. MediaRecorder.OutputFormat.DEFAULT,
  105. MediaRecorder.OutputFormat.AMR_NB,
  106. MediaRecorder.OutputFormat.AMR_WB,
  107. MediaRecorder.OutputFormat.AAC_ADTS
  108. };
  109. class FormatSelectedListener implements AdapterView.OnItemSelectedListener {
  110. public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
  111. mOutputFormat = formatArray[arg2];
  112. }
  113. public void onNothingSelected(AdapterView<?> arg0) {}
  114. }
  115. // 初始化录制时长的下拉框
  116. private void initDurationSpinner() {
  117. ArrayAdapter<String> durationAdapter = new ArrayAdapter<String>(this,
  118. R.layout.item_select, durationDescArray);
  119. Spinner sp_duration = findViewById(R.id.sp_duration);
  120. sp_duration.setPrompt("请选择录制时长"); // 设置下拉框的标题
  121. sp_duration.setAdapter(durationAdapter); // 设置下拉框的数组适配器
  122. sp_duration.setSelection(0); // 设置下拉框默认显示第一项
  123. // 给下拉框设置选择监听器,一旦用户选中某一项,就触发监听器的onItemSelected方法
  124. sp_duration.setOnItemSelectedListener(new DurationSelectedListener());
  125. }
  126. private String[] durationDescArray = {"5秒", "10秒", "20秒", "30秒", "60秒"};
  127. private int[] durationArray = {5, 10, 20, 30, 60};
  128. class DurationSelectedListener implements AdapterView.OnItemSelectedListener {
  129. public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
  130. mDuration = durationArray[arg2];
  131. }
  132. public void onNothingSelected(AdapterView<?> arg0) {}
  133. }
  134. @Override
  135. public void onClick(View v) {
  136. if (v.getId() == R.id.btn_record) {
  137. if (!isRecording) { // 未在录音
  138. startRecord(); // 开始录音
  139. } else { // 正在录音
  140. stopRecord(); // 停止录音
  141. }
  142. } else if (v.getId() == R.id.iv_audio) {
  143. mMediaPlayer.reset(); // 重置媒体播放器
  144. mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); // 设置音频流的类型为音乐
  145. try {
  146. mMediaPlayer.setDataSource(mRecordFilePath); // 设置媒体数据的文件路径
  147. mMediaPlayer.prepare(); // 媒体播放器准备就绪
  148. mMediaPlayer.start(); // 媒体播放器开始播放
  149. } catch (Exception e) {
  150. e.printStackTrace();
  151. }
  152. }
  153. }
  154. // 开始录音
  155. private void startRecord() {
  156. Log.d(TAG, "startRecord mAudioEncoder="+mAudioEncoder+", mOutputFormat="+mOutputFormat+", mDuration="+mDuration);
  157. ll_progress.setVisibility(View.VISIBLE);
  158. isRecording = !isRecording;
  159. btn_record.setText("停止录制");
  160. pb_record.setMax(mDuration); // 设置进度条的最大值
  161. mTimeCount = 0; // 时间计数清零
  162. mTimer = new Timer(); // 创建一个计时器
  163. mTimer.schedule(new TimerTask() {
  164. @Override
  165. public void run() {
  166. pb_record.setProgress(mTimeCount); // 设置进度条的当前进度
  167. tv_progress.setText(MediaUtil.formatDuration(mTimeCount*1000));
  168. mTimeCount++;
  169. }
  170. }, 0, 1000); // 计时器每隔一秒就更新进度条上的录制进度
  171. // 获取本次录制的媒体文件路径
  172. mRecordFilePath = MediaUtil.getRecordFilePath(this, "RecordAudio", ".amr");
  173. // 下面是媒体录制器的处理代码
  174. mMediaRecorder.reset(); // 重置媒体录制器
  175. mMediaRecorder.setOnInfoListener(this); // 设置媒体录制器的信息监听器
  176. mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 设置音频源为麦克风
  177. mMediaRecorder.setOutputFormat(mOutputFormat); // 设置媒体的输出格式。该方法要先于setAudioEncoder调用
  178. mMediaRecorder.setAudioEncoder(mAudioEncoder); // 设置媒体的音频编码器
  179. // mMediaRecorder.setAudioSamplingRate(8); // 设置媒体的音频采样率。可选
  180. // mMediaRecorder.setAudioChannels(2); // 设置媒体的音频声道数。可选
  181. // mMediaRecorder.setAudioEncodingBitRate(1024); // 设置音频每秒录制的字节数。可选
  182. mMediaRecorder.setMaxDuration(mDuration * 1000); // 设置媒体的最大录制时长
  183. // mMediaRecorder.setMaxFileSize(1024*1024*10); // 设置媒体的最大文件大小
  184. // setMaxFileSize与setMaxDuration设置其一即可
  185. mMediaRecorder.setOutputFile(mRecordFilePath); // 设置媒体文件的保存路径
  186. try {
  187. mMediaRecorder.prepare(); // 媒体录制器准备就绪
  188. mMediaRecorder.start(); // 媒体录制器开始录制
  189. } catch (Exception e) {
  190. e.printStackTrace();
  191. }
  192. }
  193. // 停止录音
  194. private void stopRecord() {
  195. isRecording = !isRecording;
  196. btn_record.setText("开始录制");
  197. mTimer.cancel(); // 取消定时器
  198. mMediaRecorder.stop(); // 媒体录制器停止录制
  199. }
  200. @Override
  201. public void onInfo(MediaRecorder mr, int what, int extra) {
  202. // 录制达到最大时长,或者达到文件大小限制,都停止录制
  203. if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
  204. || what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
  205. stopRecord(); // 停止录音
  206. iv_audio.setVisibility(View.VISIBLE);
  207. Toast.makeText(this, "已结束录制,音频文件路径为"+mRecordFilePath, Toast.LENGTH_LONG).show();
  208. }
  209. }
  210. @Override
  211. protected void onStop() {
  212. super.onStop();
  213. if (!TextUtils.isEmpty(mRecordFilePath) && isRecording) {
  214. stopRecord(); // 停止录音
  215. }
  216. if (mMediaPlayer.isPlaying()) { // 如果正在播放
  217. mMediaPlayer.stop(); // 停止播放
  218. }
  219. }
  220. @Override
  221. protected void onDestroy() {
  222. super.onDestroy();
  223. mMediaRecorder.release(); // 释放媒体录制器
  224. mMediaPlayer.release(); // 释放媒体播放器
  225. }
  226. }

0ce20b2a147b0f4b6180b9cb8aaa20c5.png

763c16a5f32e8b9bea418fdbc77d2c16.png

90af797cec6c316827cfb228de41127b.png

7eef8e1f7b1868748127c8f1b59806fa.png

3d7eefa48cbf0a2c4bcebdbd261a8667.png

发表评论

表情:
评论列表 (有 0 条评论,211人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Android 多媒体音频

    在开发上,习惯的将音频、视频功能的使用称之为多媒体,实际上如果讲的宽泛一些的话,相机的使用,比如拍照,录制视频等,也可以划分到多媒体的范畴里面。 从本节课开始,我们就来看看A

    相关 多媒体音频

    播放放在项目中的音频,功能可以做到点击播放、点击暂停、拖动进度条决定音频的播放进度,下面先给大家介绍一下关于多媒体和它的方法: ![Center][] 效果图: !