如何在没有侦听器的情况下获取 javafx 媒体元数据

Posted

技术标签:

【中文标题】如何在没有侦听器的情况下获取 javafx 媒体元数据【英文标题】:how to get javafx media metadata without listener 【发布时间】:2012-12-27 02:55:51 【问题描述】:

所以我已经找了一个星期了,虽然每个问题都类似,但似乎没有一个问题与我的问题完全相同(尝试逆向工程其他类似于我想要的解决方案但没有成功。

解释穴居人风格:我正在尝试使用元数据创建列表。

    我打开一个多对话框并选择多个 mp3 我把文件放在ArrayList<File> 我使用增强的 for 循环遍历文件并使用媒体变量提取元数据 元数据的信息(例如“艺术家”)是我想要保存在 ArrayList 中的信息

问题在于监听器仅在增强循环完成后才能工作,这会导致 ArrayList<String> 拥有一个没有任何东西的对象

这是一个示例:

ArrayList<String> al;
String path;
public void open()
    files=chooser.showOpenMultipleDialog(new Stage());
    for( File f:files)             
        path=f.getPath();
        Media media = new Media("file:/"+path.replace("\\", "/").replace(" ", "%20"));
        al= new ArrayList<String>();
        media.getMetadata().addListener(new MapChangeListener<String, Object>()                  
            public void onChanged(Change<? extends String, ? extends Object> change) 
                if (change.wasAdded()) 
                    if (change.getKey().equals("artist")) 
                        al.add((String) change.getValueAdded());
                     
                
            
        );
    //close for loop
    //then i want to see the size of al like this
    system.out.println(al.size());
    //then it returns 1 no matter how much file i selected
    //when i system out "al" i get an empty string

【问题讨论】:

【参考方案1】:

我对这个问题的解决方案是这样的:

public class MediaListener implements MapChangeListener<String, Object>

    public String title = null;
    public String artist = null;
    public String album = null;

    private final Consumer<MediaListener> handler;
    private boolean handled = false;

    public MediaListener(Consumer<MediaListener> handler)
    
        this.handler = handler;
    

    @Override
    public void onChanged(MapChangeListener.Change<? extends String, ?> ch)
    
        if (ch.wasAdded())
        
            String key = ch.getKey();
            switch (key)
            
                case "title":
                    title = (String) ch.getValueAdded();
                    break;
                case "artist":
                    artist = (String) ch.getValueAdded();
                    break;
                case "album":
                    album = (String) ch.getValueAdded();
                    break;
            

            if (!handled && title != null && artist != null && album != null)
            
                handler.accept(this);
                handled = true;
            
        
    

这可能不是最好的方法,但它比为每个文件创建一个新的 MediaPlayer 更干净。

示例用法:

Media media = Util.createMedia(path);
media.getMetadata().addListener(new MediaListener((data) ->

    // Use the data object to access the media
));

【讨论】:

【参考方案2】:

您可以使用以下函数来检索给定媒体对象的元数据:

public static void initializeMetaData(Media media) 
    final Ref<Boolean> ready = new Ref<>(false);

    MediaPlayer mediaPlayer = new MediaPlayer(media);
    mediaPlayer.setOnReady(() -> 
        synchronized (ready) 
            ready.set(false);
            ready.notify();
        
    );

    synchronized (ready) 
        if (!ready.get()) 
            try 
                ready.wait();
             catch (InterruptedException e) 
                e.printStackTrace();
            
        
    

但是,不要从 JavaFX 线程调用initializeMetaData,否则线程会陷入死锁。

PS:必须构建这样的解决方法真的很荒谬。我希望将来 Media 会提供一个 initialize() 方法来完成这项工作。

【讨论】:

【参考方案3】:

通过添加侦听器来读取媒体源元数据的另一种方法是在媒体播放器中提取该信息 .setOnReady();这是java控制器类的一个示例部分

公共类 uiController 实现 Initializable

@FXML private Label label;
@FXML private ListView<String> lv;
@FXML private AnchorPane root;
@FXML private Button button;

private ObservableList<String> ol= FXCollections.observableArrayList();
private List<File> selectedFiles;
private final Object obj= new Object();

@Override
public void initialize(URL url, ResourceBundle rb) 
    assert button != null : "fx:id=\"button\" was not injected: check your FXML file 'ui.fxml'.";
    assert label != null : "fx:id=\"label\" was not injected: check your FXML file 'ui.fxml'.";
    assert lv != null : "fx:id=\"lv\" was not injected: check your FXML file 'ui.fxml'.";
    assert root != null : "fx:id=\"root\" was not injected: check your FXML file 'ui.fxml'.";

    // initialize your logic here: all @FXML variables will have been injected
    lv.setItems(ol);
  

@FXML private void open(ActionEvent event) 
    FileChooser.ExtensionFilter extention= new FileChooser.ExtensionFilter("Music Files", "*.mp3","*.m4a","*.aif","*.wav","*.m3u","*.m3u8");
    FileChooser fc= new FileChooser();
    fc.setInitialDirectory(new File(System.getenv("userprofile")));
    fc.setTitle("Select File(s)");
    fc.getExtensionFilters().add(extention);
    selectedFiles =fc.showOpenMultipleDialog(root.getScene().getWindow());
    if(selectedFiles != null &&!selectedFiles.isEmpty())
        listFiles();
    

/**
 * Convert each fie selected to its URI
 */
private void listFiles() 
    try 
        for (File file : selectedFiles) 
            readMetaData(file.toURI().toString());
            synchronized(obj)
               obj.wait(100);
            
        
     catch (InterruptedException ex) 
    
    System.gc();

/**
 * Read a Media source metadata 
 * Note: Sometimes the was unable to extract the metadata especially when 
 * i have selected large number of files reasons i don't known why
 * @param mediaURI Media file URI
 */
private void readMetaData(String mediaURI)
    final MediaPlayer mp= new MediaPlayer(new Media(mediaURI));
    mp.setOnReady(new Runnable() 

        @Override
        public void run() 
            String artistName=(String) mp.getMedia().getMetadata().get("artist");
            ol.add(artistName);
            synchronized(obj)//this is required since mp.setOnReady creates a new thread and our loopp  in the main thread
                obj.notify();// the loop has to wait unitl we are able to get the media metadata thats why use .wait() and .notify() to synce the two threads(main thread and MediaPlayer thread)
            
        
    );

所做的一些更改是使用 ObservableList 来存储元数据中的艺术家姓名

在代码中你会发现这个

 同步(obj)
                    obj.wait(100);
                 
我这样做是因为 mediaplayer .setOnReady() 创建了一个新线程并且循环位于主应用程序线程中,循环必须等待一段时间才能创建另一个线程并且我们能够提取元数据,并且在.setOnReady() 有一个
 同步(obj)
                    对象通知;
                 
唤醒主线程,因此循环能够移动到下一个项目

我承认这可能不是最好的解决方案,但欢迎任何有更好方法从文件列表中读取 JavaFx 媒体元数据的人 完整的 Netbeans 项目可以在这里找到https://docs.google.com/file/d/0BxDEmOcXqnCLSTFHbTVFcGIzT1E/edit?usp=sharing

plus 使用 JavaFX 创建了一个小型 MediaPlayer 应用程序,该应用程序利用元数据 https://docs.google.com/file/d/0BxDEmOcXqnCLR1Z0VGN4ZlJkbUU/edit?usp=sharing

【讨论】:

以上是关于如何在没有侦听器的情况下获取 javafx 媒体元数据的主要内容,如果未能解决你的问题,请参考以下文章

Javafx 。如何获得下一个元素非焦点丢失

如何在不发出单独请求的情况下将元数据和音轨从广播流中分离出来

JavaFX 从控制器获取场景

如何使用更改侦听器 JavaFX 在两个 ListView 之间移动项目

如何在不使用 Android SDK 的 playUri 的情况下获取歌曲元数据?

JavaFX:如何在初始化期间从控制器获取阶段?