Android摄像头通常会支持两种图像格式:NV21和YV12,可通过下面方式设置
val parameters = mCamera.parameters
parameters.setPreviewFormat(ImageFormat.NV21) // ImageFormat.YV12
mCamera.parameters = parameters
比较诡异的是,Android的H264硬编码器不支持NV21和YV12格式,神奇!
============================================================================
val parameters = mCamera.parameters
parameters.focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO
mCamera.parameters = parameters
==========================================================================
- 成员变量赋值
ySize = videoWidth * videoHeight; // Y分量大小
oneFrameSize = (ySize * 3) >> 1; // 一帧画面大小
uvSize = oneFrameSize - ySize; // UV分量大小
uvHalfSize = uvSize / 2; // UV分量的一半
uvBytes = new byte[uvSize]; // 用于存放UV分量数据
maxIndex = ySize + uvHalfSize; // 最大索引
- YV12转I420
private void YV12ToI420(byte[] yuvDatas) {
for (i = ySize; i < maxIndex; i++) {
temp = yuvDatas[i];
yuvDatas[i] = yuvDatas[i + uvHalfSize];
yuvDatas[i + uvHalfSize] = temp;
}
}
- I420转NV12
private void I420ToNV12(byte[] i420Bytes) {
index = 0;
for (i = ySize; i < maxIndex; i++) {
uvBytes[index] = i420Bytes[i];
uvBytes[index + 1] = i420Bytes[i + uvHalfSize];
index += 2; // 上面已经连续存储了两个位置了,所以这里要跳2
}
System.arraycopy(uvBytes, 0, i420Bytes, ySize, uvSize);
}
- NV21转NV12
private void NV21ToNV12(byte[] yuvDatas) {
for (index = ySize; index < oneFrameSize; index += 2) {
temp = yuvDatas[index];
yuvDatas[index] = yuvDatas[index + 1];
yuvDatas[index + 1] = temp;
}
}
============================================================================
当把SurfaceView写在布局中时,第一次运行时,先动态权限申请权限,申请到权限之后再显示摄像头,这时会发现显示不了,从打印Log可以知道surfaceCreated函数并没有执行,原因时Activity显示时,我们还没有Camera权限,此时surfaceCreated已经执行,而因为没有权限,我们的代码还没执行到给SurfaceHolder添加回调的地方,所以就看不到surfaceCreated执行。解决方案就是SurfaceView不要写死在布局中,而是在用到的时候直接new出来,然后add到布局中,这样就没问题了。
=================================================================================
我在项目中增加了一个关闭摄像头的按钮,点击后关闭摄像头。因为SurfaceView是new出来动态添加到主界面上的,则在关闭摄像头后应该把SurfaceView移除,但是在移除SurfaceView的过程中(container.removeView(surfaceView))会触发surfaceDestroyed函数,而我在这个函数中又调用了关闭摄像头函数,这样会导致两次调用关闭摄像头的函数,在第一次调用关闭摄像头函数时,代码还没执行完removeView的操作,surfaceDestroyed就被执行了,此时就会执行第二次的关闭摄像头函数调用,第二次就能成功把removeView操作完成,然后函数回到第一次调用的地方时就会报错,因为View已经被第二次移除掉了。解决方案:加入一个变量,确保release函数在执行完一次时才会再执行第二次,如下:
private fun releaseCamera() {
if (isReleasing) return
isReleasing = true
binding.root.findViewWithTag(TAG)?.let { binding.root.removeView(it) }
camera?.apply {
setPreviewCallback(null)
stopPreview()
release()
camera = null
}
isReleasing = false
}
最后
**一个零基础的新人,我认为坚持是最最重要的。**我的很多朋友都找我来学习过,我也很用心的教他们,可是不到一个月就坚持不下来了。我认为他们坚持不下来有两点主要原因:
他们打算入行不是因为兴趣,而是因为所谓的IT行业工资高,或者说完全对未来没有任何规划。
刚开始学的时候确实很枯燥,这确实对你是个考验,所以说坚持下来也很不容易,但是如果你有兴趣就不会认为这是累,不会认为这很枯燥,总之还是贵在坚持。
技术提升遇到瓶颈了?缺高级Android进阶视频学习提升自己吗?还有大量大厂面试题为你面试做准备!
提升自己去挑战一下BAT面试难关吧
对于很多Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。整理的这些知识图谱希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
不论遇到什么困难,都不应该成为我们放弃的理由!
如果有什么疑问的可以直接私我,我尽自己最大力量帮助你!
最后祝各位新人都能坚持下来,学有所成。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!