JavaFX+VLCJ播放列表:后台获取媒体时长及报错修复
问题描述
我用JavaFX+VLCJ开发媒体播放器,需要在播放列表ListView中显示单个媒体时长与总时长,目标格式如下:
1> my_visiting_video.mp4 | 01:21:35 2> avengers_end_game.flv | 02:13:40 TotalDuration: 03:35:15
基础播放功能正常,但后台线程获取时长时遇到三个问题:
- Xlib线程未初始化错误,提示需传入
--no-xlib参数 - JavaFX线程异常:操作不在FX应用线程中
- 运行时弹出额外的播放窗口
相关代码
DMediaPlayer.java(播放核心类)
import uk.co.caprica.vlcj.factory.MediaPlayerFactory; import uk.co.caprica.vlcj.javafx.videosurface.ImageViewVideoSurface; import uk.co.caprica.vlcj.player.embedded.EmbeddedMediaPlayer; import javafx.scene.image.ImageView; import java.io.FileNotFoundException; public class DMediaPlayer implements MediaPlayerInterface { private ImageView mediaView; private final MediaPlayerFactory mediaPlayerFactory; private final EmbeddedMediaPlayer embeddedMediaPlayer; public DMediaPlayer() { this.mediaPlayerFactory = new MediaPlayerFactory(); this.embeddedMediaPlayer = mediaPlayerFactory.mediaPlayers().newEmbeddedMediaPlayer(); this.mediaView = new ImageView(); this.mediaView.setPreserveRatio(true); embeddedMediaPlayer.videoSurface().set(new ImageViewVideoSurface(this.mediaView)); } @Override public void load(String filePath) throws FileNotFoundException { embeddedMediaPlayer.media().startPaused(filePath); embeddedMediaPlayer.controls().setPosition(0f); } @Override public ImageView getMediaView(){ return mediaView; } }
updatePlayList方法(后台获取时长逻辑)
File[] mediaList; // 已在构造方法中初始化 private void updatePlayList(){ CompletableFuture.runAsync(() -> { long totalDur = 0; MediaPlayerFactory factory = new MediaPlayerFactory(); EmbeddedMediaPlayer player = factory.mediaPlayers().newEmbeddedMediaPlayer(); for (int i = 0; i < mediaList.length; i++){ long mediaDur = 0; player.media().startPaused(mediaList[i].getAbsolutePath()); mediaDur = player.status().length(); totalDur += mediaDur; drawer.addItem( (i+1) + "> " + mediaList[i].getName().substring(0, 25) + "..." + " | " + millisToDuration(mediaDur) ); } drawer.getTotalDuration().setText("Total Duration: " + totalDur); }); }
报错信息
../include/vlc_xlib.h:46:vlc_xlib_init: Xlib not initialized for threads. This process is probably using LibVLC incorrectly. Pass "--no-xlib" to libvlc_new() to fix this. Exception in thread "ForkJoinPool.commonPool-worker-3" java.lang.IllegalStateException: Not on FX application thread; currentThread = ForkJoinPool.commonPool-worker-3 at javafx.graphics@19/com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:299) ...
解决方案
1. 修复Xlib线程错误
创建MediaPlayerFactory时传入--no-xlib参数,禁用Xlib依赖——后台线程仅需解析元数据,无需视频渲染相关的X11功能:
MediaPlayerFactory factory = new MediaPlayerFactory("--no-xlib");
2. 修复JavaFX线程异常
所有更新JavaFX UI控件的操作(如drawer.addItem、setText)必须在FX应用线程执行,用Platform.runLater()包裹这些操作:
// 后台计算完成后,将UI更新逻辑放入Platform.runLater Platform.runLater(() -> { drawer.addItem(/* 列表项文本 */); drawer.getTotalDuration().setText(/* 总时长文本 */); });
3. 消除额外播放窗口
无需使用带UI渲染的EmbeddedMediaPlayer,改用HeadlessMediaPlayer(无头播放器,仅处理媒体元数据/逻辑,无UI输出),彻底避免弹出额外窗口:
HeadlessMediaPlayer player = factory.mediaPlayers().newHeadlessMediaPlayer();
4. 更优的时长获取方案(推荐)
直接解析媒体元数据,无需启动播放器,效率更高:
通过MediaPlayerFactory的media().newMedia()创建媒体对象,解析元数据获取时长,无需初始化播放器实例:
import javafx.application.Platform; import uk.co.caprica.vlcj.factory.MediaPlayerFactory; import uk.co.caprica.vlcj.media.Media; import java.util.ArrayList; import java.util.List; File[] mediaList; // 已在构造方法中初始化 private void updatePlayList(){ CompletableFuture.runAsync(() -> { long totalDur = 0; MediaPlayerFactory factory = new MediaPlayerFactory("--no-xlib"); List<String> playlistItems = new ArrayList<>(); for (int i = 0; i < mediaList.length; i++){ File mediaFile = mediaList[i]; Media media = factory.media().newMedia(mediaFile.getAbsolutePath()); media.parse(); // 同步解析元数据 long mediaDur = media.info().duration(); totalDur += mediaDur; // 构造列表项文本,避免文件名过长截断异常 String fileName = mediaFile.getName(); String displayName = fileName.length() > 25 ? fileName.substring(0,25) + "..." : fileName; String itemText = (i+1) + "> " + displayName + " | " + millisToDuration(mediaDur); playlistItems.add(itemText); media.release(); // 释放媒体资源 } // 统一在FX线程更新UI final long finalTotal = totalDur; Platform.runLater(() -> { playlistItems.forEach(drawer::addItem); drawer.getTotalDuration().setText("Total Duration: " + millisToDuration(finalTotal)); }); factory.release(); // 释放工厂资源 }); }
内容的提问来源于stack exchange,提问作者Chrishdev
相关产品推荐
相关产品推荐

