Android中视频文件的录制

之前我们说了音频的录制和播放,那么现在说说视频的录制。其实视频的录制还是用的MediaRecorder,并且配合Camera进行操作。所以这里我们详细说明一下视频录制的做法。至于视频播放,由于涉及到全屏切换,生命周期管理等一系列问题,这里不写博客说了,感兴趣的同学可以在我的资源中找到我的框架,里面有关于视频的播放案例。

好了,我们开始录制视频的学习。

1.媒体播放器MediaRecorder常用方法(录音与录像通用)

  • reset:重置录制资源。
  • prepare:准备录制。
  • start:开始录制。
  • stop:结束录制。
  • release:释放录制资源。
  • setOnErrorListener:设置错误监听器。可监听服务器异常和未知错误的事件。需要实现接口MediaRecorder.OnErrorListener的onError方法。
  • setOnInfoListener:设置信息监听器。可监听录制结束事件,包括达到录制时长或达到录制大小。需要实现接口MediaRecorder.OnInfoListener的onInfo方法。
  • setMaxDuration:设置可录制的最大时长,单位毫秒。
  • setMaxFileSize:设置可录制的最大文件大小,单位字节。
  • setOutputFile:设置输出文件的路径。

2.MeidaRecorder用于录像的常用方法

  • setCamera:设置相机对象。
  • setPreviewDisplay:设置预览界面。预览界面对象可通过SurfaceHolder对象的getSurface方法获得。
  • setOrientationHint:设置预览的角度。跟拍照一样设置为90,表示界面从水平方向到垂直方向旋转90度。
  • setVideoSource:设置视频来源。一般使用VideoSource.CAMERA表示摄像头。
  • setOutputFormat:设置媒体输出格式。媒体输出格式的取值见表1。
表1
OutputFormat类的输出格式格式分类扩展名格式说明
AMR_NB音频.amr窄带格式
AMR_WB音频.amr宽带格式
AAC_ADTS音频.aac高级的音频传输流格式
MPEG_4视频.mp4MPEG4格式
THREE_GPP视频.3gp3GP格式
  • setVideoEncoder:设置视频编码器。一般使用VideoEncoder.MPEG_4_SP表示MPEG4编码。该方法在setOutputFormat方法之后调用,否则会报错java.lang.IllegalStateException。
  • setVideoSize:设置视频的分辨率。
  • setVideoFrameRate:设置视频每秒录制的帧数。越大视频越连贯,当然最终生成的视频文件也越大。
  • setVideoEncodingBitRate:设置视频每秒录制的字节数。越大视频越清晰,setVideoFrameRate与setVideoEncodingBitRate设置一个即可。

3.代码示例

权限申请

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

布局代码activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:id="@+id/fr_root"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <Button
        android:id="@+id/bt_record"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="录制视频"/>
</RelativeLayout>

MainActivity.java

public class MainActivity extends WaterPermissionActivity implements MediaRecorder.OnErrorListener, MediaRecorder.OnInfoListener {

    private TextView bt_record;
    private FrameLayout fr_root;
    private SurfaceHolder mHolder; // 声明一个表面持有者对象
    private Camera mCamera; // 声明一个相机对象
    private MediaRecorder mMediaRecorder;
    private String moviePath;//视频路径

    @Override
    protected MvcBaseModel getModelImp() {
        return null;
    }

    @Override
    protected int getContentLayoutId() {
        return R.layout.activity_main;
    }

