从帧缓冲区处理 YUV I420?

Processing YUV I420 from framebuffer?

我有一个名为 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

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

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

以下是色度线,每条线长 192 字节...但是 U 线和 V 线轮流出现!这是由奇怪的偏移量造成的。 U offset 正好指向 luma 之后。 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()