This discussion is archived
13 Replies Latest reply: Nov 25, 2012 7:14 AM by 975783 RSS

Media duration

975783 Newbie
Currently Being Moderated
Hi there,
I have a question about using the Media.getDuration() function. I could not find a way to get the duration of an Media file and return.
I know that the method is asynchron, so I implemented a Timeline which added a delay.
If i just want to display the result by using System.out.println()
The function is:

static double seconds = 0;

public static double get_PlayTime(String FileURL)
{
final Media music = new Media(FileURL);
final MediaPlayer mp = new MediaPlayer(music);
seconds = 0;
Timeline delaytimer = new Timeline(new KeyFrame(Duration.millis(50), new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
seconds = mp.getTotalDuration().toSeconds();
//seconds = music.getDuration().toSeconds();
System.out.println("music duration: " + music.getDuration());
System.out.println("seconds: " +seconds);
System.out.println("metadata:" + music.getMetadata());
}
}));
delaytimer.setCycleCount(1);
delaytimer.play();
return seconds;
}

System.out.println works just fine but of course my return secons returns 0 because the value is returned way before the timeline starts.
Please help me to make my function return the right amout of seconds. Is there a way to delay the return statement?
I do not use this function in my playerclass.

Thanks for your help.

Edited by: 972780 on 22.11.2012 22:36
  • 1. Re: Media duration
    shakir.gusaroff Expert
    Currently Being Moderated
    Hi. Your code looks OK. Here is my output:


    get_PlayTime("file:///c://coffee.mp3");


    music duration: 3186.9387749999996 ms
    seconds: 3.186938775
    metadata:{title=Code Monkey, comment-0==First line only, artist=Jonathan Coultan, raw metadata={ID3=java.nio.HeapByteBufferRpos=89 lim=89 cap=89}}
  • 2. Re: Media duration
    975783 Newbie
    Currently Being Moderated
    Sure the output is ok. But that is just 4 testing. I want the time as result value back, so i can use it in other functions :)
  • 3. Re: Media duration
    James_D Guru
    Currently Being Moderated
    This works for me. I haven't used Media and MediaPlayer much though, so I may be missing some subtleties.
    import java.util.concurrent.CountDownLatch;
    
    import javafx.application.Application;
    import javafx.application.Platform;
    import javafx.beans.property.ReadOnlyProperty;
    import javafx.beans.value.ChangeListener;
    import javafx.beans.value.ObservableValue;
    import javafx.scene.Scene;
    import javafx.scene.control.Label;
    import javafx.scene.layout.BorderPane;
    import javafx.scene.media.Media;
    import javafx.scene.media.MediaPlayer;
    import javafx.scene.media.MediaView;
    import javafx.stage.Stage;
    import javafx.util.Duration;
    
    
    public class PlayTime extends Application {
      
      public static double getPlayTime(String fileUrl) throws InterruptedException {
        final Media media = new Media(fileUrl);
        MediaPlayer player = new MediaPlayer(media);
        ReadOnlyProperty<Duration> duration = media.durationProperty();
        final CountDownLatch latch = new CountDownLatch(1);
        
        duration.addListener(new ChangeListener<Duration>() {
          @Override
          public void changed(ObservableValue<? extends Duration> observable,
              Duration oldValue, Duration newValue) {
              if (! newValue.isUnknown()) {
                latch.countDown();
            }
          }
        });
        latch.await();
        return duration.getValue().toSeconds();
      }
      
      @Override
      public void start(Stage primaryStage) {
        final String url = getParameters().getRaw().get(0);
        System.out.println(url);
        final BorderPane root = new BorderPane();
        final Label timeLabel = new Label();
        root.setTop(timeLabel);
        Media media = new Media(url);
        MediaPlayer player = new MediaPlayer(media);
        MediaView view = new MediaView(player);
        root.setCenter(view);
        player.play();
        Scene scene = new Scene(root, 300, 300);
        primaryStage.setScene(scene);
        primaryStage.show();
        new Thread(new Runnable() {
          @Override
          public void run() {
            try {
              final double playTime = getPlayTime(url);
              System.out.println(playTime);
              Platform.runLater(new Runnable() {
                @Override
                public void run() {
                  timeLabel.setText(playTime+" seconds"); 
                }
              });
            } catch (Exception exc) {
              exc.printStackTrace();
            }       
          }
        }).start();
    
      }
    
      public static void main(String[] args) throws InterruptedException {
        launch(args);
      }
    
    }
    Edited by: James_D on Nov 22, 2012 6:14 PM (More complete code; cleaner waiting on condition)
  • 4. Re: Media duration
    975783 Newbie
    Currently Being Moderated
    Thanks for your answer. But it is not working :(

    Edited by: 972780 on 23.11.2012 20:56
  • 5. Re: Media duration
    James_D Guru
    Currently Being Moderated
    Which line is giving the error?

    What versions of Java and JavaFX are you using?
  • 6. Re: Media duration
    975783 Newbie
    Currently Being Moderated
    Exception gone after cleaning the project. I use JavaFX 2.2
    So now i have the problem that i enter an infinite loop when calling the method
  • 7. Re: Media duration
    James_D Guru
    Currently Being Moderated
    A couple of things:

    The method I wrote blocks until the media duration is available. Because it blocks, it shouldn't be called from the JavaFX Application thread (which I assume it is, as it is hanging). Use the technique I showed in the start method to execute this on an application thread (and use Platform.runLater(...) to put any changes to the UI back on the JavaFX Application Thread as shown).

    There's no loop here, so I assume you mean it's blocking indefinitely. I can see this happening if the duration becomes available before the change listener is attached (no change would ever take place, so the latch would not be released). The updated code below should fix this.

    Finally, it's probably also sensible to let this method timeout if the duration doesn't become available in some reasonable amount of time. I don't know what a reasonable amount of time is; I tried this with a full-length (but low res) movie and it took over a second for the duration to be available. I set it below to 5 seconds, you might want to experiment with this a bit (or perhaps allow the timeout to be passed as a parameter to the method). If it times out, the method returns -1 instead of the actual duration.
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.TimeUnit;
    
    import javafx.application.Application;
    import javafx.application.Platform;
    import javafx.beans.property.ReadOnlyProperty;
    import javafx.beans.value.ChangeListener;
    import javafx.beans.value.ObservableValue;
    import javafx.scene.Scene;
    import javafx.scene.control.Label;
    import javafx.scene.layout.BorderPane;
    import javafx.scene.media.Media;
    import javafx.scene.media.MediaPlayer;
    import javafx.scene.media.MediaView;
    import javafx.stage.Stage;
    import javafx.util.Duration;
    
    
    public class PlayTimeComputer extends Application {
      
      private static final long TIMEOUT = 5000 ; // milliseconds
      
      public static double getPlayTime(String fileUrl) throws InterruptedException {
        final Media media = new Media(fileUrl);
        MediaPlayer player = new MediaPlayer(media);
        ReadOnlyProperty<Duration> duration = media.durationProperty();
        final CountDownLatch latch = new CountDownLatch(1);
        
        duration.addListener(new ChangeListener<Duration>() {
          @Override
          public void changed(ObservableValue<? extends Duration> observable,
              Duration oldValue, Duration newValue) {
              if (newValue != null && ! newValue.isUnknown()) {
                latch.countDown();
            }
          }
        });
    
        boolean success  = true ;
    
        // only wait on the latch if the duration is still unavailable
    
        if (duration.getValue()==null || duration.getValue().isUnknown()) {
          success = latch.await(TIMEOUT, TimeUnit.MILLISECONDS);
        }
        
        if (success) {
          return duration.getValue().toSeconds();
        } else return -1 ;
      }
      
      @Override
      public void start(Stage primaryStage) {
        final String url = getParameters().getRaw().get(0);
        System.out.println(url);
        final BorderPane root = new BorderPane();
        final Label timeLabel = new Label("Waiting...");
        root.setTop(timeLabel);
        Media media = new Media(url);
        MediaPlayer player = new MediaPlayer(media);
        MediaView view = new MediaView(player);
        root.setCenter(view);
        player.setAutoPlay(true);
        Scene scene = new Scene(root, 800, 800);
        primaryStage.setScene(scene);
        primaryStage.show();
        new Thread(new Runnable() {
          @Override
          public void run() {
            try {
              final double playTime = getPlayTime(url);
              System.out.println(playTime);
              Platform.runLater(new Runnable() {
                @Override
                public void run() {
                  timeLabel.setText(playTime+" seconds"); 
                }
              });
            } catch (Exception exc) {
              exc.printStackTrace();
            }       
          }
        }).start();
    
      }
    
      public static void main(String[] args) throws InterruptedException {
        launch(args);
      }
    
    }
  • 8. Re: Media duration
    975783 Newbie
    Currently Being Moderated
    Thank you so much for this information and help :) I am new to Java so i have a few huge knowledge gabs.
    I will try our code later and give you feedback if i could make it work :)
  • 9. Re: Media duration
    975783 Newbie
    Currently Being Moderated
    Ok i tried out the sourcecode and it is running fine, but not returning the right value. Just giving -1 to me. So latch.await(TIMEOUT, TimeUnit.MILLISECONDS); never becomes true

    I use it this way to test it.
        private void testbutton_clicked() //this is in my main application when i click the testbutton ;)
        {
            System.out.println("so lets see if get_PlayTime is running");
            new Thread(new Runnable()
            {
                @Override
                public void run()
                {
                    try
                    {
                        final double playTime = HelpFunctions.get_PlayTime("file:///D:/audio//02.mp3");
                        System.out.println(playTime); //just test if i get the right value
                    }
                    catch (Exception exc)
                    {
                        exc.printStackTrace();
                    }
                }
            }).start();
        }
    public class HelpFunctions
    {
        private static final long TIMEOUT = 3000 ; // milliseconds 
    
    
        public static double get_PlayTime(String fileUrl) throws InterruptedException
        {
            final Media media = new Media(fileUrl);
            ReadOnlyProperty<Duration> duration = media.durationProperty();
            final CountDownLatch latch = new CountDownLatch(1);
    
            duration.addListener(new ChangeListener<Duration>()
            {
                @Override
                public void changed(ObservableValue<? extends Duration> observable,
                Duration oldValue, Duration newValue)
                {
                    if (newValue != null && ! newValue.isUnknown())
                    {
                        latch.countDown();
                    }
                }
            });
    
            boolean success  = true ;
            // only wait on the latch if the duration is still unavailable
            if (duration.getValue()==null || duration.getValue().isUnknown())
            {
                success = latch.await(TIMEOUT, TimeUnit.MILLISECONDS);
            }
    
            if (success)
                return duration.getValue().toSeconds();
            else 
                return -1 ;
        }
    ...
    }
    The URL should be fine. i can use this to play a song with a MediaPlayer. Is there still something not right?

    Edited by: 972780 on 24.11.2012 19:13
  • 10. Re: Media duration
    James_D Guru
    Currently Being Moderated
    As I understand it, you need to attach the Media object to a MediaPlayer in order for the duration to be computed. As it is, there's no media player, so the duration is never computed, the ChangeListener is never invoked and the CountDownLatch cannot be released.

    Add the line
    new MediaPlayer(media);
    after
    final Media media = new Media(fileUrl);
  • 11. Re: Media duration
    975783 Newbie
    Currently Being Moderated
    Done. Works perfect. Thank you sooooo much for your help!
    But can you explain why i have to add
    new MediaPlayer(media);
    Is the Media just beeing loaded correct if you add it to a player or what is the issue?
  • 12. Re: Media duration
    James_D Guru
    Currently Being Moderated
    Don't know... My best guess is that the Media class simply represents the data associated with the media but doesn't actually open it, whereas the MediaPlayer class is the one that reads the content. The duration is only going to be available from reading the content, so the media object needs to be attached to a MediaPlayer before that is available. But this is just a guess; as I said I haven't used the media package a whole lot.
  • 13. Re: Media duration
    975783 Newbie
    Currently Being Moderated
    OK i can live with that. Thank you so much for your fast and direct help James.
    Problem solved Thread can be closed :)

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points