    @Override
    protected void initWidget() {
        fr_root = findViewById(R.id.fr_root);
        bt_record = findViewById(R.id.bt_record);
        bt_record.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    //按下,开始录制
                    getPath();//初始化视频路径
                    initRecord();//开始录制
                } else if (event.getAction() == MotionEvent.ACTION_UP) {
                    //松开,停止录制
                    cancelRecord(); // 取消录制操作
                    freeCamera(); // 释放相机资源
                }
                return true;
            }
        });
        requestPermission(WRITE_EXTERNAL_STORAGE);
    }

    @Override
    protected void doSDWrite() {
        requestPermission(READ_EXTERNAL_STORAGE);
    }

    @Override
    protected void doSDRead() {
        requestPermission(CAMERA);
    }

    @Override
    protected void doCamera() {
        requestPermission(RECORD);
    }

    @Override
    protected void doRecord() {
        //这里为了请求好动态权限再让Surface初始化才这么弄得,可以在进入录制页面就请求好动态权限,就不用这么写了。
        SurfaceView surfaceView = new SurfaceView(this);
        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
        surfaceView.setLayoutParams(params);
        fr_root.addView(surfaceView);
        // 获取表面视图的表面持有者
        mHolder = surfaceView.getHolder();
        // 给表面持有者添加表面变更监听器
        mHolder.addCallback(mSurfaceCallback);
    }

    /**
     * 初始化录制操作,开始录制调用这个方法
     */
    private void initRecord() {
        mMediaRecorder = new MediaRecorder(); // 创建一个媒体录制器
        mMediaRecorder.setCamera(mCamera); // 设置媒体录制器的摄像头
        mMediaRecorder.setOnErrorListener(this); // 设置媒体录制器的错误监听器
        mMediaRecorder.setOnInfoListener(this); // 设置媒体录制器的信息监听器
        mMediaRecorder.setPreviewDisplay(mHolder.getSurface()); // 设置媒体录制器的预览界面
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); // 设置视频源为摄像头
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 设置音频源为麦克风
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); // 设置媒体的输出格式
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); // 设置媒体的音频编码器
        // 如果录像报错:MediaRecorder start failed: -19
        // 试试把setVideoSize和setVideoFrameRate注释掉,因为尺寸设置必须为摄像头所支持,否则报错
         mMediaRecorder.setVideoSize(2280, 1080); // 设置视频的分辨率
        // mMediaRecorder.setVideoFrameRate(16); // 设置视频每秒录制的帧数
        // setVideoFrameRate与setVideoEncodingBitRate设置其一即可
        mMediaRecorder.setVideoEncodingBitRate(1 * 1024 * 512); // 设置视频每秒录制的字节数
        mMediaRecorder.setOrientationHint(90); // 输出旋转90度,也就是保持竖屏录制
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP); // 设置媒体的视频编码器
        mMediaRecorder.setMaxDuration(10 * 1000); // 设置媒体的最大录制时长
        // mMediaRecorder.setMaxFileSize(1024*1024*10); // 设置媒体的最大文件大小
        // setMaxFileSize与setMaxDuration设置其一即可
        mMediaRecorder.setOutputFile(moviePath); // 设置媒体文件的保存路径
        try {
            mMediaRecorder.prepare(); // 媒体录制器准备就绪
            mMediaRecorder.start(); // 媒体录制器开始录制
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 定义一个表面持有者的变更监听器
     */
    private SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
        // 在表面视图创建时触发
        public void surfaceCreated(SurfaceHolder holder) {
            initCamera(); // 初始化相机
        }

        // 在表面视图变更时触发
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        }

        // 在表面视图销毁时触发
        public void surfaceDestroyed(SurfaceHolder holder) {
            freeCamera(); // 释放相机资源
        }
    };

    /**
     * 释放相机资源
     */
    private void freeCamera() {
        if (mCamera != null) {
            mCamera.stopPreview(); // 停止预览
            mCamera.lock(); // 锁定相机,即关闭相机
            mCamera.release(); // 释放相机资源
            mCamera = null;
        }
    }

    /**
     * 初始化相机操作
     */
    private void initCamera() {
        if (mCamera != null) {
            freeCamera();
        }
        try {
            // 打开摄像头,默认后置摄像头
            mCamera = Camera.open();
            // 设置相机的展示角度
            mCamera.setDisplayOrientation(90);
            // 设置相机的预览界面
            mCamera.setPreviewDisplay(mHolder);
            // 开始预览画面
            mCamera.startPreview();
            // 解锁相机,即打开相机
            mCamera.unlock();
        } catch (Exception e) {
            e.printStackTrace();
            freeCamera();
        }
    }

    /**
     * 录制前创建一个空文件并获取路径
     */
    private void getPath() {
        List<String> list = new ArrayList<>();
        list.add("record");
        String dirPath = PathGetUtil.getLongwayPath(this, list);
        File fileDir = new File(dirPath);
        if (!fileDir.exists()) {
            fileDir.mkdirs();
        }
        File fileVoice = new File(dirPath, "movie" + System.currentTimeMillis() + ".mp4");
        if (!fileVoice.exists()) {
            try {
                fileVoice.createNewFile();
                moviePath = fileVoice.getPath();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void onError(MediaRecorder mr, int what, int extra) {
        if (mr != null) {
            mr.reset();  // 重置媒体录制器
        }
    }

    @Override
    public void onInfo(MediaRecorder mr, int what, int extra) {
        // 录制达到最大时长,或者达到文件大小限制,都停止录制
        if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
                || what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
            cancelRecord();
        }
    }

    /**
     * 取消录制操作,停止录制的时候调用该方法
     */
    private void cancelRecord() {
        if (mMediaRecorder != null) {
            mMediaRecorder.setOnErrorListener(null); // 错误监听器置空
            mMediaRecorder.setPreviewDisplay(null); // 预览界面置空
            try {
                mMediaRecorder.stop(); // 媒体录制器停止录制
            } catch (Exception e) {
                e.printStackTrace();
            }
            mMediaRecorder.release(); // 媒体录制器释放资源
            mMediaRecorder = null;
        }
    }
}

同样的,我们录制出来的视频由于没有进行处理,视频质量不是很高,很不清晰。所以如果大家想更加清晰的话,可以考虑把设置的Camera部分换成Camera2或者更好用的CameraX。我这里就不进行具体操作了,原理都是一样的,大家感兴趣可以进行尝试。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Unity录制Android视频,我们可以使用Unity的视频录制功能结合Android的相机模块来实现。具体步骤如下: 1. 首先,确保已经在Unity项目导入了Android的插件包,以便可以使用Android的功能。可以通过File菜单下的Build Settings来导入插件包。 2. 在Unity创建一个脚本来处理视频录制的功能。可以使用Unity的VideoCapture类来实现录制。在脚本,我们需要定义一个全局的VideoCapture对象,并在录制按钮被点击时,调用StartCapture()方法来开始录制视频。 3. 在StartCapture()方法,我们需要通过Unity的相机CaptureDevice来获取相机设备。可以通过WebCamTexture.devices获取所有可用的相机设备,并选择所需的相机。 4. 选择相机设备后,我们需要定义一个保存视频的路径。可以使用Application.persistentDataPath来指定保存的路径,并为视频文件生成一个唯一的文件名。 5. 接下来,我们需要创建一个新的VideoCapture实例,并调用Open方法来打开相机设备,然后通过SetOutputFilePath方法来设置保存视频的路径。 6. 完成设置后,调用StartCapture方法来开始录制视频。可以设置录制的帧率和分辨率等参数。 7. 在视频录制结束后,我们需要调用StopCapture方法来停止录制,然后调用Dispose方法来释放资源。 8. 最后,我们可以通过Android的媒体扫描器来通知系统有新的视频文件生成。可以使用Unity的插件来调用Android的媒体扫描器,并将保存视频的路径作为参数传入。 通过以上步骤,我们可以在Unity实现Android视频录制的功能。请注意,录制视频可能会消耗一定的系统资源,因此在录制时应注意手机的性能和电量问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值