Android 音视频开发【视频篇】【二】视频采集 | 8月更文挑战
这是我参与8月更文挑战的第2天,活动详情查看:8月更文挑战
上一章介绍了
RGB
、YUV
格式,本章将介绍如何采集YUV
格式的数据
一、视频相关概念
1.1 分辨率
用以横向
和纵向
的像素点个数来衡量一副图像的大小,比如1080*960
,其中1080
表示横向
的像素点个数为1080
个,960
表示纵向
的像素点个数为960
个
1.2 帧率
1s
内有多少个副图像,比如30fps
,表示1s
内有30
副图像,即30
帧,而60fps
,表示1s
内有60
帧,更高的还有90fps
、120fps
,帧率越高,相对来说,画面就会更流畅
根据人眼的视觉暂留特性,24fps
时,人眼就会认为是流畅
的,而更高的帧率,会给人一种身临其境的感觉
当然,也不是帧率越高越好,需要考虑显示器的刷新率,帧率过高可能反而浪费不必要的资源
1.3 码率/比特率
码率,也即比特率,单位是bps
,表示视频的比特数,计算公式是:
$$ 码率 = 文件大小(kb) / 时长(s) $$
二、视频采集
采集视频,在手机中可以通过摄像头进行采集,那么,在Android
也提供了很多对摄像头进行操作的类
-
Camera
操作相对简单,提供对摄像头的很多的操作方法,比如打开、关闭摄像头、变焦、拍照等,不过谷歌已经不建议使用,而是建议使用
CameraX
,或者是Camera2
-
Camera2
Camera2
相对于Camera
增加了很多特性,比如对Api
的架构进行了很大的优化,比Camera
更先进,还可以获取每帧的信息,等等,还有很多特性,不过,Camera2
相对于Camera
,使用上变得更加复杂 -
CameraX
JetPack
家族的一员,CameraX
是在Camea2
之后新出的一个相机框架,可能是谷歌觉得虽然Camera2
功能强大,但是使用起来确实麻烦了很多,所有为了在保证功能的前提的下,操作变得更简洁,所有推出了CameraX
虽然谷歌建议我们使用CameraX
或者是Camera2
,但是视频系列的相关文章,还是以Camera
,不为别的,就是为了简单,哈哈
2.1 Camera
关于相机的使用,可以放入子线程去使用
首先,第一步,当然是打开相机
打开相机
java
camera = Camera.open(id);
打开相机,使用的是Camera
类的静态方法open()
,该方法需要传入一个int
类型的id
,有两个值可选
-
Camera.CameraInfo.CAMERA_FACING_BACK
后摄
-
Camera.CameraInfo.CAMERA_FACING_FRONT
前摄
在获取camera
实例后,此时摄像头并没有正在的打开
接下来需要对camera
做一些参数配置,比如预览尺寸
获取相机参数
java
Camera.Parameters parameters = camera.getParameters();
对camera
进行参数设置,先得调用该方法,然后下面就会对parameters
进行一些参数设置
获取全部预览尺寸
java
List<Camera.Size> previewSizeList = parameters.getSupportedPreviewSizes();
通过调用parameters.getSupportedPreviewSizes()
获取当前相机所支持的预览尺寸,因为我们传入相机的预览尺寸必须得是相机所支持的
一般来说,我们通常希望相机的尺寸能够和屏幕一样大,或者是和屏幕同等比例,这样,显示出来的效果也就比较好,所有下面通过一个算法,找到最佳的尺寸
java
/**
* 寻找最合适的尺寸
*/
public static Camera.Size findTheBestSize(List<Camera.Size> sizeList, int screenW, int screenH) {
if (sizeList == null || sizeList.isEmpty()) {
throw new IllegalArgumentException();
}
Camera.Size bestSize = sizeList.get(0);
for (Camera.Size size : sizeList) {
int width = size.height;
int height = size.width;
float ratioW = (float) width / screenW;
float ratioH = (float) height / screenH;
if (ratioW == ratioH) {
bestSize = size;
break;
}
}
return bestSize;
}
一个参数,直接传入相机所支持的预览尺寸,后面两个参数是屏幕的尺寸
该算法的目的就是遍历每一个预览尺寸,但预览尺寸的比例和屏幕的尺寸比例一致时,就可以返回,而如果当遍历完,还是没找到合适的尺寸,就返回第一个相机所支持的尺寸
通过此方法,就获取到了一个最佳的尺寸
设置预览尺寸
java
parameters.setPreviewSize(width, height);
本章的目的是采样视频数据,所以我们得设置相机预览数据的回调格式
设置预览格式
java
parameters.setPreviewFormat(ImageFormat.NV21);
相机设置参数
java
camera.setParameters(parameters);
在对parameters
做了一系列的设置后,我们就可以将其设置给camera
设置旋转角度
java
camera.setDisplayOrientation(CameraUtils.getDisplayOrientation(activity, id));
设置旋转角度,使用的是CameraUtils.getDisplayOrientation(activity, id)
方法
java
/**
* 获取摄像头旋转角度
*/
public static int getDisplayOrientation(Activity activity, int facing) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(facing, info);
int rotation = activity.getWindowManager().getDefaultDisplay()
.getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
return result;
}
设置数据缓存和回调
之前设置了预览数据的回调格式为NV21
,它是一种YUV420
的格式
camera
提供了addCallbackBuffer
和setPreviewCallbackWithBuffer
的方法,用于接收预览数据和设置回调,它能够减少对象的创建
java
camera.addCallbackBuffer(nv21Data);
camera.setPreviewCallbackWithBuffer(this);
设置回调后,我们需要重写onPreviewFrame
方法
java
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
Log.d(TAG, "onPreviewFrame: " + nv21Data.length);
camera.addCallbackBuffer(nv21Data);
if (fos == null) {
return;
}
try {
fos.write(nv21Data);
} catch (IOException e) {
e.printStackTrace();
}
}
在该方法中,每次回调预览数据,都会先调用addCallbackBuffer
方法,该方法会复用对应的对象
接着后面将数据写入到文件中
设置SurfaceTexture,开启预览
当然,上面还没结束,还需要设置SurfaceTexture
才能正常开启预览,也就才能正常获取预览回调数据
java
try {
camera.setPreviewTexture(new SurfaceTexture(0));
} catch (IOException e) {
e.printStackTrace();
}
camera.startPreview();
这里设置的是一个new
出来的surfaceTexture
,产生的效果是无预览采样视频
你也可以使用SurfaceView
或者TextureView
显示预览画面
停止预览,释放资源
当我们停止录制时,需要停止预览并释放资源
java
private void release() {
if (camera != null) {
camera.stopPreview();
camera.release();
camera = null;
}
if (fos != null) {
try {
fos.close();
fos = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
三、GitHub
- 丰田加速在中国推进氢能应用,氢燃料电池车 MIRAI 应用场景扩大
- Mirai 恶意软件变体 MooBot 瞄准 D-Link 设备
- 使用 GO-CQHttp或mirai框架 搭建QQ的机器人
- Android 音视频开发【视频篇】【二】视频采集 | 8月更文挑战
- Android 音视频开发【特效篇】【一】抖音传送带特效 | 8月更文挑战
- 如何打造一款高性能的全屏红包雨
- 新型Enemybot DDoS僵尸网络借用Mirai和Gafgyt攻击代码
- 恶意软件Mirai正积极利用Spring4Shell漏洞
- Linux 恶意软件呈上升趋势:XorDDoS、Mirai 和 Mozi 最流行
- Python mirai开发QQ机器人起步教程(2021.9.9测试有效)
- 通报:Confluence远程代码执行漏洞(CVE-2021-26084)被黑产大规模利用
- 通报:腾讯主机安全捕获YAPI远程代码执行0day漏洞在野利用,该攻击正在扩散,可使用防火墙阻截
- Mirai_ptea Botnet利用KGUARD DVR未公开漏洞报告
- 研究人员通过 Mirai 恶意软件有效载荷确定了两个新的物联网漏洞
- 研究人员发现了另一种针对新物联网漏洞的Mirai变种
- 防御DDoS措施种类太多选晕了头?学会利用他人经验做对的选择
- 丰田将成汽车界诺基亚?
- HTB-靶机-Mirai
- 黑客通常可以分为以下8种类型
- 研究人员通过 Mirai 恶意软件 payload 确定了两个新的 IoT 漏洞