JMF playback, transparent graphics plane, macOS tsMuxeR
Wire up MAIN FEATURE to play playlist 00000 via JMF Manager.createPlayer / prefetch / start. Clear the BD-J graphics plane with AlphaComposite.Clear so the video plane shows through during playback; redraw the menu on EndOfMediaEvent or Enter/Escape. Claim exclusive key access through DVB EventManager so the player firmware does not interpret arrow keys as transport controls. Swap the Linux ELF tsMuxeR binary for the native macOS ARM64 build. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
1b44001d44
commit
ac846dc38f
3 changed files with 108 additions and 6 deletions
|
|
@ -1,14 +1,27 @@
|
|||
package org.homebrew;
|
||||
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.Color;
|
||||
import java.awt.Container;
|
||||
import java.awt.Font;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
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;
|
||||
import org.havi.ui.event.HRcEvent;
|
||||
|
||||
public class AdjustmentsMenu extends Container implements KeyListener, ControllerListener {
|
||||
private Player player;
|
||||
private boolean playing;
|
||||
|
||||
public class AdjustmentsMenu extends Container implements KeyListener {
|
||||
private static final String[] MAIN_ITEMS = {
|
||||
"MAIN FEATURE",
|
||||
"BONUS: WAITING ROOM OUTTAKES",
|
||||
|
|
@ -38,6 +51,17 @@ public class AdjustmentsMenu extends Container implements KeyListener {
|
|||
public void paint(Graphics g) {
|
||||
int w = getWidth();
|
||||
int h = getHeight();
|
||||
Graphics2D g2 = (Graphics2D) g;
|
||||
|
||||
// Clear the graphics plane to transparent so the video plane shows
|
||||
g2.setComposite(AlphaComposite.Clear);
|
||||
g2.fillRect(0, 0, w, h);
|
||||
g2.setComposite(AlphaComposite.SrcOver);
|
||||
|
||||
if (playing) {
|
||||
return;
|
||||
}
|
||||
|
||||
String[] items = hidden ? HIDDEN_ITEMS : MAIN_ITEMS;
|
||||
|
||||
g.setColor(new Color(8, 8, 10));
|
||||
|
|
@ -111,7 +135,7 @@ 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";
|
||||
playFeature();
|
||||
} else if ("SETTINGS".equals(item)) {
|
||||
status = "Settings stub selected";
|
||||
} else {
|
||||
|
|
@ -121,6 +145,29 @@ public class AdjustmentsMenu extends Container implements KeyListener {
|
|||
repaint();
|
||||
}
|
||||
|
||||
private void playFeature() {
|
||||
try {
|
||||
if (player == null) {
|
||||
player = Manager.createPlayer(
|
||||
new MediaLocator("bd://PLAYLIST:00000"));
|
||||
player.addControllerListener(this);
|
||||
} else {
|
||||
player.setMediaTime(new Time(0));
|
||||
}
|
||||
player.prefetch();
|
||||
player.start();
|
||||
playing = true;
|
||||
status = "Playing main feature";
|
||||
} catch (Exception e) {
|
||||
status = "Playback error: " + e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
private void showMenu() {
|
||||
playing = false;
|
||||
repaint();
|
||||
}
|
||||
|
||||
private void unlockHidden() {
|
||||
hidden = true;
|
||||
selected = 0;
|
||||
|
|
@ -133,6 +180,13 @@ public class AdjustmentsMenu extends Container implements KeyListener {
|
|||
int code = e.getKeyCode();
|
||||
String[] items = hidden ? HIDDEN_ITEMS : MAIN_ITEMS;
|
||||
|
||||
if (playing) {
|
||||
if (code == KeyEvent.VK_ENTER || code == KeyEvent.VK_ESCAPE) {
|
||||
showMenu();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (code == KeyEvent.VK_UP) {
|
||||
selected = (selected + items.length - 1) % items.length;
|
||||
repaint();
|
||||
|
|
@ -151,10 +205,18 @@ public class AdjustmentsMenu extends Container implements KeyListener {
|
|||
}
|
||||
}
|
||||
|
||||
public void controllerUpdate(ControllerEvent event) {
|
||||
if (event instanceof EndOfMediaEvent) {
|
||||
showMenu();
|
||||
} else {
|
||||
status = "Event: " + event.getClass().getName();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
public void keyReleased(KeyEvent e) {
|
||||
}
|
||||
|
||||
public void keyTyped(KeyEvent e) {
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,19 @@
|
|||
package org.homebrew;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.event.KeyEvent;
|
||||
import javax.tv.xlet.Xlet;
|
||||
import javax.tv.xlet.XletContext;
|
||||
import javax.tv.xlet.XletStateChangeException;
|
||||
import org.dvb.event.EventManager;
|
||||
import org.dvb.event.UserEvent;
|
||||
import org.dvb.event.UserEventListener;
|
||||
import org.dvb.event.UserEventRepository;
|
||||
import org.havi.ui.HScene;
|
||||
import org.havi.ui.HSceneFactory;
|
||||
import org.havi.ui.event.HRcEvent;
|
||||
|
||||
public class MyXlet extends Thread implements Xlet {
|
||||
public class MyXlet extends Thread implements Xlet, UserEventListener {
|
||||
private HScene scene;
|
||||
private AdjustmentsMenu menu;
|
||||
private boolean running;
|
||||
|
|
@ -18,9 +24,43 @@ public class MyXlet extends Thread implements Xlet {
|
|||
|
||||
scene = HSceneFactory.getInstance().getDefaultHScene();
|
||||
scene.add(menu, BorderLayout.CENTER);
|
||||
scene.addKeyListener(menu);
|
||||
scene.validate();
|
||||
scene.repaint();
|
||||
|
||||
// Claim exclusive access to navigation and transport keys so the
|
||||
// player firmware doesn't interpret them as FF/RW/skip.
|
||||
UserEventRepository repo = new UserEventRepository("keys");
|
||||
repo.addKey(KeyEvent.VK_UP);
|
||||
repo.addKey(KeyEvent.VK_DOWN);
|
||||
repo.addKey(KeyEvent.VK_LEFT);
|
||||
repo.addKey(KeyEvent.VK_RIGHT);
|
||||
repo.addKey(KeyEvent.VK_ENTER);
|
||||
repo.addKey(KeyEvent.VK_SPACE);
|
||||
repo.addKey(KeyEvent.VK_0);
|
||||
repo.addKey(KeyEvent.VK_1);
|
||||
repo.addKey(KeyEvent.VK_2);
|
||||
repo.addKey(KeyEvent.VK_3);
|
||||
repo.addKey(KeyEvent.VK_4);
|
||||
repo.addKey(KeyEvent.VK_5);
|
||||
repo.addKey(KeyEvent.VK_6);
|
||||
repo.addKey(KeyEvent.VK_7);
|
||||
repo.addKey(KeyEvent.VK_8);
|
||||
repo.addKey(KeyEvent.VK_9);
|
||||
repo.addKey(HRcEvent.VK_COLORED_KEY_0);
|
||||
repo.addKey(HRcEvent.VK_COLORED_KEY_1);
|
||||
repo.addKey(HRcEvent.VK_COLORED_KEY_2);
|
||||
repo.addKey(HRcEvent.VK_COLORED_KEY_3);
|
||||
EventManager.getInstance().addUserEventListener(this, repo);
|
||||
}
|
||||
|
||||
public void userEventReceived(UserEvent event) {
|
||||
if (event.getType() == KeyEvent.KEY_PRESSED) {
|
||||
KeyEvent ke = new KeyEvent(
|
||||
scene, KeyEvent.KEY_PRESSED,
|
||||
event.getWhen(), event.getModifiers(),
|
||||
event.getCode(), event.getKeyChar());
|
||||
menu.keyPressed(ke);
|
||||
}
|
||||
}
|
||||
|
||||
public void startXlet() throws XletStateChangeException {
|
||||
|
|
@ -41,6 +81,7 @@ public class MyXlet extends Thread implements Xlet {
|
|||
public void destroyXlet(boolean unconditional) {
|
||||
running = false;
|
||||
interrupt();
|
||||
EventManager.getInstance().removeUserEventListener(this);
|
||||
|
||||
if (scene != null && menu != null) {
|
||||
menu.setVisible(false);
|
||||
|
|
@ -61,4 +102,3 @@ public class MyXlet extends Thread implements Xlet {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue