我想开发一个以特定方式播放mp3文件的Java程序。我使用startTime和endTime在此文件中标记了许多片段。该程序应该播放第一个片段,然后睡5秒钟。然后播放第二个片段并再次睡眠。等等。我使用JavaFX类MediaPlayer。程序原型如下:
import javafx.application.Application;
import javafx.stage.Stage;
import java.io.FileNotFoundException;
import java.io.IOException;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.util.Duration;
import java.util.concurrent.TimeUnit;
public class JavaFXMediaPlayer02 extends Application {
@Override
public void start(Stage stage) throws FileNotFoundException,IOException,InterruptedException {
Media media = new Media("file:///D:/1016_00.mp3");
MediaPlayer mediaPlayer = new MediaPlayer(media);
//Set and play the first fragment of mp3-file
mediaPlayer.setStartTime(Duration.millis(1219.0));
mediaPlayer.setStopTime(Duration.millis(2728.0));
mediaPlayer.play();
System.out.println("1st fragment played!");
TimeUnit.SECONDS.sleep(5);
//Set and play the second fragment
mediaPlayer.setStartTime(Duration.millis(3947.0));
mediaPlayer.setStopTime(Duration.millis(6629.0));
mediaPlayer.play();
System.out.println("2nd fragment played!");
TimeUnit.SECONDS.sleep(5);
//Set and play the second fragment
mediaPlayer.setStartTime(Duration.millis(7453.0));
mediaPlayer.setStopTime(Duration.millis(10704.0));
mediaPlayer.play();
System.out.println("3rd fragment played!");
}
public static void main(String[] args) {
launch(args);
}
}
但我只听到第三个片段。怎么了?为什么我没有听到第一个和第二个片段?如何纠正我的程序? JavaFX不适合我的任务吗?
答案 0 :(得分:2)
这里的问题在于TimeUnit.SECONDS.sleep(5);
调用。此方法将当前线程设置为睡眠。在您的情况下,此线程是JavaFX应用程序线程。这导致整个应用程序“冻结”(如果你添加了一些GUI元素会更加明显),因此mediaPlayer.play();
命令被执行,但由于睡眠功能而立即被“冻结”。在`TimeUnit.SECONDS.sleep(5);
来电之后,您为MediaPlayer
设置新的开始和结束时间,然后再次执行play()
,以便在新的开始时间开始播放。这就是为什么只播放你的最后一个片段。
现在解决方案:
您永远不应该在JavaFX App Thread上调用Thread.sleep()
或类似的方法。但在你的情况下,你必须在播放片段之间等待一段时间。第一种方法是在新线程上调用Thread.sleep()
或TimeUnit.SECONDS.sleep(5);
并在JFX App Thread上调用Mediaplayer
方法。但这不能正常工作,因为您尚未设置调用线程的“顺序”。有多种方法可以执行此操作(通过Semaphores,Locks and Conditions,JavaFX Concurrency等等...)
我尝试通过快速编程来解决您的问题,但我遇到了mediaPlayer.setStopTime(Duration.millis());
的问题。它似乎不适用于我的计算机,因此文件始终播放到最后。我添加了一个停止按钮来模拟自动停止。
以下类设置新的起点和终点并播放片段。如果媒体播放器停止,它将调用LittleMediaScheduler
类上的下一个片段。
public class LittleMediaHelper implements Runnable {
public double startTime;
public double endTime;
public MediaPlayer player;
public int id;
public LittleMediaScheduler scheduler;
public LittleMediaHelper(double startTime, double endTime,
MediaPlayer player, int id) {
this.startTime = startTime;
this.endTime = endTime;
this.player = player;
this.id = id;
}
public LittleMediaScheduler getScheduler() {
return scheduler;
}
public void setScheduler(LittleMediaScheduler scheduler) {
this.scheduler = scheduler;
}
@Override
public void run() {
Platform.runLater(new Runnable() {
@Override
public void run() {
player.setStartTime(Duration.millis(startTime));
player.setStopTime(Duration.millis(endTime));
System.out.println(player.getStartTime());
System.out.println(player.getStopTime());
player.play();
player.setOnStopped(new Runnable() {
@Override
public void run() {
int idtmp = id + 1;
System.out.println("NEXT " + idtmp);
scheduler.call(idtmp);
}
});
}
});
}
}
这个类负责在新线程上睡眠一定数量,并在成功睡眠后调用下一个LittleMediaHelper
课程播放功能。
public class LittleMediaScheduler {
private ArrayList<LittleMediaHelper> hArrL;
private int SLEEPTIME = 2000;
public LittleMediaScheduler(LittleMediaHelper... helpers) {
this.hArrL = new ArrayList<>();
for (LittleMediaHelper h : helpers) {
h.setScheduler(this);
System.out.println(h.startTime);
this.hArrL.add(h);
}
System.out.println(hArrL.size());
}
public void init() {
Thread t = new Thread(this.hArrL.get(0));
t.start();
}
public void call(final int id) {
Thread t = new Thread(new Task<String>() {
@Override
protected String call() throws Exception {
Thread.sleep(SLEEPTIME);
return null;
}
@Override
protected void succeeded() {
super.succeeded();
System.out.println("Next playing...");
if (id > LittleMediaScheduler.this.hArrL.size() - 1) {
return;
}
LittleMediaHelper next = LittleMediaScheduler.this.hArrL
.get(id);
Thread nextT = new Thread(next);
nextT.start();
}
});
t.start();
}
}
带有停止按钮的主类。没有mediaPlayer.pause()
,虽然设置了新的起始端点,但播放器会以某种方式重复两次。不知道这是不是一个bug。
public class JavaFXMediaPlayer02 extends Application {
@Override
public void start(Stage stage) throws FileNotFoundException, IOException,
InterruptedException {
Media media = new Media("file:///C:/test.mp3");
final MediaPlayer mediaPlayer = new MediaPlayer(media);
LittleMediaHelper phase1 = new LittleMediaHelper(0, 1000, mediaPlayer,
0);
LittleMediaHelper phase2 = new LittleMediaHelper(50000, 55000,
mediaPlayer, 1);
LittleMediaHelper phase3 = new LittleMediaHelper(200000, 200500,
mediaPlayer, 2);
LittleMediaScheduler scheduler = new LittleMediaScheduler(phase1,
phase2, phase3);
scheduler.init();
Group g = new Group();
Button b = new Button("STOP");
b.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent arg0) {
mediaPlayer.pause();
mediaPlayer.stop();
}
});
g.getChildren().add(b);
Scene sc = new Scene(g);
stage.setScene(sc);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}