当前位置:网站首页>使用缓冲的方式采集视频
使用缓冲的方式采集视频
2022-07-28 19:04:00 【android_cai_niao】
摄像头权限申请什么的就不说了,直接上关键代码:
class MainActivity : AppCompatActivity() {
private val camera = Camera.open()
private val surfaceTexture = SurfaceTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
camera.setPreviewTexture(surfaceTexture)
camera.setPreviewCallback {
data, camera ->
println("摄像头正在采集图像")
}
camera.startPreview()
}
override fun onDestroy() {
super.onDestroy()
camera.setPreviewCallback(null)
camera.stopPreview()
camera.release()
}
}
这是一个无预览摄像头视频采集,只是一个非常简单的代码,camera.setPreviewCallback中的data即为图像数据,我们没有做保存处理,每次得到的data都会是一个新的数组对象,试验如下:
class MainActivity : AppCompatActivity() {
private val camera = Camera.open()
private val surfaceTexture = SurfaceTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES)
private var oldData = ByteArray(0)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
camera.setPreviewTexture(surfaceTexture)
camera.setPreviewCallback {
data, camera ->
if (oldData !== data) {
oldData = data
println("新data")
} else {
println("旧data")
}
}
camera.startPreview()
}
override fun onDestroy() {
super.onDestroy()
camera.setPreviewCallback(null)
camera.stopPreview()
camera.release()
}
}
运行代码,打印的都是"新data"。如果是25帧/秒,则每秒要创建25个新的Byte数组,不停地创建新的对象性能是比较低的,所以可以使用缓冲对象,每次都用同一个数组,代码如下:
camera.setPreviewCallbackWithBuffer {
data, camera ->
if (oldData !== data) {
oldData = data
println("新data")
} else {
println("旧data")
}
}
这里把setPreviewCallback函数替换成了setPreviewCallbackWithBuffer,运行代码,会发现没有任何输出,这是因为还需要我们提供一个缓冲数组,如下:
class MainActivity : AppCompatActivity() {
private val camera = Camera.open()
private val surfaceTexture = SurfaceTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES)
private val previewSize = camera.parameters.previewSize
private val width = previewSize.width
private val height = previewSize.height
private val callbackBuffer = ByteArray((width * height * 3) shr 1)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
camera.setPreviewTexture(surfaceTexture)
camera.addCallbackBuffer(callbackBuffer)
camera.setPreviewCallbackWithBuffer {
data, camera ->
if (callbackBuffer !== data) {
println("新data")
} else {
println("旧data")
}
}
camera.startPreview()
}
override fun onDestroy() {
super.onDestroy()
camera.setPreviewCallback(null)
camera.stopPreview()
camera.release()
}
}
运行代码,会发现只打印了一次"旧data",只是因为系统只会在我们调用了camera.addCallbackBuffer(callbackBuffer)之后,才使用我们给的buffer对象装一帧的图像给我们,所以,在我们需要数据的时候就需要调用camera.addCallbackBuffer(callbackBuffer),如果一直需要,就需要一直调用camera.addCallbackBuffer(callbackBuffer),示例如下:
class MainActivity : AppCompatActivity() {
private val camera = Camera.open()
private val surfaceTexture = SurfaceTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES)
private val previewSize = camera.parameters.previewSize
private val width = previewSize.width
private val height = previewSize.height
private val callbackBuffer = ByteArray((width * height * 3) shr 1)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
camera.setPreviewTexture(surfaceTexture)
camera.addCallbackBuffer(callbackBuffer)
camera.setPreviewCallbackWithBuffer {
data, camera ->
if (callbackBuffer !== data) {
println("新data")
} else {
println("旧data")
}
// TODO 处理data
// data处理完了,再把缓冲对象给到框架,让其再填充图像数据在里面
//camera.addCallbackBuffer(data) // 这个方式也可以,反正都是同一个对象
camera.addCallbackBuffer(callbackBuffer)
}
camera.startPreview()
}
override fun onDestroy() {
super.onDestroy()
camera.setPreviewCallback(null)
camera.stopPreview()
camera.release()
}
}
再次运行代码,会看到一直在输出"旧data",这样就避免了每一帧都创建新的data对象,但是需要注意,我们在处理data数据的时候一定要快,假设需要25帧/秒,则每一帧的处理时间为40毫秒,我们必须在40毫秒内处理完data,然后再把data设置到addCallbackBuffer中,如果处理的时间慢,就会导致丢帧。比如,我们加入帧率的统计代码,如下:
class MainActivity : AppCompatActivity() {
private val camera = Camera.open()
private val surfaceTexture = SurfaceTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES)
private val previewSize = camera.parameters.previewSize
private val width = previewSize.width
private val height = previewSize.height
private val callbackBuffer = ByteArray((width * height * 3) shr 1)
private var fps = 0
private var start = 0L
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
println("width = $width height = $height")
camera.setPreviewTexture(surfaceTexture)
camera.addCallbackBuffer(callbackBuffer)
camera.setPreviewCallbackWithBuffer {
data, camera ->
fps++
val current = System.currentTimeMillis()
if (start == 0L) {
start = current
}
if (current - start >= 1000) {
println("帧速:${
fps}帧/秒")
fps = 0
start = current
}
camera.addCallbackBuffer(data)
}
camera.startPreview()
}
override fun onDestroy() {
super.onDestroy()
camera.setPreviewCallback(null)
camera.stopPreview()
camera.release()
}
}
在小米11 pro运行结果如下:
width = 1920 height = 1080
帧速:28帧/秒
帧速:24帧/秒
帧速:30帧/秒
帧速:30帧/秒
帧速:31帧/秒
帧速:30帧/秒
帧速:31帧/秒
帧速:30帧/秒
帧速:31帧/秒
帧速:30帧/秒
可以看到,帧速并不是稳定输出的,也会有偏差。
接下来,模拟一下对data数据的处理,假设这个处里需要80毫秒:
class MainActivity : AppCompatActivity() {
private val camera = Camera.open()
private val surfaceTexture = SurfaceTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES)
private val previewSize = camera.parameters.previewSize
private val width = previewSize.width
private val height = previewSize.height
private val callbackBuffer = ByteArray((width * height * 3) shr 1)
private var fps = 0
private var start = 0L
private val handler = object: Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
camera.addCallbackBuffer(callbackBuffer)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
println("width = $width height = $height")
camera.setPreviewTexture(surfaceTexture)
camera.addCallbackBuffer(callbackBuffer)
camera.setPreviewCallbackWithBuffer {
data, camera ->
fps++
val current = System.currentTimeMillis()
if (start == 0L) {
start = current
}
if (current - start >= 1000) {
println("帧速:${
fps}帧/秒")
fps = 0
start = current
}
// 模拟一个80毫秒的耗时操作
handler.sendEmptyMessageDelayed(0, 80L)
}
camera.startPreview()
}
override fun onDestroy() {
super.onDestroy()
camera.setPreviewCallback(null)
camera.stopPreview()
camera.release()
}
}
运行效果如下:
width = 1920 height = 1080
帧速:7帧/秒
帧速:8帧/秒
帧速:6帧/秒
帧速:6帧/秒
帧速:8帧/秒
帧速:7帧/秒
帧速:8帧/秒
帧速:8帧/秒
帧速:9帧/秒
帧速:8帧/秒
可以看到,由于处理数据花费的时间太多,帧速极速下降,所以看到的视频就会很卡,不是卡住不动,而是视频不流畅,画面不连贯。
接下来,我们把耗时时间改成30毫秒,再次运行,结果如下:
width = 1920 height = 1080
帧速:13帧/秒
帧速:13帧/秒
帧速:14帧/秒
帧速:14帧/秒
帧速:14帧/秒
帧速:14帧/秒
帧速:14帧/秒
帧速:13帧/秒
帧速:14帧/秒
理论上1帧需要30毫秒,1秒(1000毫秒)可以处理33帧啊,但是结果显示只有14帧左右,那我们把延时再改小为10毫秒,再次运行,结果如下:
width = 1920 height = 1080
帧速:20帧/秒
帧速:22帧/秒
帧速:31帧/秒
帧速:30帧/秒
帧速:31帧/秒
帧速:31帧/秒
帧速:30帧/秒
帧速:30帧/秒
帧速:30帧/秒
帧速:31帧/秒
帧速:29帧/秒
帧速:31帧/秒
帧速:30帧/秒
帧速:31帧/秒
可以看到,帧速恢复到了30帧/秒,这样视频就不会卡了,但是10毫秒的处理时间,在真实开发中根本就不够用,每一帧图像要经过旋转、加水印、H264编码、网络发送等一系列操作,10毫秒根本就完成不了,所以解决方案就是多线程处理,可以使用双缓冲机制,比如视频采集、格式转换、旋转、加水印、编码、发送,每两个相连的步骤之间都可以加入双缓冲机制,这样就可以实现每一个步骤都是并行运行的。
边栏推荐
- 远光软件获得阿里云产品生态集成认证,携手阿里云共建新合作
- C# 读取 CSV文件内数据,导入DataTable后显示
- Easynlp Chinese text and image generation model takes you to become an artist in seconds
- Thinking and summary of R & D Efficiency
- JS page black and white background switch JS special effect
- Space shooting Lesson 16: props (Part 2)
- Network layer performance test
- Explain the script data transmission and notification in unity
- Lvs+keepalived high availability deployment practical application
- PL515 SOT23-5 单/双口 USB 充电协议端口控制器 百盛电子代理商
猜你喜欢

