# zlm4j_FFI **Repository Path**: IFinal/zlm4j_ffi ## Basic Information - **Project Name**: zlm4j_FFI - **Description**: 用Java FFM API(Project Panama),完成对ZLMediaKit 库调用,让java对视频开发更简单 - **Primary Language**: Java - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 2 - **Created**: 2026-06-06 - **Last Updated**: 2026-06-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # zlm4j_FFI [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) ## 介绍 zlm4j_FFI 是一个基于 Java FFM API(Project Panama)实现的 ZLMediaKit 库调用方案。通过 Java 22+ 提供的 FFM 接口,可以实现对 ZLMediaKit 原生库的高效调用,让 Java 开发视频流媒体应用更加简单便捷。 相比传统的 JNA 调用方式,FFM API 提供更接近原生的性能表现,能够更直接地操作内存和调用 native 函数。 可以用来编写JT1078流媒体服务器和GB28181视频服务器 ## 软件架构 | 组件 | 说明 | |------|------| | **JDK 版本** | JDK 22+(推荐 JDK 25) | | **依赖库** | ZLMediaKit(原生动静态库) | | **构建工具** | Maven | ## 功能特性 - ✅ HTTP/RTSP/RTMP/RTC 服务器管理 - ✅ 媒体播放器(MK_PLAYER) - ✅ 代理播放器(MK_PROXY_PLAYER) - ✅ 媒体录制(MP4/TS) - ✅ 流媒体推流(Pusher) - ✅ RTP 服务器 - ✅ WebRTC 支持 - ✅ 完整的回调事件机制(60+ 回调接口) - ✅ 跨平台支持(Windows/Linux/macOS) ## 项目结构 ``` src/main/java/com/sky/zlm4j/ ├── core/ # 核心 FFM 绑定 │ ├── InitEnv.java # 库加载与环境初始化 │ ├── ZLMApi.java # ZLMediaKit API 封装 │ └── ZLMediaKit.java # ZLMediaKit C库函数绑定 ├── structure/ # 数据结构封装 │ ├── MK_CONFIG.java # 配置结构 │ ├── MK_EVENTS.java # 事件回调结构 │ ├── MK_FRAME.java # 帧数据结构 │ ├── MK_INI.java # 配置文件操作 │ ├── MK_MEDIA.java # 媒体源管理 │ ├── MK_PLAYER.java # 播放器 │ ├── MK_PROXY_PLAYER.java # 代理播放器 │ ├── MK_TRACK.java # 音视频轨道 │ └── CodecArgs.java # 编解码参数 ├── callback/ # 回调接口定义(60+) │ ├── IMKPlayCallBack.java │ ├── IMKPublishCallBack.java │ └── ... ├── config/ │ └── MK_EVENTS.java # 全局事件配置 ├── Util/ │ └── Util.java # 工具类 └── WebRTCController.java # WebRTC 控制器 ``` ## 快速开始 ### 环境要求 - JDK 22 或更高版本 - ZLMediaKit 动态库(项目已内置各平台预编译库) ### 代理流案例 ``` package com.sky.zlm4j; import com.sky.zlm4j.callback.IMKNoReaderCallBack; import com.sky.zlm4j.callback.IMKProxyPlayerCallBack; import com.sky.zlm4j.callback.IMKStreamChangeCallBack; import com.sky.zlm4j.config.MK_CONFIG; import com.sky.zlm4j.config.MK_EVENTS; import com.sky.zlm4j.core.ZLMApi; import com.sky.zlm4j.structure.MK_INI; import com.sky.zlm4j.structure.*; import java.lang.foreign.MemorySegment; import static com.sky.zlm4j.core.ZLMApi.*; /** * MK_PROXY_PLAYER 独立测试类 */ public class MK_PROXY_PLAYER_Test { public static void main(String[] args) { // 使用一个全局 Arena,整个程序生命周期 // 1. 初始化环境 MK_CONFIG mk_config = new MK_CONFIG(8, 1, 1, null, 0, 0, null, 0, null, null); mk_config.mk_env_init(mk_config); // 2. 配置服务器 MK_INI mkIni = ZLMApi.mk_ini_default(); mkIni.mk_ini_set_option("general.mediaServerId", "JMediaServer"); mkIni.mk_ini_set_option("http.notFound", "

Media Server V1.0 By LiDaoFu

