Flutter2空安全迁移后YUV转RGB出现RangeError索引越界问题
问题原因分析
这是Flutter Camera插件处理YUV420图像的常见坑,报错提示的数值错配是因为Dart跨Isolate抛出异常时的信息显示异常,实际报错不是来自Y平面的数组访问,核心原因有3个:
- 索引计算逻辑没有考虑YUV平面的行步长:绝大多数安卓设备为了内存对齐,Camera输出的YUV420图像每个平面的
bytesPerRow(每行实际字节数)会大于图像的width,你之前用h * width + w计算Y分量索引的逻辑,仅在行步长等于宽度时有效,一旦行步长更大就会出现越界。 - 异常信息错位:你看到的报错里的
index 10实际是U/V平面的访问索引,U/V平面的总长度仅为Y平面的1/4左右,错误提示里的上限值345600是Y平面的长度,Dart跨Isolate传递异常时错误复用了之前的长度参数,才出现了看起来逻辑矛盾的提示。 - 跨Isolate传递CameraImage导致数据损坏:CameraImage是绑定原生内存的对象,直接通过Isolate端口传递时,字节数组的视图会被截断或偏移,导致实际可用长度远小于预期值。
修复方案
1. 修正索引计算逻辑,增加边界校验
调整Y分量索引的计算方式,同时访问每个平面前先校验索引合法性,避免越界:
static imageLib.Image convertYUV420ToImage(CameraImage cameraImage) { final int width = cameraImage.width; final int height = cameraImage.height; final yPlane = cameraImage.planes[0]; final uPlane = cameraImage.planes[1]; final vPlane = cameraImage.planes[2]; final int yRowStride = yPlane.bytesPerRow; final int uvRowStride = uPlane.bytesPerRow; final int? uvPixelStride = uPlane.bytesPerPixel; final image = imageLib.Image(width, height); // 外层循环遍历高度,内存访问更连续,性能更好 for (int h = 0; h < height; h++) { for (int w = 0; w < width; w++) { // 用Y平面行步长计算Y分量索引 final int yIndex = h * yRowStride + w; if (yIndex >= yPlane.bytes.length) continue; final y = yPlane.bytes[yIndex]; final int uvIndex = uvPixelStride! * (w ~/ 2) + uvRowStride * (h ~/ 2); if (uvIndex >= uPlane.bytes.length || uvIndex >= vPlane.bytes.length) continue; final u = uPlane.bytes[uvIndex]; final v = vPlane.bytes[uvIndex]; final int pixelIndex = h * width + w; if (pixelIndex >= image.data.length) continue; image.data[pixelIndex] = ImageUtils.yuv2rgb(y, u, v); } } return image; }
2. 修正Isolate传参逻辑
不要直接传递CameraImage对象,把需要的基础数据和字节数组打包成Map传递,避免原生对象序列化异常:
// 发送到Isolate前的打包逻辑 Map<String, dynamic> imageParams = { 'yBytes': cameraImage.planes[0].bytes, 'uBytes': cameraImage.planes[1].bytes, 'vBytes': cameraImage.planes[2].bytes, 'width': cameraImage.width, 'height': cameraImage.height, 'yRowStride': cameraImage.planes[0].bytesPerRow, 'uvRowStride': cameraImage.planes[1].bytesPerRow, 'uvPixelStride': cameraImage.planes[1].bytesPerPixel, };
Isolate内部直接使用打包的参数处理图像即可,不需要再依赖CameraImage对象。
内容的提问来源于stack exchange,提问作者bazinac
相关产品推荐
相关产品推荐

