Java 22外部函数与内存API遍历结构体:调用FFmpeg获视频尺寸报错
使用Java 22 Foreign Function & Memory API调用FFmpeg获取视频分辨率时的内存访问错误
背景与目标
我尝试用Java 22的Foreign Function & Memory API调用FFmpeg的libavformat库,先通过jextract生成绑定:
jextract --target-package org.ffmpeg.avformat \ --output /path/to/src/main/java \ -I <base include dir> libavformat/avformat.h
目标是打开视频文件并获取分辨率,FFmpeg中需要遍历AVFormatContext→AVStream→AVCodecParameters结构体来获取宽高,相关逻辑的C语言实现如下:
相关C结构体定义
struct AVFormatContext { unsigned int nb_streams; AVStream **streams; // 其他字段 } struct AVStream { AVCodecParameters *codecpar; // 其他字段 } struct AVCodecParameters { int width; int height; // 其他字段 }
C语言获取宽高的代码
int width = formatCtx->streams[i]->codecpar->width; int height = formatCtx->streams[i]->codecpar->height;
Java实现尝试
我编写的Java代码如下:
try (Arena arena = Arena.ofConfined()) { ///////////////////// 初始化部分 //////////////////////// MemorySegment formatCtxPtr = arena.allocate(C_POINTER); MemorySegment inputFormat = MemorySegment.NULL; MemorySegment formatDictPtr = MemorySegment.NULL; MemorySegment url = arena.allocateFrom("file:/path/to/video.mp4"); int result = avformat_open_input(formatCtxPtr, url, inputFormat, formatDictPtr); if (result != 0) { throw new IOException("无法打开文件"); } MemorySegment formatCtx = formatCtxPtr.get(C_POINTER, 0); // 验证AVFormatContext av_dump_format(formatCtx, 0, url, 0); ///////////////////// 初始化结束 //////////////////////// StructLayout formatCtxLayout = (StructLayout) AVFormatContext.layout(); VarHandle streamsHandle = formatCtxLayout.varHandle( MemoryLayout.PathElement.groupElement("streams")); MemorySegment streamsPtr = (MemorySegment) streamsHandle.get(formatCtx, 0); MemorySegment stream = streamsPtr.getAtIndex(C_POINTER, 0); StructLayout avStreamLayout = (StructLayout) AVStream.layout(); VarHandle codecParamsHandle = avStreamLayout.varHandle( MemoryLayout.PathElement.groupElement("codecpar")); MemorySegment codecParamsPtr = (MemorySegment) codecParamsHandle.get(stream, 0); MemorySegment codecParams = codecParamsPtr.get(C_POINTER, 0); StructLayout codecParamsLayout = (StructLayout) AVCodecParameters.layout(); VarHandle widthHandle = codecParamsLayout.varHandle( MemoryLayout.PathElement.groupElement("width")); int width = (int) widthHandle.get(codecParams, 0); }
遇到的错误
执行到widthHandle.get()时触发错误:
java.lang.InternalError: 不安全内存访问操作中发生错误
问题分析与解决方法
1. 缺失流信息解析步骤
仅调用avformat_open_input无法填充流的完整参数,必须后续调用avformat_find_stream_info解析流的详细信息,否则codecpar可能未被正确初始化:
// 在av_dump_format之前添加以下代码 result = avformat_find_stream_info(formatCtx, formatDictPtr); if (result < 0) { throw new IOException("无法获取流信息"); }
2. 未做空指针与流类型检查
FFmpeg的streams数组可能包含无效流,codecpar也可能为空,且并非所有流都是视频流。必须添加检查逻辑:
// 读取流数量 VarHandle nbStreamsHandle = formatCtxLayout.varHandle(MemoryLayout.PathElement.groupElement("nb_streams")); int nbStreams = (int) nbStreamsHandle.get(formatCtx, 0); StructLayout codecParamsLayout = (StructLayout) AVCodecParameters.layout(); VarHandle codecTypeHandle = codecParamsLayout.varHandle(MemoryLayout.PathElement.groupElement("codec_type")); VarHandle widthHandle = codecParamsLayout.varHandle(MemoryLayout.PathElement.groupElement("width")); VarHandle heightHandle = codecParamsLayout.varHandle(MemoryLayout.PathElement.groupElement("height")); for (int i = 0; i < nbStreams; i++) { MemorySegment stream = streamsPtr.getAtIndex(C_POINTER, i); if (stream == MemorySegment.NULL) continue; MemorySegment codecParams = (MemorySegment) codecParamsHandle.get(stream, 0); if (codecParams == MemorySegment.NULL) continue; // 筛选视频流(AVMEDIA_TYPE_VIDEO为jextract生成的常量) if ((int) codecTypeHandle.get(codecParams, 0) == AVMEDIA_TYPE_VIDEO()) { int width = (int) widthHandle.get(codecParams, 0); int height = (int) heightHandle.get(codecParams, 0); System.out.printf("视频分辨率:%dx%d%n", width, height); break; } }
3. 结构体布局可能不匹配
FFmpeg结构体存在平台相关的内存填充,若jextract使用的头文件与链接的FFmpeg库版本不一致,会导致内存偏移错误:
- 确保jextract使用的头文件和运行时链接的FFmpeg库版本完全一致
- 可通过C语言的
offsetof宏验证结构体成员偏移,对比jextract生成的StructLayout中的偏移值
4. 资源泄漏风险
使用完FFmpeg上下文后,必须调用avformat_close_input释放资源:
finally { if (!formatCtxPtr.isNull()) { avformat_close_input(formatCtxPtr); } }
内容的提问来源于stack exchange,提问作者alexantd
相关产品推荐
相关产品推荐

