Android Camera各个API录像实践
前言
上次刚写了《Android相机各个API拍照实践》,实际上用Camera1、Camera2、CameraX三个API的录像功能,我这也写好了,和拍照类似,但是比拍照坑更多,花了我挺多时间调试的,下面记录下。
目标
还是和上一篇文章一样,先明确下目标:
- 能够使用Android三种API预览、录像、播放结果
- 能够在录像过程中对视频进行放大,类似微信录小视频
- 三种API能够自由切换,互不干扰
- 能够拿到拍照结果,并保存到系统录像位置
上一篇文章的代码里面还加上了系统拍照、系统选取、系统分享、保存相册等,这里就不重复了,只要保存到DCIM就行了。
效果图
这里还是可以先搞张效果图看下的,我觉得最后弄的还凑合:
接口封装
本来想把拍照和录像写到同一个接口的,后面发现预览还是有一些差别的,就另外建了个接口:
1 | kotlin复制代码import androidx.activity.ComponentActivity |
startPreview和release还是一样,不过takePhoto换成了另外三个方法,startRecord开始录像,stopRecord停止录像并获取结果,zoom能够在录像过程中放大视频。
自定义相关View
录制按钮
要想录像,首先还是要有个像样的按钮,我瞄了一样微信录像的按钮,自己也搞了个,主要有下面点功能:
- 能够触发开始录像、放大、停止录像三种事件
- 开始录像时有过场动画
- 按钮可以移动,移动距离和放大倍数相关联,页面显示放大倍数
- 能够记录录像时间,在外圈更新进度
- 有最大录制时长,到达该值时停止录制
然后,我就写了下面一个自定义的按钮,用来录像,效果如上面的Gif图:
因为本篇文章的重点不上这个按钮,这里就不详细介绍了,不是很复杂,代码里面注释也挺清楚。
类似ViewPager的带缩放的容器
为了能够在一个页面能够显示三个录像功能,我这又把RecordButton放到了一个能缩放的容器里面,也是根据我之前自定义View事件的控件改造的:
《自定义view实战(7):大小自动变换的类ViewPager》
稍微对这个控件改造了下,去掉了很多东西,只保留这个缩放功能,控件源码如下:
当然这个控件也是锦上添花罢了,我们这篇文章的重点是用三种API去录像。
使用Camera1 API
搞定录像按钮的问题,我们就能来编写三种API的代码了。
Camera1预览
首先是Camera1的预览:
1 | kotlin复制代码/** |
和拍照一模一样,按顺序执行这七步就行,具体代码后面给出。同样,因为是工具类mSurfaceCallback的surfaceCreated收不到,默认已经created了,可以直接open Camera。
Camera1录像
Camera1的录像需要用到MediaRecorder,MediaRecorder使用前注意要先预览,下面看下预览代码:
1 | kotlin复制代码/** |
实际就是创建一个mMediaRecorder对象,设置好相关参数,调用start进行录制就可以。
这里代码看起来简单,实际有好多坑,下面一个一个讲。
首先是mCamera的unlock一定要调用,而且在stopRecord的时候还要调用lock方法,目的就是释放相机资源,给mMediaRecorder使用。
其次就是这里的参数设置是有顺序的,不要乱改顺序,当然读者也可以改下顺序,看下哪些会有问题。
在一个就是setVideoSize方法一定要传入合适的尺寸,不然会出问题,可能就是prepare抛出异常,造成闪退,我这没try-catch prepare方法,因为prepare失败了也没法用啊。
Camera1缩放
Camera1的缩放比较简单,拿到mCamera的params进行修改就行了,重新设置即生效。
1 | kotlin复制代码override fun zoom( |
Camera1停止录像
停止录像调用mMediaRecorder的stop方法即可,不过这里还要reset一下,不然无法继续拍照。
1 | kotlin复制代码override fun stopRecord(activity: ComponentActivity, callback: Consumer<String>) { |
mCamera的lock上面有提到,Camera1拍完照之后不会再预览了,需要手动调用下。
这里有个坑就是,录制时间过短的话stop会失败,造成闪退,找了很久也没找到这个最短时间,我就不如catch这个异常,直接传出去算了。
Camera1释放资源
加了一个mMediaRecorder,需要注意它的释放。
1 | kotlin复制代码/** |
完整代码
使用Camera2 API
使用Camera1进行录像相对来说还是比较简单的,只不过就是加了一个MediaRecorder,到了Camera2感觉就头疼了,下面看下吧。
这里先说一下啊,我这用的Camera2 API录像可能不是最佳选择,下面我把预览和拍照分成了两个独立的Session,实际是可以写成一个的,只不过会把预览和录像搞在一起,看需要吧。
Camera2预览
前面说了,我把预览和拍照分成了两个独立的Session,所以这里Camera2预览就仅仅需要预览罢了,下面看代码:
1 | kotlin复制代码/** |
和拍照的预览相对比,去掉了一个ImageReader的配置,然后就是着重写了下尺寸的获取:
1 | kotlin复制代码private fun getSizes() { |
这个很重要,这里一定要选对尺寸,不然后面MediaRecorder的prepare就是过不去。其他很好理解,使用可以看下完整代码。
Camera2录像
Camera2录像和Camera1的录像比起来就复杂了,先看下代码,我尽量把要注意的细节说一下:
1 | kotlin复制代码/** |
预览Session切换
首先,这里要先把预览的session给关了,创建一个带预览和录像的session进行录像,我这都用mPreviewSession去保存,但要注意下这里有两个session:
1 | kotlin复制代码private fun closePreviewSession() { |
下面是创建新的Session:
1 | kotlin复制代码// 创建新的预览对话,能将视频输出到录像surface |
MediaRecorder配置
其次,MediaRecorder的配置也比较容易出错:
1 | kotlin复制代码private fun setUpMediaRecorder(activity: ComponentActivity, callback: Consumer<String>) { |
这里MediaRecorder设置setVideoSource为MediaRecorder.VideoSource.SURFACE后,它自己就带了一个surface,我们要通过MediaRecorder的getSurface,向里面传递数据。
这里的参数顺序也要注意下,最最最坑的就是这个尺寸了,这里不能和拍照一样使用最大尺寸,使用的话就闪退,后面我重写了getSizes方法,才让prepare生效,实际就是(width=1920, height=1080),不过还是要根据机型决定:
1 | kotlin复制代码private fun getSizes() { |
如果发生各种异常,代码又觉得没错,那估计就是你的尺寸出错了,很坑。
录像请求
上面切换Session后,要注意把预览和录像的surface传进去,这里是两个surface了:
1 | kotlin复制代码// 设置预览输出 |
切换session并发送请求后,就可以路线了:
1 | kotlin复制代码// 录像请求 |
总而言之,Camera2的录像比较复杂。
Camera2缩放
看完Camera2的录像是不是觉得头痛,只可惜Camera2缩放也是让人头痛,下面看代码:
1 | kotlin复制代码override fun zoom( |
真不知道谁设计的这功能,缩放居然是通过修改Rect实现的,这里需要我们计算缩放的Rect,好在我用GPT帮我写的。
这里我通过持有mRecordRequestBuilder,并修改了参数,再次对mPreviewSession发起请求,算是有用了。
Camera2停止录像
Camera2停止录像和拍照的差不多,只不过拍照后需要重新预览,这里不做处理,在使用的地方调用吧。
1 | kotlin复制代码override fun stopRecord(activity: ComponentActivity, callback: Consumer<String>) { |
Camera2释放资源
记得把mMediaRecorder和持有的mRecordRequestBuilder释放了。
1 | kotlin复制代码override fun release() { |
完整代码
使用CameraX API
引引入CameraX库
这里和拍照类似,暂且列一下吧,这用的version catalog管理依赖,实际都差不多:
1 | toml复制代码# cameraX |
在dependence里面添加上依赖,就能开始写代码了。
1 | kotlin复制代码dependencies { |
CameraX预览
需要注意下,录像和前面不一样,CameraX提供了录像功能,只要使用videoCapture便可以,下面是代码:
1 | kotlin复制代码/** |
需要注意的是videoCapture别和ImageCapture一样用Builder创建,会提示报错,如果强行使用的话会很卡很卡!按网上说的,这里会把预览和录像的surface叠加,导致卡顿,虽然很多博客都是通过Builder创建的,实际上用法是错的。。。
我们需要通过recorder去创建videoCapture,来实现录像功能。
CameraX录像
CameraX录像稍微复杂一些,网上大部分博文都是通过videoCapture去startRecord,其实不对,正确用法应该如下:
1 | kotlin复制代码/** |
这里有个坑,因为我们需要在stopRecord中拿到回调的视频路径,但是这个视频完成录制是异步的,只能在startRecord里面的代码监听,所以我这加了个mRecordEndCallback来传递结果,具体代码要结合后面stopRecord来看。
CameraX缩放
CameraX的缩放需要通过mCamera的cameraControl去设置:
1 | kotlin复制代码override fun zoom( |
注意下,这个mCamera是在bindToLifecycle时的返回值,我们在拍照的时候并未用到:
1 | kotlin复制代码try { |
CameraX停止录像
CameraX停止录像只需要通过mRecording执行stop就行,只是stop是异步的,所以这里需要先保存下callback,再stop:
1 | kotlin复制代码override fun stopRecord(activity: ComponentActivity, callback: Consumer<String>) { |
最后在startRecord代码中的回调中执行,通过“?.”操作符就能在录像完成时触发回调了:
1 | kotlin复制代码when(recordEvent) { |
CameraX释放资源
这里多了一个mCamera需要释放,内部设置的回调mRecordEndCallback也清除下:
1 | kotlin复制代码/** |
完整代码
使用Demo
使用的demo就是上面gif显示的内容,代码如下:
小结
这篇文章把Android相机中Camera1、Camera2、CameraX三个API的录像功能实践了下,涉及不深,主要就是使用,记录学习的过程吧。
本文转载自: 掘金