Android多媒体之MediaRecorder


前言

Android系统提供MediaRecorder类,可以通过他对音频和视频以及屏幕进行录制。

一、MediaRecorder状态图

音频/视频文件和流的录制控制作为状态机进行管理。下图显示了受支持的录制控制操作驱动的 MediaRecorder对象的生命周期和状态。椭圆表示 MediaRecorder对象可能驻留的状态。弧表示驱动对象状态转换的录制控制操作。有两种类型的弧。带单箭头的弧表示同步方法调用,带双箭头的弧表示异步方法调用。

MediaRecorder 状态图

  1. 使用MediaRecorder构造方法可以构造一个MediaRecorder,这时候就是Initial 状态。在其它状态下调用 reset()后,也会进入inital.
  2. 在使用setAudioSource()或setVideoSource()后,进入Initalized状态。
  3. 调用setOutputFormat()设置录制格式后,进入DataSourceConfigured状态,在该状态下,可以对录制的数据进行加密等其它设置。
  4. 调用prepare(),进入prepared状态。
  5. 只有prepared状态下才能调用 strart()进入 Recording状态。在该状态下,调用stop()和reset()可以停止录制。
  6. 在不需要录制后,调用release()进入released状态,当前状态表示结束,需要重新构造MediaRecorder,才能进入intial状态.
  7. 注册setOnErrorListener 监听,可以方便记录在运行出现的错误信息。为了方便结束这些信息,MediaRecorder对象需要运行在Loooper的线程上创建,(默认情况下,主线程已经运行了一个Looper)。

二、录制

1、音频录制

步骤:

  1. 调用MediaRecorder构造方法获取新实例。
  2. 使用setOutputFormat()设置输出文件格式。
  3. 使用setOutputFile()设置输出文件名。
  4. 使用setAuidoEncoder()设置音频编码器。
  5. 通过prepare()完成初始化。
  6. 通过start()和stop()开启和停止录制功能。
  7. 使用完MediaRecorder实例后,使用release()来释放其资源。

在Andoroid 9(API 28)之后,在后台运行的应用无法访问麦克风。因此只有应用位于前台,或者在前台服务添加MediaRecorder实例时才能录制。
从Android 8(API 26)开始,可以使用MediaMuxer来录制多个同步的音频和视频流。在更低的版本只能录制一个音轨或视频轨道。

2、Camera 录制

使用 Android 框架捕获视频需要注意对 Camera 对象的管理以及与 MediaRecorder 类的协调。使用 Camera 录制视频时,您必须管理 Camera.lock() 和 Camera.unlock() 调用,以允许 MediaRecorder 访问相机硬件,Camera.open() 和 Camera.release() 调用除外。

从 Android 4.0(API 级别 14)开始,系统将自动为您管理Camera.lock() 和 Camera.unlock() 调用。

步骤:

  1. 打开相机 - 使用 Camera.open() 获取相机对象实例。
  2. 连接预览 - 使用 Camera.setPreviewDisplay() 将 SurfaceView 连接到相机,以准备实时相机图像预览。
  3. 开始预览 - 调用 Camera.startPreview() 以开始显示实时相机图像。
  4. 开始录制视频 - 要成功录制视频,必须完成下列步骤:
1、解锁相机 - 通过调用 Camera.unlock() 解锁相机以供 MediaRecorder 使用。

2、配置 MediaRecorder - 按此顺序在以下 MediaRecorder 方法中调用。
	setCamera() - 设置要用于视频捕获的相机,使用应用程序的当前 Camera 实例。
	
	setAudioSource() - 设置音频源,使用 MediaRecorder.AudioSource.CAMCORDER。
	
	setVideoSource() - 设置视频源,使用 MediaRecorder.VideoSource.CAMERA。

	设置视频的输出格式和编码。对于 Android 2.2(API 级别 8)及更高版本,请使用 MediaRecorder.setProfile 方法,并使用 CamcorderProfile.get() 获取配置文件实例。对于 Android 2.2 之前的版本,您必须设置视频的输出格式和编码参数:
	setOutputFormat() - 设置输出格式,指定默认设置或 MediaRecorder.OutputFormat.MPEG_4。
	setAudioEncoder() - 设置声音编码类型,指定默认设置或 MediaRecorder.AudioEncoder.AMR_NB。
	setVideoEncoder() - 设置视频编码类型,指定默认设置或 MediaRecorder.VideoEncoder.MPEG_4_SP。
	
	setOutputFile() - 设置输出文件,使用保存媒体文件部分示例方法中的 	getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()setPreviewDisplay() - 为您的应用指定 SurfaceView 预览布局元素。使用您在连接预览部分中指定的相同对象。
	
	3、准备 MediaRecorder - 通过调用 MediaRecorder.prepare() 提供的配置设置准备 MediaRecorder4、启动 MediaRecorder - 通过调用 MediaRecorder.start() 开始录制视频。
  1. 停止录制视频 - 依次调用以下方法,以便成功完成视频录制
1、停止 MediaRecorder - 通过调用 MediaRecorder.stop() 停止视频录制。

2、重置 MediaRecorder - (可选)通过调用 MediaRecorder.reset() 从录制器中移除配置设置。

