android IO异常:写入失败:Kotlin中UVC文件输出流上的EINVAL(无效参数)

dxxyhpgq  于 2023-01-24  发布在  Android
关注(0)|答案(1)|浏览(192)

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)

我可能做错了什么?是标头格式错误吗?

w41d8nur

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微秒”,如果你试图在msus之间转换,我认为乘数只有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位。
如果您链接到规范源代码,我可以修改我所写的内容,但很可能会发现非常相似的猜测。

相关问题