I'm trying to use the Foreign Function & Memory API in Java 22 to call into FFmpeg's libavformat. First, I used jextract to generate a binding:
jextract --target-package org.ffmpeg.avformat \ --output /path/to/src/main/java \ -I <base include dir> libavformat/avformat.hMy initial goal is to open a video file and get its dimensions. Many FFmpeg functions make use of a struct called AVFormatContext, which is a main context struct that refers to other structs, which refer to other structs, etc. So I need to walk through a few different structs to get the width & height:
struct AVFormatContext { unsigned int nb_streams; AVStream **streams; // etc.}struct AVStream { AVCodecParameters *codecpar; // etc.}struct AVCodecParameters { int width; int height; // etc.}In C, this would involve something like:
int width = formatCtx->streams[i]->codecpar->width;int height = formatCtx->streams[i]->codecpar->height;Here is my attempt in Java:
try (Arena arena = Arena.ofConfined()) { ///////////////////// begin setup //////////////////////// 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("Couldn't open file"); } MemorySegment formatCtx = formatCtxPtr.get(C_POINTER, 0); // Verify the AVFormatContext av_dump_format(formatCtx, 0, url, 0); ///////////////////// end setup //////////////////////// 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);}Sadly that last call towidthHandle.get() causes:java.lang.InternalError: a fault occurred in an unsafe memory access operation
- 2I'm not familiar with libavformat but jextract should have generated getter (and indexed) getter calls to retrieve each member field of each of those structures, avoiding you need to use Layout/VarHandle. eg see if you have
AVFormatContext.streams(formatCtx)etcDuncG– DuncG2024-05-23 14:37:10 +00:00CommentedMay 23, 2024 at 14:37
1 Answer1
It looks like you have one too many dereferences:
MemorySegment codecParams = codecParamsPtr.get(C_POINTER, 0);ThecodecParamsPtr is aAVCodecParameters*, which you then try to load a pointer from. That will however just read the first 8 bytes of the struct as a pointer.
Also, like @DuncG says in the comments, you can just use the getters that jextract generates. This should work:
// AVStream**MemorySegment streams = AVFormatContext.streams(formatCtx);// AVStream*MemorySegment stream = streams.getAtIndex(C_POINTER, 0);// AVCodecParameters*MemorySegment codecParams = AVStream.codecpar(stream);int width = AVCodecParameters.width(codecParams);1 Comment
Explore related questions
See similar questions with these tags.