3、释放 MediaRecorder - 通过调用 MediaRecorder.release() 释放 MediaRecorder4、锁定相机 - 锁定相机以便未来的 MediaRecorder 会话可通过调用 Camera.lock() 使用它。从 Android 4.0(API 级别 14)开始,除非 MediaRecorder.prepare() 调用失败,否则不需要此调用。
  1. 停止预览 - 当 Activity 结束对相机的使用时,使用 Camera.stopPreview() 停止预览。
  2. 释放相机 - 释放相机以便其他应用可通过调用 Camera.release() 使用它。

如果应用通常用于录制视频,请在开始预览之前将 setRecordingHint(boolean) 设置为 true。此设置有助于减少开始录制所需的时间。

3、屏幕录制

Android 从 4.0 开始就提供了手机录屏方法,但是需要 root 权限,比较麻烦不容易实现。但是从 5.0 开始,供给了 App 录制屏幕的一系列方法,通过MediaProjection的屏幕采集接口,和系统级服务MediaProjectionManager进行管理。

为了保护用户的屏幕内容,Android 10 更改了 READ_FRAME_BUFFER、CAPTURE_VIDEO_OUTPUT 和 CAPTURE_SECURE_VIDEO_OUTPUT 权限的作用域,从而禁止以静默方式访问设备的屏幕内容。从 Android 10 开始,这些权限只能通过签名访问。并且必须开启前台服务。

如果不使用上面的逻辑访问屏幕类容,会报如下错误:

java.lang.SecurityException: Media projections require a foreground service 
of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION

在 Manifest 中需要的权限:

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

步骤:

  1. 请求权限通过后,请求获取录屏权限。
 MediaProjectionManager mProjectionManager = ((MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE));
        if (mProjectionManager != null) {
            Intent intent = mProjectionManager.createScreenCaptureIntent();
         	startActivityForResult(intent, 1);
         }
  1. 用户同意屏幕录取权限,通过onActivityResult的返回Code和data获取到授权MediaProjection实例。
MediaProjectionManager projectionManager = (MediaProjectionManager)getSystemService(Context.MEDIA_PROJECTION_SERVICE);

MediaProjection  mMediaProjection = projectionManager.getMediaProjection(mResultCode, Objects.requireNonNull(mResultData));
  1. 启动一个前台服务Notification(Android 10开始必须要)。
  2. 上面步骤完成后,后续的录制步骤可参考Camera。
  3. 停止录制还需要调mediaProjection.stop(),最后记得释放资源。

三、案例

1、音频录制

package com.cs.mediarecordertest.audiorecorder;

import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.cs.mediarecordertest.R;
import java.io.File;
import java.io.IOException;

import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;

import static android.os.Environment.DIRECTORY_MOVIES;

public class AudioRecorderActivity extends AppCompatActivity {
    private static final String TAG = "AudioRecorderActivity";

    private String mPath;
    private MediaPlayer player;
    private String mFileName;
    private MediaRecorder recorder;
    public static final int RECORDING = 1;
    public static final int PLAYING = 2;
    public static final int IDLE = 0;

    private int mState = IDLE;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_audiorecorder);
        hide();
        initView();

    }


    private void hide() {
        // Hide UI first
        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            actionBar.hide();
        }

    }

    private boolean checkState() {

        switch (mState) {
            case RECORDING:
                Toast.makeText(this, "当前录制状态,请先结束录制!", Toast.LENGTH_SHORT).show();
                return false;
            case PLAYING:
                Toast.makeText(this, "当前播放状态,请先结束播放!", Toast.LENGTH_SHORT).show();
                return false;


        }

        return true;
    }

    private void initView() {

        MediaPlayer player = new MediaPlayer();
        MediaPlayer.TrackInfo[] trackInfo = player.getTrackInfo();
        player.selectTrack(1);
        File filesDir = getExternalFilesDir(DIRECTORY_MOVIES);
        mPath = filesDir.getAbsolutePath();
        mFileName = mPath + "/audio_1.3gp";
    }


    public void startRecording() {
        if (!checkState()) {
            return;
        }
        mState = RECORDING;
        recorder = new MediaRecorder();
        recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
        recorder.setOutputFile(mFileName);
        recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);

        try {
            recorder.prepare();
        } catch (IOException e) {
            Log.e(TAG, "prepare() failed");
            mState = IDLE;
        }

        recorder.start();
    }

    public void stopRecording() {

        if (recorder != null) {
            recorder.stop();
            recorder.release();
            recorder = null;
        }else{
            Toast.makeText(this, "当前已是空闲状态!", Toast.LENGTH_SHORT).show();
        }
        mState = IDLE;
    }


    public void onAudioRecorderStart(View view) {
        startRecording();
    }

    public void onAudioRecorderStop(View view) {
        stopRecording();
    }

    public void onPlayAudio(View view) {
        if (!checkState()) {
            return;
        }
        mState = PLAYING;
        player = new MediaPlayer();
        try {
            player.setDataSource(mFileName);
            player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mp) {
                    mState = IDLE;
                }
            });
            player.prepare();
            player.start();

        } catch (IOException e) {
            Log.e(TAG, "prepare() failed");
            mState = IDLE;
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (recorder != null) {
            recorder.release();
            recorder = null;
        }

        if (player != null) {
            player.release();
            player = null;
        }
    }

}

这里的案例都是非常检漏的code,只能勉强看见效果,有许多不完善的地方。
因为没有写动态权限申请,需要安装成功后去设置里面,手动把APP需要的权限打开。
另外的两个录制案例在https://github.com/zyjy33/MediaRecorderTest.git可供参考。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zyjy33

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值