从帧缓冲区处理 YUV I420?

Posted

技术标签:

【中文标题】从帧缓冲区处理 YUV I420?【英文标题】:Processing YUV I420 from framebuffer? 【发布时间】:2021-11-29 18:11:32 【问题描述】:

我有一个名为 buf 的字节数组,其中包含从帧缓冲区获得的 YUV I420 格式的单个视频帧。对于每个视频帧,我还有以下信息:

Size (e.g. 320x180)
Stride Y (e.g. 384)
Stride U (e.g. 384)
Stride V (e.g. 384)
Plane offset Y (e.g. 0)
Plane offset U (e.g. 69120)
Plane offset V (e.g. 69312)

在一个文件中连接多个视频帧,并将其与大小信息一起传递给 VLC 或 FFmpeg 中的原始视频解码器只会产生乱码,所以我认为 buf 中的字节应该使用上述信息重新排序以产生可播放输出,但我对使用视频完全陌生,所以这可能是错误的。

大小、步幅和偏移量信息应该以何种顺序与buf 中的字节组合以生成可以在视频播放器中原始播放的字节流?

示例:

https://transfer.sh/E8LNy5/69518644-example-01.yuv

【问题讨论】:

您可以发布/上传一些示例数据(完整框架)吗?平面偏移看起来不对。 U和V之间只有192,这是不对的。步幅略大于宽度对我来说意味着有填充。 @ChristophRackwitz 当然,我已经添加了一个完整框架的链接。我必须承认在十六进制编辑器中查看它看起来很奇怪,但这是我从帧缓冲区中得到的? imgur.com/a/x4Ik5Gq 这是被解释为一个灰度流的 yuv 文件,384 宽。 352 像素宽的可用数据。数据看起来很奇怪。我还不能推测。 U 和 V 似乎也是交错的。如果是 U 然后是 V,我们会看到堆叠的两张平面图片,而不是并排的图片。如果它们是按样本交错的,我们会看到一张带有一些纹理的图片。一种奇怪的格式。数据从何而来? this 建议这种格式可以称为M420。无论如何,奇怪,但可解码。 imgur.com/a/x4Ik5Gq 上的第二张图片看起来是否合理,颜色是否正确?可用数据确实是 320x180 【参考方案1】:

数据的布局看起来很奇怪,但使用给定的偏移量和步幅,这可以解码为 YUV。

首先有 384 * 180 字节的亮度。

以下是色度线,每条线长 192 字节...但 U 和 V 线轮流使用!这是由奇怪的偏移量造成的。 U 偏移量正好指向亮度后。 V 偏移量进一步增加了 192 个字节......读取将跨越 384 个字节。

这是提取这些平面并将它们组装为 I420 的代码,用于使用 cvtColor 进行解码:

#!/usr/bin/env python3

import numpy as np
import cv2 as cv

def extract(data, offset, stride, width, height):
    data = data[offset:] # skip to...
    data = data[:height * stride] # get `height` lines
    data.shape = (height, stride)
    return data[:, :width] # drop overscan/padding

width, height = 320, 180

Yoffset = 0
Uoffset = 69120 # 384*180
Voffset = 69312 # 384*180 + 192

Ystride = 384
Ustride = 384
Vstride = 384

data = np.fromfile("69518644-example-01.yuv", dtype=np.uint8)

Y = extract(data, Yoffset, Ystride, width, height)
U = extract(data, Uoffset, Ustride, width // 2, height // 2)
V = extract(data, Voffset, Vstride, width // 2, height // 2)

# construct I420: Y,U,V planes in order

i420 = np.concatenate([Y.flat, U.flat, V.flat])
i420.shape = (height * 3 // 2, width)

result = cv.cvtColor(i420, cv.COLOR_YUV2BGR_I420)

cv.namedWindow("result", cv.WINDOW_NORMAL)
cv.resizeWindow("result", width * 4, height * 4)
cv.imshow("result", result)
cv.waitKey()
cv.destroyAllWindows()

【讨论】:

以上是关于从帧缓冲区处理 YUV I420?的主要内容,如果未能解决你的问题,请参考以下文章

为啥从帧缓冲区创建的纹理没有正确映射

从帧缓冲区对象读取时,OpenGL 纹理坐标是相反的

WebGL - 当我从帧缓冲区读取像素数据时,图标失去透明度

尝试从帧缓冲区传递纹理会导致 L_INVALID_OPERATION:glDrawArrays:绘制的源纹理和目标纹理相同

帧缓冲子系统框架

如何保存帧缓冲区然后将其取回