如何通过FFmpeg C API获取displaymatrix旋转信息并修正视频?
FFmpeg处理竖屏视频旋转问题解决方案
1. 获取视频的displaymatrix旋转元数据
竖屏视频的旋转信息一般存在AVStream的侧数据(对应ffprobe检测到的displaymatrix)或元数据中,不是AVFrame的侧数据,之前遍历AVFrame找不到很正常。可以用两种方式获取:
方式一:读取AVStream的侧数据(推荐)
直接从视频流的侧数据中提取显示矩阵,再解析出旋转角度:
#include <libavutil/display.h> // 假设已打开输入文件,拿到AVFormatContext* fmt_ctx和目标视频流索引stream_idx AVStream* stream = fmt_ctx->streams[stream_idx]; int32_t* display_matrix = (int32_t*)av_stream_get_side_data(stream, AV_PKT_DATA_DISPLAYMATRIX, NULL); if (display_matrix) { int rotation = av_display_rotation_get(display_matrix); // 注意:av_display_rotation_get返回0/90/180/270,-90度会返回270 printf("视频旋转角度:%d度\n", rotation); }
方式二:读取AVStream的元数据字段
部分视频会把旋转信息存在元数据的rotate键里,也可以兜底检查:
AVDictionaryEntry* rotate_entry = av_dict_get(stream->metadata, "rotate", NULL, 0); if (rotate_entry) { int rotation = atoi(rotate_entry->value); printf("视频旋转角度:%d度\n", rotation); }
实际代码里可以把两种方式结合,确保覆盖不同编码的视频。
2. 修改视频缓冲区数据修正旋转并保持宽高比
如果必须修改AVFrame的像素数据来让显示正常,用FFmpeg的swscale模块就能搞定,步骤如下:
步骤1:根据旋转角度调整输出宽高
旋转90/270/-90度时,输出的宽高要和原始帧交换;180度则宽高不变。
步骤2:用swscale做旋转转换
代码示例:
#include <libswscale/swscale.h> // 假设原始帧是AVFrame* src_frame,已获取到rotation角度 int dst_width, dst_height; if (rotation == 90 || rotation == 270 || rotation == -90) { dst_width = src_frame->height; dst_height = src_frame->width; } else { dst_width = src_frame->width; dst_height = src_frame->height; } // 分配目标帧内存 AVFrame* dst_frame = av_frame_alloc(); dst_frame->format = src_frame->format; dst_frame->width = dst_width; dst_frame->height = dst_height; av_frame_get_buffer(dst_frame, 32); // 初始化swscale上下文,指定旋转参数 int sws_flags = SWS_BILINEAR; // 用双线性插值保证画质 if (rotation == 90) { sws_flags |= SWS_ROTATE_90; } else if (rotation == 180) { sws_flags |= SWS_ROTATE_180; } else if (rotation == 270 || rotation == -90) { sws_flags |= SWS_ROTATE_270; } SwsContext* sws_ctx = sws_getContext(src_frame->width, src_frame->height, (AVPixelFormat)src_frame->format, dst_width, dst_height, (AVPixelFormat)dst_frame->format, sws_flags, NULL, NULL, NULL); // 执行旋转转换 sws_scale(sws_ctx, src_frame->data, src_frame->linesize, 0, src_frame->height, dst_frame->data, dst_frame->linesize); // 现在用dst_frame作为修正后的帧写入OpenGL纹理即可,宽高比自动保持正确 // ...后续渲染逻辑... // 记得用完释放资源 sws_freeContext(sws_ctx); av_frame_free(&dst_frame);
补充:更高效的替代方案
其实没必要修改缓冲区数据,Chrome就是在渲染阶段处理的——直接在OpenGL里通过矩阵变换修正旋转,比如调整MVP矩阵或者修改顶点着色器的纹理坐标映射,这样不用拷贝像素数据,性能更好。如果你的场景允许,优先用这种方式。
内容的提问来源于stack exchange,提问作者VioGL
相关产品推荐
相关产品推荐

