使用Raylib无法正常显示解码后的视频帧,求解决思路
我正在探索libav和raylib,理解音视频工作原理的同时用raylib构建界面。实现了一个可解码音视频帧的结构体,视频帧会被转换为32bpp的RGBA格式,相关初始化代码如下:
if (av_image_alloc((uint8_t **)media->dst_frame->data, media->dst_frame->linesize, media->ctxs[0]->width, media->ctxs[0]->height, AV_PIX_FMT_RGBA, 1) < 0) { fprintf(stderr, "Failed to setup dest image\n"); return -1; } media->sws_ctx = sws_getContext( media->ctxs[0]->width, media->ctxs[0]->height, media->ctxs[0]->pix_fmt, media->ctxs[0]->width, media->ctxs[0]->height, AV_PIX_FMT_RGBA, SWS_BILINEAR, NULL, NULL, 0); // 解码函数中的代码: int ret = sws_scale(media->sws_ctx, media->frame->data, media->frame->linesize, 0, media->frame->height, media->dst_frame->data, media->dst_frame->linesize);
主文件中初始化raylib并完成纹理加载,尝试获取首帧作为视频预览:
Image previewImage = GenImageColor(videoArea.width, videoArea.height, BLACK); // 试图通过转换格式保证兼容性 ImageFormat(&previewImage, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8); Texture2D videoTexture = LoadTextureFromImage(previewImage); UnloadImage(previewImage);
if (!state->has_media) { DrawText("Drop a video file here!", videoArea.x + 10, videoArea.y + 10, 20, GRAY); } else { if (state->first_frame) { do { decode_packet(state->media); } while (!is_frame_video(state->media)); UpdateTexture(videoTexture, state->media->dst_frame->data[0]); state->first_frame = 0; } } DrawTexture(videoTexture, videoArea.x, videoArea.y, WHITE);
拖入MP4文件后显示效果异常,画面呈现错位乱码,疑似行对齐问题,求正确的解决方向。
核心问题是Libav输出帧的行字节数(linesize)与Raylib纹理要求的紧密排列行字节数不匹配:
Libav的行对齐机制
av_image_alloc默认启用对齐(最后一个参数为1),会将linesize设置为16字节的倍数,而非严格的宽度×4(RGBA每个像素4字节)。当视频宽度不是16的倍数时,linesize会比实际像素所需的字节数多,多余的字节是填充用的无效数据。Raylib的纹理数据要求
UpdateTexture期望传入的像素数据是紧密无填充的,每行字节数必须等于纹理宽度×4。如果直接传入带对齐填充的Libav帧数据,Raylib会把填充字节当成有效像素读取,导致画面错位、色彩混乱。
具体修复方案:
方案一:手动逐行复制,剔除对齐填充
提前分配一个与纹理尺寸匹配的紧密缓冲区,将Libav帧的有效像素数据逐行复制到缓冲区后再更新纹理:// 初始化阶段分配缓冲区(与纹理同尺寸) uint8_t* tight_pixels = malloc(videoTexture.width * videoTexture.height * 4); // 获取到视频帧后执行复制 for (int y = 0; y < media->dst_frame->height; y++) { memcpy( tight_pixels + y * videoTexture.width * 4, media->dst_frame->data[0] + y * media->dst_frame->linesize[0], videoTexture.width * 4 ); } // 用紧密缓冲区更新纹理 UpdateTexture(videoTexture, tight_pixels);方案二:让Libav生成无对齐的帧数据
修改av_image_alloc的最后一个参数为0,禁用自动对齐,这样linesize会严格等于宽度×4:if (av_image_alloc((uint8_t **)media->dst_frame->data, media->dst_frame->linesize, media->ctxs[0]->width, media->ctxs[0]->height, AV_PIX_FMT_RGBA, 0) < 0) { fprintf(stderr, "Failed to setup dest image\n"); return -1; }注意:部分硬件加速的sws_scale可能对非对齐数据兼容性较差,优先推荐方案一。
额外检查:纹理尺寸匹配
确保videoTexture的宽高与Libav解码出的视频帧宽高完全一致,不要直接使用videoArea的尺寸生成纹理(除非你需要做缩放适配)。尺寸不匹配同样会导致画面错乱。
内容的提问来源于stack exchange,提问作者gabriel_tiso




