Play feature under the menu; add mpv path; simplify VLC launch

Root causes found this session (all player-side, disc is valid -- bd_info
validates it and mpv plays the ISO's feature directly):

- Plain 'vlc <iso>' can't run ANY BD-J: system Java is 26, and libbluray's BD-J
  needs a JVM where System.setSecurityManager() works (<=21). run-vlc.sh pins
  mise Temurin 8, which is why it gets a menu at all.
- The BD-J VFS-cache 'error creating /tmp/libbluray-bdj-cache' flood is
  libbluray's SecurityManager.canWrite() denying the cache root while an Xlet
  context is active. Fixed by pointing TMPDIR at LIBBLURAY_CACHE_ROOT (buda
  root), which canWrite() allows unconditionally.
- Menu->playback failed with 'blurayReleaseVout: subpicture channel exists':
  VLC botches the background->playlist transition for an idle BD-J menu. Now the
  Xlet starts the feature immediately and draws the menu on top, so VLC stays in
  normal playing mode; MAIN FEATURE just hides the menu, any key brings it back,
  and the feature loops.

Also: drop the redundant tools/vlc-plugins swap (system VLC already ships the
bluray plugin), and add scripts/run-mpv.sh to play the feature with no BD-J/JVM
at all. Vendored the SDK helloworld sample for reference.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
happy 2026-08-25 16:25:42 -04:00
parent 48c172cb80
commit 4c270b744f
10 changed files with 362 additions and 25 deletions

View file

@ -7,8 +7,15 @@ import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.media.ControllerEvent;
import javax.media.ControllerListener;
import javax.media.EndOfMediaEvent;
import javax.media.Manager;
import javax.media.MediaLocator;
import javax.media.Player;
import javax.media.Time;
public class AdjustmentsMenu extends Container implements KeyListener {
public class AdjustmentsMenu extends Container implements KeyListener, ControllerListener {
private static final String[] MAIN_ITEMS = {
"MAIN FEATURE",
"BONUS: WAITING ROOM OUTTAKES",
@ -30,6 +37,8 @@ public class AdjustmentsMenu extends Container implements KeyListener {
private boolean hidden;
private int codeProgress;
private String status = "BD-J scaffold loaded";
private Player featurePlayer;
private boolean menuShown = true;
public AdjustmentsMenu() {
setFocusable(true);
@ -111,7 +120,11 @@ public class AdjustmentsMenu extends Container implements KeyListener {
selected = 0;
status = "Returned to approved materials";
} else if ("MAIN FEATURE".equals(item)) {
status = "Main feature is playlist 00000";
// The feature is already playing behind the menu; just hide the
// menu to reveal it. Press the menu key (or ENTER) to bring it back.
status = "Playing main feature -- press ENTER for menu";
setMenuVisible(false);
return;
} else if ("SETTINGS".equals(item)) {
status = "Settings stub selected";
} else {
@ -121,6 +134,47 @@ public class AdjustmentsMenu extends Container implements KeyListener {
repaint();
}
// Called once by the Xlet at startup. Start the feature playing straight
// away and leave it running for the whole session. This keeps VLC in normal
// "playing" mode with a live playlist behind the menu, so selecting an item
// never triggers the background->playlist transition that VLC botches
// ("blurayReleaseVout: subpicture channel exists", no video). The menu is
// just drawn on top and toggled with setMenuVisible().
public void startPlayback() {
if (featurePlayer != null) {
return;
}
try {
// libbluray's "bd" protocol; PLAYLIST:00000 -> BDMV/PLAYLIST/00000.mpls.
featurePlayer = Manager.createPlayer(new MediaLocator("bd://PLAYLIST:00000"));
featurePlayer.addControllerListener(this);
featurePlayer.start();
} catch (Exception e) {
featurePlayer = null;
status = "Playback failed: " + e.getMessage();
}
repaint();
}
// Toggle only this menu Container, not the whole HScene, so the scene (and
// its key listener) stays alive while the video shows underneath.
private void setMenuVisible(boolean visible) {
menuShown = visible;
setVisible(visible);
if (visible) {
requestFocus();
}
repaint();
}
public void controllerUpdate(ControllerEvent event) {
// Loop the feature so there is always a live playlist behind the menu.
if (event instanceof EndOfMediaEvent && featurePlayer != null) {
featurePlayer.setMediaTime(new Time(0));
featurePlayer.start();
}
}
private void unlockHidden() {
hidden = true;
selected = 0;
@ -131,6 +185,14 @@ public class AdjustmentsMenu extends Container implements KeyListener {
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
// When the menu is hidden (feature playing full screen), any key brings
// the menu back over the still-running video.
if (!menuShown) {
setMenuVisible(true);
return;
}
String[] items = hidden ? HIDDEN_ITEMS : MAIN_ITEMS;
if (code == KeyEvent.VK_UP) {

View file

@ -29,6 +29,9 @@ public class MyXlet extends Thread implements Xlet {
scene.setVisible(true);
menu.requestFocus();
scene.requestFocus();
// Start the feature immediately so VLC always has a live playlist behind
// the menu (avoids the background->playlist transition it botches).
menu.startPlayback();
start();
}