Android (系统+自定义)短视频录制(含暂停继续录制功能) 总结
前言
在Android开发中自然少不了对视频录制的需求,然而视频录制虽然有系统提供给我们能够直接使用的API,但是我们往往在完成需求的过程中需要自定义实现短视频录制。网上虽然也有不少资料,但是总是零零碎碎的,因此自己收集了多方面的资料自己写了一个demo来理解和学习短视频录制,记录在此同时也希望能够给大家带来一些参考和启发。
- 前言
- 一调用系统相机的视频录制
- 二自定义视频录制
- 实现相机预览
- 实现视频录制功能
一、调用系统相机的视频录制
首先我们来了解Andorid系统为我们提供的系统相机的API的实现方式,系统相机相对而言呢好处当然是使用简单,视频清晰啦。
首先记得添加必要的权限
<uses-permission android:name="android.permission.CAMERA" /><uses-permission android:name="android.permission.RECORD_AUDIO" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
因为这里我们主要的关注点在相机的使用上,关于Android6.0的权限管理这里就不做多的插入了,有需要的朋友可以到这儿学习学习啦
- (鸿洋博客)Android 6.0 运行时权限处理完全解析
- (郭霖视频)Android 6.0运行时权限讲解
好的,回到主题,我们调用视频相机录制的核心代码如下
Uri fileUri = Uri.fromFile(getOutputMediaFile());//设置视频录制保存地址的uri
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, 10); //限制持续时长
startActivityForResult(intent, RECORD_SYSTEM_VIDEO);
简单讲解一下,这个MediaStore.ACTION_VIDEO_CAPTURE 顾名思义就是我们调用系统视频录制的action。开始我们应该设置一个Uri作为我们想要保存视频的路径,如果我们不传递自定义的路径,那么系统就会将录制后的视频保存在默认的路径(一般在DCIM/Camera)下。限制的视频录制时间单位是秒(s),这里我们就是设置成为10s。
好了,下面贴出这一段完整的demo代码
public static final int RECORD_SYSTEM_VIDEO = 1;private VideoView mVideoView; //用来显示播放录制后的视频@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mVideoView = (VideoView) findViewById(R.id.videoView);}/*** 启用系统相机录制** @param view*/public void reconverIntent(View view) {Uri fileUri = Uri.fromFile(getOutputMediaFile());Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, 10); //限制的录制时长 以秒为单位//intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY,1); //设置拍摄的质量最小是0,最大是1(建议不要设置中间值,不同手机似乎效果不同。。。)//intent.putExtra(MediaStore.EXTRA_SIZE_LIMIT, 1024 * 1024);//限制视频文件大小 以字节为单位 startActivityForResult(intent, RECORD_SYSTEM_VIDEO);}private File getOutputMediaFile() { if(!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()){Toast.makeText(this, "请检查SDCard!", Toast.LENGTH_SHORT).show();return null;}File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "MyCameraApp");if (!mediaStorageDir.exists()) {mediaStorageDir.mkdirs();}// Create a media file nameString timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());File mediaFile = new File(mediaStorageDir.getPath() + File.separator + "VID_" + timeStamp + ".mp4");return mediaFile;}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (resultCode != RESULT_OK) {return;}switch (requestCode) {case RECORD_SYSTEM_VIDEO:mVideoView.setVideoURI(data.getData());mVideoView.start();break;}}
这里是layout布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context="com.liuzhongjun.cameratest.my.MainActivity"><Button
android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="reconverIntent"android:text="启动系统视频录制视频"android:textAllCaps="false" /><Button
android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="customVideo"android:text="启动自定义相机录制视频" /><VideoView
android:id="@+id/videoView"android:layout_width="match_parent"android:layout_height="match_parent" />
</LinearLayout>
- 多说一点,因为国内某些手机对相机做了系统上的修改,可能通过我们传递的fileUri无法保存到相对应的位置,因此我们可以在onActivityResult的回调数据中通过下面的方法自行保存视频
try {AssetFileDescriptor videoAsset = getContentResolver().openAssetFileDescriptor(data.getData(), "r");FileInputStream fis = videoAsset.createInputStream();File tmpFile = new File(Environment.getExternalStorageDirectory(),"VideoFile.mp4"); FileOutputStream fos = new FileOutputStream(tmpFile);byte[] buf = new byte[1024];int len;while ((len = fis.read(buf)) > 0) {fos.write(buf, 0, len);} fis.close();fos.close();} catch (IOException io_e) {// TODO: handle error}
- 同时针对与系统相机录制其他参数有兴趣的朋友,可以到官网去详细了解MediaStore.ACTION_VIDEO_CAPTURE的其他参数
OK,简单的使用就完成了,感觉时候还是很简单吧。然而优点虽然是简单好用,缺点就是无法自定义,相机默认录制的视频又过大(曾经用华为mate9亲测录制10s的视频大小就达到了19M多,Mi2s也差不多有10M,不同的手机因为手机分频率的不同就能够有很大的差别),按这样下去用户录制一点视频上传所耗费流量和时间都不少了啊,然而通过我们的自定义视频录制,10s的视频我们能够控制在2M以内,另外我们也可以加入视频录制的暂停和继续,同时可以更加需求做出各种各样的自定义功能。噼里啪啦说了这么多好处,那么就开始我们的自定义视频录制之旅吧!
二、自定义视频录制
因为我们在这里会使用到自定义相机的某些硬件特性,因此我们还需要在AndroidManifest.xml中加入如下特征(之前的系统相机所需要的权限这里同样需要),保证一些功能的正常使用。
<uses-feature
android:name="android.hardware.camera"android:required="true" /><uses-feature android:name="android.hardware.camera.autofocus" />
自定义视频录制相对系统相机录制而言较为复杂,在正式使用之前我们先来了解这个类 MediaRecorder
点击会链接到官网该类API的介绍,我们可以知道通过MediaRecorder我们就可以实现视频与音频的录制并将其合并为标准的视频格式。
另外如果英语还不错或者想要了解官方资料的,对于自定义音视频录制的实现流程完全可用通过这里:
- 高能 Camera API 使用的正确姿势(含盖详细的视频录制流程) 了解并熟悉视频录制的重要流程(其实网上的大部分资料都是通过官网的资料衍生而来的,同时不免会带有一些主观的内容,事实上有足够的学习能力者通过官网学习效果说不定会更好。
接下来让我们好好分析下实现流程:
1.首先我们需要实现相机的预览(这里就需要通过Camera+SurfaceView实现自定义相机的显示)
2.监听录像按钮,在点击录像的同时完成对MediaRecorder初始化和参数配置,启动音视频录制。
OK,有了思路就就明白要怎么走了。首先我们需要实现相机的预览
1.实现相机预览
首先我们需要有显示界面相应的布局文件
<?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"android:gravity="center"><SurfaceView
android:id="@+id/record_surfaceView"android:layout_width="match_parent"android:layout_height="match_parent" /><RelativeLayout
android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:background="#20ffffff"android:padding="10dp"><!-- 开始/结束 录制按钮 --><ImageView
android:id="@+id/record_control"android:layout_width="60dp"android:layout_height="60dp"android:layout_centerInParent="true"android:contentDescription="@string/begin"android:onClick="startRecord"android:src="@drawable/recordvideo_start" /><Chronometer
android:id="@+id/record_time"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentEnd="true"android:format="%s" /></RelativeLayout>
</RelativeLayout>
接下来是实现代码
public class CustomRecordActivity extends AppCompatActivity implements View.OnClickListener {//UIprivate ImageView mRecordControl;private SurfaceView surfaceView;private SurfaceHolder mSurfaceHolder;private Chronometer mRecordTime;//DATAprivate boolean isPause; //暂停标识private boolean isRecording; // 标记,判断当前是否正在录制private long mRecordCurrentTime = 0; //录制时间间隔// 存储文件private File mVecordFile;private Camera mCamera;private MediaRecorder mediaRecorder;private MediaRecorder.OnErrorListener onErrorListener = new MediaRecorder.OnErrorListener() {@Overridepublic void onError(MediaRecorder mediaRecorder, int what, int extra) {try {if (mediaRecorder != null) {mediaRecorder.reset();}} catch (Exception e) {e.printStackTrace();}}};@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_custom);initView();}private void initView() {surfaceView = (SurfaceView) findViewById(R.id.record_surfaceView);mRecordControl = (ImageView) findViewById(R.id.record_control);mRecordTime = (Chronometer) findViewById(R.id.record_time);mRecordControl.setOnClickListener(this);//配置SurfaceHodlermSurfaceHolder = surfaceView.getHolder();// 设置Surface不需要维护自己的缓冲区mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);// 设置分辨率mSurfaceHolder.setFixedSize(320, 280);// 设置该组件不会让屏幕自动关闭mSurfaceHolder.setKeepScreenOn(true);mSurfaceHolder.addCallback(mCallBack); //相机创建回调接口}private SurfaceHolder.Callback mCallBack = new SurfaceHolder.Callback() {@Overridepublic void surfaceCreated(SurfaceHolder surfaceHolder) {initCamera();}@Overridepublic void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {if (mSurfaceHolder.getSurface() == null) {return;}}@Overridepublic void surfaceDestroyed(SurfaceHolder surfaceHolder) {stopCamera();}};/*** 初始化摄像头** @author liuzhongjun* @date 2016-3-16*/private void initCamera() {if (mCamera != null) {stopCamera();}//默认启动后置摄像头mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);if (mCamera == null) {Toast.makeText(this, "未能获取到相机!", Toast.LENGTH_SHORT).show();return;}try {mCamera.setPreviewDisplay(mSurfaceHolder);//配置CameraParamssetCameraParams();//启动相机预览mCamera.startPreview();} catch (IOException e) {Log.d(TAG, "Error starting camera preview: " + e.getMessage());}}/*** 设置摄像头为竖屏** @author liuzhongjun* @date 2016-3-16*/private void setCameraParams() {if (mCamera != null) {Camera.Parameters params = mCamera.getParameters();//设置相机的很速屏幕if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {params.set("orientation", "portrait");mCamera.setDisplayOrientation(90);} else {params.set("orientation", "landscape");mCamera.setDisplayOrientation(0);}//设置聚焦模式params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);//缩短Recording启动时间params.setRecordingHint(true);//是否支持影像稳定能力,支持则开启 if (params.isVideoStabilizationSupported()) params.setVideoStabilization(true);mCamera.setParameters(params);}}/*** 释放摄像头资源** @author liuzhongjun* @date 2016-2-5*/private void stopCamera() {if (mCamera != null) {mCamera.setPreviewCallback(null);mCamera.stopPreview();mCamera.release();mCamera = null;}}@Overridepublic void onClick(View view) {switch (view.getId()) {case R.id.record_control:if (!isRecording) {//开始录制视频} else {//停止视频录制}break;} }
}
通过上面的代码即可实现了自定义相机的显示,能够有预览图像了。
另外,建议我们在AndroidManifest.xml声明的时候,设置screenOrientation为一直竖屏,这样能够防止录像时横竖切换造成的数据丢失(我看到的系统相机也都是固定为竖屏的,什么?你说看到横屏的录像?还不拿来我看看?),设置代码如下:
<activity
android:name=".my.CustomRecordActivity"android:screenOrientation="portrait">
</activity>
2.实现视频录制功能
来吧,将MediaRecord的配置流程按照官网的样子来一波吧!(图片来自Android开发者官网)
原文是英文,这里我通过谷歌浏览器将其翻译了方便大家更好的观看
这一个系列标准的步骤中,我们需要着重理解的是第4步的开始录制视频:首先我们必须按照这样的顺序来配置MediaRecorder,what?你问我为什么?没有为什么想搞懂就去看源码去!(反正我知道你不按照套路来你就不行)。
ok,下面是实现的demo代码,相对于官方的demo做了一些优化和调整。
/*** 开始录制视频*/public void startRecord() {boolean creakOk = createRecordDir();if (!creakOk) {return;}initCamera();mCamera.unlock();setConfigRecord();try {//开始录制mediaRecorder.prepare();mediaRecorder.start();} catch (IOException e) {e.printStackTrace();}isRecording = true;if (mRecordCurrentTime != 0) {mRecordTime.setBase(SystemClock.elapsedRealtime() - (mRecordCurrentTime - mRecordTime.getBase()));} else {mRecordTime.setBase(SystemClock.elapsedRealtime());}mRecordTime.start();}/*** 停止录制视频*/public void stopRecord() {if (isRecording && mediaRecorder != null) {// 设置后不会崩mediaRecorder.setOnErrorListener(null);mediaRecorder.setPreviewDisplay(null);//停止录制mediaRecorder.stop();mediaRecorder.reset();//释放资源mediaRecorder.release();mediaRecorder = null;mRecordTime.stop();//设置开始按钮可点击,停止按钮不可点击mRecordControl.setEnabled(true);mPauseRecord.setEnabled(false);isRecording = false;}}/*** 创建视频文件保存路径*/private boolean createRecordDir() {if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {Toast.makeText(this, "请查看您的SD卡是否存在!", Toast.LENGTH_SHORT).show();return false;}File sampleDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "Record");if (!sampleDir.exists()) {sampleDir.mkdirs();}String recordName = "VID_" + new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()) + ".mp4";mVecordFile = new File(sampleDir, recordName);return true;}/*** 配置MediaRecorder()*/private void setConfigRecord() {mediaRecorder = new MediaRecorder();mediaRecorder.reset();mediaRecorder.setCamera(mCamera);mediaRecorder.setOnErrorListener(OnErrorListener);//使用SurfaceView预览mediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());//1.设置采集声音mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//设置采集图像mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);//2.设置视频,音频的输出格式 mp4mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);//3.设置音频的编码格式mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);//设置图像的编码格式mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);//设置立体声
// mediaRecorder.setAudioChannels(2);//设置最大录像时间 单位:毫秒
// mediaRecorder.setMaxDuration(60 * 1000);//设置最大录制的大小 单位,字节
// mediaRecorder.setMaxFileSize(1024 * 1024);//音频一秒钟包含多少数据位CamcorderProfile mProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P);mediaRecorder.setAudioEncodingBitRate(44100);if (mProfile.videoBitRate > 2 * 1024 * 1024)mediaRecorder.setVideoEncodingBitRate(2 * 1024 * 1024);elsemediaRecorder.setVideoEncodingBitRate(1024 * 1024);mediaRecorder.setVideoFrameRate(mProfile.videoFrameRate);//设置选择角度,顺时针方向,因为默认是逆向90度的,这样图像就是正常显示了,这里设置的是观看保存后的视频的角度mediaRecorder.setOrientationHint(90);//设置录像的分辨率mediaRecorder.setVideoSize(352, 288);mediaRecorder.setOutputFile(mVecordFile.getAbsolutePath());}
核心代码就这么些啦,感觉重要部分注释已经标注的很清楚了!
有什么建议和问题欢迎大家提出。
源代码下载地址(含视频录制的暂停继续):https://github.com/Gentleman-jun/VideoRecordDemo
Android (系统+自定义)短视频录制(含暂停继续录制功能) 总结相关推荐
- 仿android微信视频编辑,Android 仿微信短视频录制
VideoRecorder Android 仿微信短视频录制 预览 Bug 修复与更新日志: 更新日志: 1.2.0:仿照微信,短按拍照长按拍摄 --19.06.21 1.1.5:增加进度条,修改依赖 ...
- Android 仿微信短视频录制
VideoRecorder 项目地址:junerver/VideoRecorder 简介: Android 仿微信短视频录制 更多:作者 提 Bug 标签: Android 仿微信短视频录制 项目 ...
- 短视频技术详解:Android端的短视频开发技术
在 <如何快速实现移动端短视频功能?>中,我们主要介绍了当前短视频的大热趋势以及开发一个短视频应用所涉及到的功能和业务.在本篇文章中,我们主要谈一谈短视频在Android端上的具体实现技术 ...
- Android端的短视频开发技术
在上一篇 <快速实现移动端短视频功能?没你想得那么难!>文章中,我们主要介绍了当前短视频的大热趋势以及开发一个短视频应用所涉及到的功能和业务.在这篇文章中,我们主要谈一谈短视频在Andro ...
- (H5+安卓+ios)直播app系统源码,直播商城带货源码,方维直播app系统,直播短视频源码
商品介绍 (H5+安卓+ios)直播app系统源码,直播商城带货源码,方维直播app系统,直播短视频源码,本系统有三个版本:带商城直播系统,带H5直播系统,和一般的直播短视频系统,默认带H5版本系统 ...
- 第三讲:如何使用群控系统在短视频平台营销
第三讲:如何使用群控系统在短视频平台营销 短视频的兴起,直接前提是移动智能终端的全面普及,以及4G信号的全面铺开,人们对移动智能设备(通俗说就是智能手机)的依赖度越来越高,短视频以其相对强烈的在视觉和 ...
- 短视频源码教程之短视频app制作如何实现合拍功能
当用户发现比较搞笑的视频时,总是想进行模仿的,在短视频app制作合拍功能之前,我们至少需要两部手机,一部负责播放原视频,一部负责拍摄我们自己,非常不便,而现在,合拍功能应运而生,本篇文章是关于在短视频 ...
- 短视频程序源码,实现ios系统的短视频缓存
1.业务背景 短视频程序源码实现视频播放,对于ios开发来说其实并不是一个难事儿,简单几行代码就能实现,确实,最初的短视频播放也是基于此,给定视频url直接丢给系统播放器(AVPlayer)就可以播放 ...
- vue完美模拟pc版快手实现短视频,含短视频详情播放
目录 一.预览 二.效果图 项目实现的demo效果图: 三.项目细节说明 1.项目结构.设计说明 2.项目可拓展能力题外话(看不懂可以忽略) 3.项目路由配置 4.框架布局页面源码 5.首页实现 四. ...
最新文章
- c语言中count的头文件,求助C语言大佬 , 只会写到一个.c文件里 ,不会用.h头文件...
- linux shell编程学习笔记(9)正则表达式
- SonarQube代码质量管理平台安装与配置
- 解读百度Q4财报:智能云以三大关键词进位“第二引擎”,强势驱动百度未来
- Vases and Flowers HDU - 4614
- 北京大学 软件工程1 软件 软件工程 软件开发 软件工程框架
- git切换用户密码_Git 最基本的命令
- python elif可以单独使用_Celery在python中的单独使用
- 10W 赞的程序员高薪职业建议
- 项目管理系统、工作台、经营看板、质量管理、合同管理、合同审核、新建合同、分包商管理、立项审批、创建项目、项目模板、项目统计、计划管理、结项申请、审批流程、审批记录、审批状态、参数设置、axure原型
- 计算机网络学习笔记(0. 引言)
- sklearn——model_selection——knn手写识别系统+iris分类
- 靠谱么?人工智能为《我是歌手4》“占卜”
- 如何提升代码的安全性 —— 代码防御性编程的十条技巧
- 完美世界服务器维护多久,完美世界服务端启动和维护
- Java8之后,使用jdbc连接本地access数据库。DEMO
- python tkinter ttk的使用(下)
- FPGA开发——SRIO
- KubeSphere 部署
- 如何搭建企业报表管理系统?
热门文章
- 【Vivado那些事】Xilinx 7系列时钟结构详解
- .NET-7.WPF学习2. 知识总结
- ei和elsiver
- python开源项目框架二次开发_Python中三大框架各自的应用场景(DJango,flask,Tornado)...
- 傅里叶(三):傅里叶变换的推导
- pytorch实现BiLSTM代码
- 人人商城[二开]任意位置调用插件函数功能
- 正规券商交易所系统用什么API接口?
- 通知!2022年成都市科学技术局关于组织申报重点研发计划(重大科技专项)、科技创新基地(平台)和人才计划项目指南
- 张尧学为什么不愿意提“普适计算”?是不是怕粘包?