You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何判断vlcj中MediaDiscoverer的媒体发现进程何时完成?

如何判断vlcj MediaDiscoverer的本地音乐目录发现进程完成?

我正在用vlcj 4.8.0开发音乐播放器,想通过MediaDiscoverer组件自动查找本地包含音频文件的目录(之前用DirectoryChooser和file.listFiles(File::isDirectory)实现,现在觉得MediaDiscoverer更适配媒体场景)。

修改了vlcj示例库的MediaDiscovererTest.java来过滤出音频目录,但遇到了异步扫描的问题:代码能在发现目录时打印路径并保存到列表,但主线程会直接执行后续逻辑(比如打印列表长度),导致输出始终为0。目前只能用Thread.sleep(2000)来临时等待,但这显然不是可靠方案。

之前处理媒体解析时,可以通过MediaEventListener的mediaParsedChanged回调判断解析完成,但查遍MediaDiscoverer文档没找到类似的完成事件,请问有没有更可靠的方式判断扫描何时结束?

我的修改后代码如下:

public class MediaDiscovererTest {

    private static final List<String> musicDirectories = new ArrayList<>();

    public static void main(String[] args) throws Exception {

        MediaPlayerFactory factory = new MediaPlayerFactory();
        List<MediaDiscovererDescription> discoverers = factory.mediaDiscoverers().discoverers(MediaDiscovererCategory.LOCAL_DIRS);

        if (discoverers.isEmpty()) {
            return;
        }

        Iterator<MediaDiscovererDescription> iterator = discoverers.iterator();
        while (iterator.hasNext()) {
            MediaDiscovererDescription discoverer = iterator.next();
            if (discoverer.name().equals("picture_dir") || discoverer.name().equals("video_dir")) {
                iterator.remove(); // 仅关注音频目录
            }
        }

        for (MediaDiscovererDescription description : discoverers) {
            final String name = description.name();
            MediaDiscoverer discoverer = factory.mediaDiscoverers().discoverer(name);

            MediaList list = discoverer.newMediaList();

            MediaListEventAdapter mediaListEventAdapter = new MediaListEventAdapter() {
                @Override
                public void mediaListItemAdded(MediaList mediaList, MediaRef item, int index) {
                    Media newMedia = item.newMedia();
                    System.out.println(newMedia.info().mrl());
                    // 将新目录添加到列表
                    musicDirectories.add(newMedia.info().mrl());
                    newMedia.release();
                }
            };

            list.events().addMediaListEventListener(mediaListEventAdapter);

            discoverer.start();
//            list.events().removeMediaListEventListener(mediaListEventAdapter);
//            discoverer.release();
//            list.release();
        }

        System.out.println(musicDirectories.size()); // 当前测试状态下,此处始终输出0

        Thread.currentThread().join();

    }

}

解决方案

vlcj的MediaDiscoverer本身没有提供直接的扫描完成回调,但可以通过以下两种可靠方式实现同步等待:

方法1:用CountDownLatch结合超时判断(推荐)

针对一次性扫描的目录发现器(如music_dir),扫描完成后不会再发送mediaListItemAdded事件,我们可以监控新条目的添加时间,超过阈值后认为扫描完成:

public class MediaDiscovererTest {

    private static final List<String> musicDirectories = Collections.synchronizedList(new ArrayList<>());
    private static final CountDownLatch scanLatch = new CountDownLatch(1);
    private static long lastItemAddedTimestamp;

    public static void main(String[] args) throws Exception {

        MediaPlayerFactory factory = new MediaPlayerFactory();
        List<MediaDiscovererDescription> discoverers = factory.mediaDiscoverers().discoverers(MediaDiscovererCategory.LOCAL_DIRS);

        if (discoverers.isEmpty()) {
            return;
        }

        // 过滤非音频目录发现器
        discoverers.removeIf(d -> d.name().equals("picture_dir") || d.name().equals("video_dir"));

        for (MediaDiscovererDescription description : discoverers) {
            MediaDiscoverer discoverer = factory.mediaDiscoverers().discoverer(description.name());
            MediaList list = discoverer.newMediaList();

            MediaListEventAdapter eventAdapter = new MediaListEventAdapter() {
                @Override
                public void mediaListItemAdded(MediaList mediaList, MediaRef item, int index) {
                    try (Media media = item.newMedia()) {
                        String dirPath = media.info().mrl();
                        System.out.println(dirPath);
                        musicDirectories.add(dirPath);
                        lastItemAddedTimestamp = System.currentTimeMillis();
                    }
                }
            };

            list.events().addMediaListEventListener(eventAdapter);
            discoverer.start();
        }

        // 启动扫描完成监控线程
        new Thread(() -> {
            while (true) {
                long currentTime = System.currentTimeMillis();
                // 2秒无新条目则判定扫描完成
                if (currentTime - lastItemAddedTimestamp > 2000) {
                    scanLatch.countDown();
                    break;
                }
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }).start();

        // 等待扫描完成
        scanLatch.await();

        System.out.println("扫描完成,共找到" + musicDirectories.size() + "个音乐目录");

        // 释放资源
        factory.release();
    }
}

方法2:轮询列表稳定性(简单场景适用)

通过轮询音乐目录列表的长度,当连续多次检查长度不变时,判定扫描完成:

// 在discoverer.start()之后添加轮询逻辑
for (MediaDiscovererDescription description : discoverers) {
    // ... 初始化发现器和事件监听逻辑 ...
    discoverer.start();
}

// 轮询等待扫描稳定
int previousSize = -1;
int stableCheckCount = 0;
while (stableCheckCount < 3) { // 连续3次长度不变则停止
    Thread.sleep(500);
    int currentSize = musicDirectories.size();
    if (currentSize == previousSize) {
        stableCheckCount++;
    } else {
        previousSize = currentSize;
        stableCheckCount = 0;
    }
}

System.out.println("扫描完成,共找到" + musicDirectories.size() + "个音乐目录");

注意事项

  • 必须使用同步集合(如Collections.synchronizedList),因为mediaListItemAdded回调在异步线程中执行,避免并发修改异常。
  • 不同平台的目录发现器行为有差异,超时时间可根据实际场景调整。
  • 扫描完成后务必释放MediaDiscoverer、MediaList和MediaPlayerFactory资源,防止内存泄漏。

内容的提问来源于stack exchange,提问作者GreatTusk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 06:12:07