API внешних функций и памяти Java 22: перемещение структур

Я пытаюсь использовать API внешних функций и памяти в Java 22 для вызова формата libavformat FFmpeg. Сначала я использовал jextract для создания привязки:

jextract --target-package org.ffmpeg.avformat \
    --output /path/to/src/main/java \
    -I <base include dir> libavformat/avformat.h

Моя первоначальная цель — открыть видеофайл и узнать его размеры. Многие функции FFmpeg используют структуру AVFormatContext, которая является структурой основного контекста, которая ссылается на другие структуры, которые ссылаются на другие структуры и т. д. Поэтому мне нужно пройтись по нескольким различным структурам, чтобы получить ширину и высоту:

struct AVFormatContext {
    unsigned int nb_streams;
    AVStream **streams;
    // etc.
}

struct AVStream {
    AVCodecParameters *codecpar;
    // etc.
}

struct AVCodecParameters {
    int width;
    int height;
    // etc.
}

В C это будет включать что-то вроде:

int width  = formatCtx->streams[i]->codecpar->width;
int height = formatCtx->streams[i]->codecpar->height;

Вот моя попытка на 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);
}

К сожалению, последний вызов widthHandle.get() вызывает: java.lang.InternalError: a fault occurred in an unsafe memory access operation

Я не знаком с libavformat, но jextract должен был генерировать геттерные (и индексированные) вызовы геттеров для получения каждого поля-члена каждой из этих структур, избегая необходимости использовать Layout/VarHandle. например, посмотри, есть ли у тебя AVFormatContext.streams(formatCtx) и т. д.

DuncG 23.05.2024 16:37
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
3
1
99
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Похоже, у вас слишком много разыменований:

MemorySegment codecParams    = codecParamsPtr.get(C_POINTER, 0);

codecParamsPtr — это AVCodecParameters*, из которого вы затем пытаетесь загрузить указатель. Однако при этом будут прочитаны только первые 8 байтов структуры как указатель.

Кроме того, как говорит @DuncG в комментариях, вы можете просто использовать геттеры, которые генерирует jextract. Это должно работать:

// 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);

Работает как по волшебству! Спасибо!

alexantd 24.05.2024 13:20

Другие вопросы по теме