diff --git a/core/src/main/java/dev/quoije/umd/player/PlaybackEngine.java b/core/src/main/java/dev/quoije/umd/player/PlaybackEngine.java index 35618c2..a0abcca 100644 --- a/core/src/main/java/dev/quoije/umd/player/PlaybackEngine.java +++ b/core/src/main/java/dev/quoije/umd/player/PlaybackEngine.java @@ -75,7 +75,7 @@ public final class PlaybackEngine implements AutoCloseable { * Decodes until the next video frame is available, or end of stream. * Returns {@code null} at end of stream. */ - public VideoFrame nextVideoFrame() throws IOException { + public synchronized VideoFrame nextVideoFrame() throws IOException { int budget = 1000; while (videoQueue.isEmpty() && budget-- > 0) { if (!routeOne()) { @@ -89,7 +89,7 @@ public final class PlaybackEngine implements AutoCloseable { * Decodes until the next audio chunk is available, or end of stream. * Returns {@code null} at end of stream. */ - public AudioChunk nextAudioChunk() throws IOException { + public synchronized AudioChunk nextAudioChunk() throws IOException { int budget = 1000; while (audioQueue.isEmpty() && budget-- > 0) { if (!routeOne()) { @@ -100,7 +100,7 @@ public final class PlaybackEngine implements AutoCloseable { } /** True once the stream has been fully decoded. */ - public boolean isEof() { + public synchronized boolean isEof() { return eof && videoQueue.isEmpty() && audioQueue.isEmpty(); } diff --git a/ui/src/main/java/dev/quoije/umd/ui/PlayerApp.java b/ui/src/main/java/dev/quoije/umd/ui/PlayerApp.java index 90f8055..fd57c1d 100644 --- a/ui/src/main/java/dev/quoije/umd/ui/PlayerApp.java +++ b/ui/src/main/java/dev/quoije/umd/ui/PlayerApp.java @@ -195,7 +195,7 @@ public final class PlayerApp extends Application { try (UmdIsoReader iso = new UmdIsoReader(Path.of(isoPath)); PlaybackEngine engine = PlaybackEngine.open(iso, streamName, 0)) { - startAudio(engine); + Thread audio = startAudio(engine); awaitClock(); long basePts = clockStartPts; long baseNanos = clockStartNanos; @@ -219,6 +219,10 @@ public final class PlayerApp extends Application { sleepUntil(targetNanos); } + // Wait for the audio thread to finish draining before the + // engine (and its decoders) is closed by this try block. + audio.join(2000); + MoviePlayer mp = moviePlayer; if (mp != null) { mp.onPlayListEnd(playListNumber); @@ -232,7 +236,7 @@ public final class PlayerApp extends Application { } /** Plays decoded PCM through the system audio line, acting as the master A/V clock. */ - private void startAudio(PlaybackEngine engine) { + private Thread startAudio(PlaybackEngine engine) { Thread audio = new Thread(() -> { SourceDataLine line = null; try { @@ -264,6 +268,7 @@ public final class PlayerApp extends Application { }, "audio-player"); audio.setDaemon(true); audio.start(); + return audio; } private static SourceDataLine openLine() throws Exception {