"); mkIni.mk_ini_set_option_int("protocol.auto_close", 0); mkIni.mk_ini_set_option_int("general.streamNoneReaderDelayMS", 10000); mkIni.mk_ini_set_option_int("general.maxStreamWaitMS", 30000); mkIni.mk_ini_set_option_int("protocol.enable_ts", 1); mkIni.mk_ini_set_option_int("protocol.enable_hls", 1); mkIni.mk_ini_set_option_int("protocol.enable_fmp4", 1); mkIni.mk_ini_set_option_int("protocol.enable_rtsp", 1); mkIni.mk_ini_set_option_int("protocol.enable_rtmp", 1); mkIni.mk_ini_set_option_int("protocol.enable_mp4", 1); mkIni.mk_ini_set_option_int("protocol.enable_hls_fmp4", 1); mkIni.mk_ini_set_option_int("protocol.enable_audio", 1); mkIni.mk_ini_set_option_int("protocol.mp4_as_player", 1); mkIni.mk_ini_set_option_int("protocol.mp4_max_second", 3600); mkIni.mk_ini_set_option("http.rootPath", "D:/www"); mkIni.mk_ini_set_option("protocol.mp4_save_path", "D:/www"); mkIni.mk_ini_set_option("protocol.hls_save_path", "D:/www"); mkIni.mk_ini_set_option_int("protocol.hls_demand", 0); mkIni.mk_ini_set_option_int("protocol.rtsp_demand", 0); mkIni.mk_ini_set_option_int("protocol.rtmp_demand", 0); mkIni.mk_ini_set_option_int("protocol.ts_demand", 0); mkIni.mk_ini_set_option_int("protocol.fmp4_demand", 0); // 3. 设置事件回调 MK_EVENTS mkEvents = new MK_EVENTS(); mkEvents.on_mk_media_changed = IMKStreamChangeCallBack.of((regist, mkMediaSource) -> { System.out.println("状态: " + (regist == 1 ? "注册" : "注销")); System.out.println("app: " + mkMediaSource.mk_media_source_get_app()); System.out.println("stream: " + mkMediaSource.mk_media_source_get_stream()); }); mkEvents.on_mk_media_no_reader = IMKNoReaderCallBack.of((sender) -> { System.out.println("无人观看,关闭流"); try { sender.mk_media_source_close(true); } catch (Throwable e) { e.printStackTrace(); } }); mkEvents.mk_events_listen(); // 4. 启动服务器 mk_http_server_start(7788, false); mk_rtsp_server_start(554, false); mk_rtmp_server_start(1935, false); mk_rtc_server_start(8000); // 5. 创建配置 MK_INI option = ZLMApi.mk_ini_create(); option.mk_ini_set_option_int("enable_mp4", 0); option.mk_ini_set_option_int("enable_audio", 0); option.mk_ini_set_option_int("enable_fmp4", 1); option.mk_ini_set_option_int("enable_ts", 1); option.mk_ini_set_option_int("enable_hls", 1); option.mk_ini_set_option_int("enable_rtsp", 1); option.mk_ini_set_option_int("enable_rtmp", 1); option.mk_ini_set_option_int("mp4_max_second", 3600); option.mk_ini_set_option_int("add_mute_audio", 0); option.mk_ini_set_option_int("auto_close", 0); // 6. 创建代理播放器 - 使用全局 Arena System.out.println("创建代理播放器..."); MK_PROXY_PLAYER proxy = ZLMApi.mk_proxy_player_create2("__defaultVhost__", "live", "test_stream", option); option.mk_ini_release(); if (proxy == null) { System.err.println("代理播放器创建失败!"); return; } // 7. 设置回调 - 使用全局 Arena IMKProxyPlayerCallBack onPlayResult = (pUser, err, what, sys_err) -> { if (err == 0) { System.out.println("✅ 播放成功"); } else { System.out.println("❌ 播放失败: " + what); } }; proxy.mk_proxy_player_set_on_play_result(onPlayResult, MemorySegment.NULL, null); // 8. 设置关闭回调 - 注意不要立即释放 IMKProxyPlayerCallBack onClose = (pUser, err, what, sys_err) -> { System.out.println("设置关闭回调"); // 13. 再次查询 int readerCount = proxy.mk_proxy_player_total_reader_count(); System.out.println("最终读者数量: " + readerCount); }; proxy.mk_proxy_player_set_on_close(onClose, MemorySegment.NULL); // 9. 开始播放 - 使用全局 Arena String sourceUrl = "rtmp://liteavapp.qcloud.com/live/liteavdemoplayerstreamid"; System.out.println("开始拉流: " + sourceUrl); proxy.mk_proxy_player_play(sourceUrl); // 11. 查询读者数量(这个调用是安全的) int readerCount = proxy.mk_proxy_player_total_reader_count(); System.out.println("当前读者数量: " + readerCount); try { Thread.sleep(60000); } catch (InterruptedException ex) { throw new RuntimeException(ex); } // 14.停止 mk_stop_all_server(); } } ```