bounty将在2天后过期。回答此问题可获得+500的声誉奖励。Filip Luchianenco正在寻找来自声誉良好来源的答案:我们的目标是了解错误的原因以及什么是好的前进方向。
我正在尝试使用FileOutputStream将Android相机流帧写入UVC缓冲区。UVC驱动程序在设备上工作,并且它具有定制的内核。
使用imageAnalyzer时,我得到每秒24帧:
imageAnalyzer = ImageAnalysis.Builder()
.setTargetAspectRatio(screenAspectRatio)
.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_YUV_420_888)
...
imageAnalysis.setAnalyzer(cameraExecutor) { image ->
val buffer = image.planes[0].buffer
val data = buffer.toByteArray()
...
}
然后基于UVC规范,我构建帧的报头:
val header = ByteBuffer.allocate(26)
val frameSize = image.width * image.height * ImageFormat.getBitsPerPixel(image.format) / 8
val EOH = 0x01
val ERR = 0x00
val STI = 0x01
val REST = 0x00
val SRC = 0x00
val PTS = (System.currentTimeMillis() - referenceTime) * 10000
val endOfFrame = 0x01
val FID = (frameId).toByte()
将以上所有内容添加到标题中
header.putInt(frameSize)
header.putShort(image.width.toShort())
header.putShort(image.height.toShort())
header.put(image.format.toByte())
header.put(((EOH shl 7) or (ERR shl 6) or (STI shl 5) or (REST shl 4) or SRC).toByte())
header.putLong(PTS)
header.put(endOfFrame.toByte())
header.put(FID)
打开FileOutputStream并尝试写入头和图像:
val uvcFileOutputStream = FileOutputStream("/dev/video3", true)
uvcFileOutputStream.write(header.toByteArray() + data)
uvcFileOutputStream.close()
尝试调整头/有效负载,但我仍然得到相同的错误:
java.io.IOException: write failed: EINVAL (Invalid argument)
at libcore.io.IoBridge.write(IoBridge.java:654)
at java.io.FileOutputStream.write(FileOutputStream.java:401)
at java.io.FileOutputStream.write(FileOutputStream.java:379)
我可能做错了什么?是标头格式错误吗?
1条答案
按热度按时间w41d8nur1#
我不知道直接的答案,但我很好奇地想看看并有一些发现。我集中在Kotlin部分,因为我不知道UVC,因为我怀疑问题就在那里。
∮巨大的假设∮
由于没有规范的链接,我只是找到了这个源代码:ZIP中的https://www.usb.org/document-library/video-class-v15-document-set我查看了
USB_Video_Payload_Frame_Based_1.5.pdf
第9页,第2.1节有效负载标题我的所有发现都基于此,因此如果我弄错了,其他一切都是错误的。但如果您验证了相同的内容,它仍然可以导致解决方案。发现1:HLE是错误的
HLE是头文件的长度,而不是图像数据的长度,你把整个图像的大小放在这里(所有RGB字节数据)。表2-1描述了控制PTS和SCR是否存在的PTS和SCR位。这意味着如果
BFH
中的PTS和SCR为0,则报头较短。这就是HLE
为2、6、12.确认源+字段为1字节长的事实(表2-1的每行为1字节/8比特),这意味着报头最多只能为255字节长。发现2:所有标题都未对齐
因为你把HLE和
putInt
放在一起,你写了4个字节,从这一点开始,头中的一切都混乱了,标志取决于图像大小,等等。发现3:SCR和PTS标志不一致
假设我对1和2的判断是错误的,您仍然将SRC和PTS位设置为0,但推送了一个long(8字节)。
发现4:错误来源
实际上,在这一点上有些东西真的不对劲,所以我查看了你引用的GitHub ticket,找到了一个更好的代码表示的例子:
遗憾的是,我无法匹配您的标题结构,所以我将假设您正在实现的东西非常类似于我正在寻找的,因为所有的PDF都有几乎相同的标题表。
发现5:HLE错误或缺失
假设您需要从图像大小开始,HLE仍然是错误的,因为它是图像格式的类型,与SCR和PTS标志无关。
发现6:BFH缺少字段
如果您遵循这些规范之一,BFH始终是一个字节,8位。这一点可以通过
shl
如何在代码中将其组合在一起以及每个标志(flag = true)的描述来确认|错误/ 1/0)。发现7:PTS错误
将毫秒级的精度乘以10000看起来很奇怪。文档中说“最多450微秒”,如果你试图在
ms
和us
之间转换,我认为乘数只有1000。无论哪种方式,它都只是一个int(4字节)大,肯定不是long
。发现8:编码助理?
我有一种感觉后,阅读这一切,Copilot,ChatGPT或其他生成器写你的原始代码。这个声音证实了你寻找一个有信誉的来源。
发现9:可靠来源示例
如果我是你,我会尝试在GitHub中找到一个这样的工作示例,使用关键字搜索如下:https://github.com/search?q=hle+pts+sti+eoh+fid+scr+bfh&type=code并不重要,因为这些都是二进制文件/流格式,所以无论使用哪种语言,它们都应该以相同的方式生成和读取。
发现10:位元顺序
看看big endian / little endian字节顺序。如果你看一下我链接的PDF中的表2-1,你可以看到哪个位应该Map到哪个字节。你可以在写入缓冲区之前轻松地指定你需要的顺序,从PDF的外观来看,它是
header.order(ByteOrder.LITTLE_ENDIAN)
。我认为传统上0是最低位,31是最高位。我不能引用这方面的源代码,我好像还记得uni,0位应该是2^0
分量(1),7位应该是2^7
(128),颠倒过来会使计算和理解变得更加困难,所以PTS [7:0]意味着byte是32位PTS数的最低8位。如果您链接到规范源代码,我可以修改我所写的内容,但很可能会发现非常相似的猜测。