Cause analysis of restart of EMC cx4-120 SPB controller

Explain the mobile control implementation of unity in detail

"When you are no longer a programmer, many things will get out of control" -- talk to SUSE CTO, the world's largest independent open source company

Subcontracting loading of wechat applet

Pl515 SOT23-5 single / Dual Port USB charging protocol port controller Parkson electronic agent

EfficientFormer:轻量化ViT Backbone

The 678th operation

Seventeen year operation and maintenance veterans, ten thousand words long, speak through the code of excellent maintenance and low cost~

Introduction to redis I: redis practical reading notes

Meaning of disk status of EMC DataDomain
随机推荐
“当你不再是程序员,很多事会脱离掌控”—— 对话全球最大独立开源公司SUSE CTO...
网络各层性能测试
GIS数据漫谈(六)— 投影坐标系统
Ask if you don't understand, and quickly become an advanced player of container service!
Lvs+keepalived high availability deployment practical application
Explain the imported 3D model in unity
LVS+KeepAlived高可用部署实战应用
Alibaba cloud MSE supports go language traffic protection
Prometheus complete process of configuring alertmanager
Redis的三种删除策略以及逐出算法
十七年运维老兵万字长文讲透优维低代码~
[complete collection of common ADB commands and their usage (from a comprehensive summary of [wake up on Sunday)]
[工具类] Map的util包, 常用 实体类转化为map等操作
Oracle库访问很慢,是什么原因?
What is "security"? Volvo tells you with its unique understanding and action
Observer mode, object pool
Unity foundation 6-rotation
查询oracle视图创建语句及如何向视图中插入数据[通俗易懂]
Explain the camera in unity and its application
Cartoon JS shooting game source code