Android 图像 U 和 V 缓冲区太大

Android Image U and V buffers too large

我正在使用 camera2 api 从 android 图像中读取 YUV 值。因此我有3架飞机。

for (int x = 0; x < imageSheaf[0].Width; x++)
{
    for (int y = 0; y < imageSheaf[0].Height; y++)
    {
        imageYuv[x, y] = new yuv();
    }
}


for (int j = 0; bufferY.HasRemaining; j++)
{
    for (int i = 0; i < rowStrideY/2; i += 2)
    {
        if (i > width / 2 - 1 || j > height / 2 - 1)
            Log.Info("Processing", "Out of Bounds");
        imageYuv[i, j].y = bufferY.Get();
        bufferY.Get();//skip a pixel due to 4:2:0 sub sampling
    }

    for (int i = 0; i < rowStrideY/2; i++)//skip a line due to 4:2:0 sub sampling
    {
        bufferY.Get();
        bufferY.Get();
    }

    if (!bufferY.HasRemaining)
        Log.Debug("Processing", "finished");
}

for (int j = 0; bufferU.HasRemaining; j++)
{
    for (int i = 0; i < rowStrideU; i++)
    {
        if (!bufferU.HasRemaining)
            Log.Debug("Processing", "finished");
        imageYuv[i, j].u = bufferU.Get();
    }

    if (!bufferU.HasRemaining)
        Log.Debug("Processing", "finished");
}

for (int j = 0; bufferV.HasRemaining; j++)
{
    for (int i = 0; i < rowStrideV; i++)
    {
        if (!bufferV.HasRemaining)
            Log.Debug("Processing", "finished");
        imageYuv[i, j].v = bufferV.Get();
    }

    if (!bufferV.HasRemaining)
        Log.Debug("Processing", "finished");
}

这是我用来从字节缓冲区获取 Y、U 和 V 值的代码。

ImageFormat 是 YUV_420_888,据我了解,4:2:0 子采样意味着对于每个 U 或 V 像素有 4 个 Y 像素。

我的问题是 U 和 V 平面的字节缓冲区大小大于它们应该导致数组越界异常的大小:

[Processing] RowstrideY = 720
[Processing] RowstrideU = 368
[Processing] RowstrideV = 368
[Processing] y.remaining = 345600, u.remaining = 88312, v.remaining = 88312

(图片尺寸为720x480)

YUV420 的 Y 每像素 8 位,U 和 V 每四像素组 8 位。因此在 720x480 下,您希望 U-V 平面为 360x240。

但是,实际硬件可能有额外的对齐或步幅限制。在这种情况下,硬件似乎要求步幅是 16 的倍数,所以它从 360 增加到 368。

你希望它变成 368*240=88320 的长度,但请记住,每行的最后八个字节只是填充。所以buffer实际上可以是(368*239)+360 = 88312字节,不遗漏任何数据。如果您收到数组边界异常,那是因为您试图从最后一行读取行尾填充字节,但这是不允许的。 API 仅保证您将能够读取数据。

这样做的动机是,如果最后一行的填充恰好跨越页面边界,系统将需要为每个缓冲区分配一个额外的不必要的页面。

您可以修改代码以从每一行复制数据字节,然后进行第二个循环,仅消耗行末尾的填充字节(如果有)。