求助:将原始视频转I420供VPX编码器及libyuv转换UYVY异常
嘿,我之前也踩过类似的坑!你现在遇到的这个情况,大概率是内存步长(stride)不匹配或者转换时的尺寸/参数设置错误导致的。Decklink采集的视频帧通常会做字节对齐(比如按16字节对齐),实际stride会比理论计算的(宽度×像素字节数)要大,如果忽略这一点,转换时就只会读取到左上角的有效区域。
常见问题排查与修复步骤
1. 先拿到Decklink帧的真实Stride
Decklink捕获的UYVY格式帧,每个像素占2字节,但实际一行数据的总字节数(也就是stride)可不是简单的width * 2——采集卡会做字节对齐,比如宽度不是16倍数的话,会自动补全到最近的16倍数。你得从Decklink的回调里拿真实的stride值,比如调用IDeckLinkVideoInputFrame::GetBytesPerRow()方法,这个返回值才是UYVY源的正确stride。
2. 正确调用libyuv的转换函数
别随便给转换函数传理论值,一定要把真实的源stride和目标各平面的stride传对。给你一个修正后的代码示例:
#include "libyuv/convert.h" // 假设已经从Decklink拿到了这些数据 uint8_t* uyvy_raw_data = ...; // 采集到的原始UYVY帧数据指针 int frame_width = 1920; int frame_height = 1080; int uyvy_row_stride = ...; // 从GetBytesPerRow()拿到的真实行字节数 // 初始化vpx的I420图像结构 vpx_image_t vpx_img; // 让vpx自动分配内存并计算合适的stride(推荐用这个方式) vpx_img_alloc(&vpx_img, VPX_IMG_FMT_I420, frame_width, frame_height, 1); // 调用libyuv的UYVY转I420函数 libyuv::UYVYToI420( uyvy_raw_data, uyvy_row_stride, vpx_img.planes[0], vpx_img.stride[0], vpx_img.planes[1], vpx_img.stride[1], vpx_img.planes[2], vpx_img.stride[2], frame_width, frame_height );
3. 别手动硬编码vpx_image的Stride
如果你是手动初始化vpx_image_t,千万别自己瞎写stride值!vpx的vpx_img_alloc函数会根据格式、宽高自动计算对齐后的stride(符合编码器要求的那种),硬编码很容易出问题。
比如I420格式的话,Y平面的stride是对齐后的宽度,U/V平面是对齐后宽度的一半,这些vpx都会帮你算好。
4. 检查原始帧数据是否完整
有时候也可能是你从Decklink拿到的原始数据不完整——比如只读取了部分缓冲区。你可以核对一下原始UYVY数据的总字节数是不是等于uyvy_row_stride * frame_height,如果字节数不够,转换出来自然只有部分区域有效。
额外小提示
- 要是你的VPX编码器对分辨率有对齐要求(比如VP9要求宽高是16的倍数),转完I420后可以用libyuv的
I420Scale函数做一下缩放调整,确保符合编码器的要求。 - 调试的时候可以把源stride、目标各平面的stride都打印出来,对比一下理论值和实际值,很快就能定位是不是stride的问题。
内容的提问来源于stack exchange,提问作者Matthew

