Java调用FFmpeg动态库

作者:罗上文,微信:Loken1,公众号:FFmpeg弦外之音

XXX

Java 是不能直接调用 DLL 里面的 C 函数的,它需要通过一个中间层,也就是通过 JNI 来实现调用。

1-1

首先,我们创建一个 ffmpegNative.java 文件,如下:

class ffmpegNative {
    public static native int avformat_version();
}

这里的 avformat_version() 是一个 Java 的函数,最重要的是 native 关键字,这个关键字代表 avformat_version() 函数会去调原生的 C 函数,那这个原生 C 函数的名称是什么呢?

我们可以用 javac 命令来生成 ffmpegNative.java 对应的 C 语言头文件来查看函数名。如下:

javac -h . ffmpegNative.java

执行完之后,就可以在当前目录看到 ffmpegNative.h,这是 C 语言的头文件,如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class ffmpegNative */

#ifndef _Included_ffmpegNative
#define _Included_ffmpegNative
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     ffmpegNative
 * Method:    avformat_version
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_ffmpegNative_avformat_1version
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

上面这段代码有一些防止重复引入、兼容 C++ 的代码,我们把它们删掉,会看得更清楚,删掉之后如下:

#include <jni.h>
JNIEXPORT jint JNICALL Java_ffmpegNative_avformat_1version
  (JNIEnv *, jclass);

可以看到,java 虚拟机给我们定义的 C 语言函数名称是 Java_ffmpegNative_avformat_1version(),现在我们只需要实现这个函数就可以了。

这个 Java_ffmpegNative_avformat_1version 函数命名是有规律的,具体请阅读《Java核心技术 卷二》第12章

现在我们创建一个新的文件 ffmpegNative.c,内容如下:

#include "ffmpegNative.h"
#include "libavformat/avformat.h"

JNIEXPORT jint JNICALL Java_helloNative_avformat_1version(JNIEnv *env, jclass cl)
{
    int ver = avformat_version();
    return ver;
};

这就是 Java 调用 FFmpeg dll 动态库函数的流程,如下:

1-2

完整的项目代码读者可以在 GitHub 下载。


现在让我们来编译这个项目代码。

cl /c ffmpegNative.c ^
/I "C:\Program Files\Java\jdk-22\include" ^
/I "C:\Program Files\Java\jdk-22\include\win32" ^
/I "E:\ffmpeg_principle_volume1\source\code\ffmpeg_java\build64\ffmepg-n4.4.1-msvc\include"

提醒:jni.h 头文件是在 jdk 目录 C:\Program Files\Java\jdk-22\include 里面的。

link.exe /DLL /DEBUG ^
/LIBPATH "E:\ffmpeg_principle_volume1\source\code\ffmpeg_java\build64\ffmepg-n4.4.1-msvc\bin\avformat.lib" ^
/EXPORT:Java_ffmpegNative_avformat_1version ^
/OUT:ffmpegNative.dll ffmpegNative.obj

上面的命令执行完之后,我们就能拿到 ffmpegNative.dll 这个动态库,读者可以使用 dumpbin 来查看依赖,可以看到 ffmpegNative.dll 是依赖 avformat-58.dll

dumpbin /DEPENDENTS ffmpegNative.dll

1-3

然后 Main.java 就可以调用 ffmpegNative.dll 动态库了,因为这个动态库完全是按照 JNI 标准编译出来的,里面的函数可以被 Java 虚拟机调用。

Main.java 文件的代码如下:

class Main {
    public static void main(String[] args) {
        int ver = ffmpegNative.avformat_version();
        System.out.println(ver);
    }
    static
    {
        System.loadLibrary("ffmpegNative");
    }
}

我简单讲一下这段代码的含义,System.loadLibrary("ffmpegNative") 是负责加载 dll 动态库到进程的。

然后 ffmpegNative.avformat_version() 是 Java 类的函数,这个函数会去调 JNI C 函数 Java_helloNative_avformat_1version(),然后 Java_helloNative_avformat_1version() 再去调 ffmpeg 里面的 avformat_version()

整个过程 JNI 的 C 函数起到了一个桥接的作用。


我们继续编译项目里面的 Main.java,如下:

javac Main.java
# 设置环境变量,让 Java 运行时可以找到动态库。
set path=%path%;E:\ffmpeg_principle_volume1\source\code\ffmpeg_java\build64\ffmepg-n4.4.1-msvc\bin;
java Main

输出的结果如下图:

1-4


有另一种更方便的方法,叫 JNA(java-native-access),简单补充一下。

FFmpeg 源码里面也有一个 jni.h,这是干什么的?


参考资料:

1,《Java核心技术 卷二》第12章

版权所属 xianwanzhiyin.net 罗上文 2024 all right reserved,powered by Gitbook该文件修订时间: 2024-07-04 00:08:26

results matching ""

    No results matching ""