NV12转RGB24格式转换异常:边框畸变与颜色错位问题求助
问题排查:FFmpeg GPU解码后NV12转BGR24 Bitmap的畸变与颜色问题
问题现象
- 使用FFmpeg的
sw_scale实现NV12转BGR24:颜色显示正常,但左侧边框出现畸变 - 使用PixelFormatConverter的自定义NV12转BGR24逻辑:不仅左侧边框畸变,还出现颜色错位
关键排查点
1. Bitmap行对齐校验
BMP格式要求每行像素数据的字节数必须是4的整数倍。你用av_image_get_buffer_size(AV_PIX_FMT_BGR24, ..., 32)获取的缓冲区大小已经包含了对齐处理,但要确保转换函数(不管是sw_scale还是自定义逻辑)使用的目标行步长是对齐后的值,而非直接用frame->width * 3:
- 用
sw_scale时,要把dstStride设为av_image_get_buffer_size计算出的每行字节数,而非硬编码width*3 - 自定义转换函数中,不能直接按
width*3逐行写入,要使用对齐后的行宽作为步长,否则会因数据偏移导致左侧边框畸变
2. NV12数据的行步长(linesize)问题
FFmpeg GPU解码输出的AVFrame中,Y平面(data[0])和UV平面(data[1])的linesize不一定等于图像宽度(可能因硬件对齐要求大于宽度)。如果转换时直接用frame->width作为行宽,而非frame->linesize[0]和frame->linesize[1],会读取到错误的UV数据,引发颜色错位或边框异常。
3. Bitmap的存储方向
BMP的像素数据是从下到上存储的,而AVFrame的像素是从上到下排列。如果转换后未反转图像行顺序,可能导致显示异常(虽当前问题未提,但需确认是否在显示环节处理了此问题)。
4. 自定义转换的UV采样逻辑
NV12是YUV420格式,每个UV像素对应4个Y像素。自定义转换时需检查:
- UV平面的行步长是否使用
frame->linesize[1]而非frame->width - UV采样的坐标映射是否正确(比如行偏移、列偏移的计算是否符合420采样规则),错误的采样会直接导致颜色错位
转换后生成Bitmap的代码
// frame 是FFmpeg GPU解码得到的NV12格式AVFrame int image_size = av_image_get_buffer_size(AV_PIX_FMT_BGR24, frame->width, frame->height, 32); size_t sizeof_file_header = sizeof(BITMAPFILEHEADER); size_t sizeof_info_header = sizeof(BITMAPINFOHEADER); BITMAPFILEHEADER bmpheader = {0}; bmpheader.bfType = 0x4d42; bmpheader.bfReserved1 = 0; bmpheader.bfReserved2 = 0; bmpheader.bfOffBits = sizeof_file_header + sizeof_info_header; bmpheader.bfSize = bmpheader.bfOffBits + image_size; BITMAPINFOHEADER bmpinfo = {0}; bmpinfo.biSize = sizeof_info_header; bmpinfo.biWidth = frame->width; bmpinfo.biHeight = frame->height; bmpinfo.biPlanes = 1; bmpinfo.biBitCount = 24; bmpinfo.biCompression = BI_RGB; bmpinfo.biSizeImage = 0; bmpinfo.biXPelsPerMeter = 0; bmpinfo.biYPelsPerMeter = 0; bmpinfo.biClrUsed = 0; bmpinfo.biClrImportant = 0; uint8_t* buffer = new uint8_t[bmpheader.bfSize]; memcpy(buffer, &bmpheader, sizeof_file_header); memcpy(buffer + sizeof_file_header, &bmpinfo, sizeof_info_header); // 调用转换函数(sw_scale或自定义NV12_TO_BGR24) NV12_TO_BGR24(frame->data[0], (buffer + bmpheader.bfOffBits), frame->width, frame->height);
内容的提问来源于stack exchange,提问作者Kostas Kourkounis
相关产品推荐
相关产品推荐

