1 /* 2 * This file is part of libbluray 3 * Copyright (C) 2010 William Hahne 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2.1 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library. If not, see 17 * <http://www.gnu.org/licenses/>. 18 */ 19 20 package org.videolan; 21 22 import java.io.File; 23 import java.io.InputStream; 24 import java.io.InvalidObjectException; 25 import java.util.Enumeration; 26 import org.videolan.Logger; 27 28 import org.bluray.net.BDLocator; 29 import org.bluray.system.RegisterAccess; 30 import org.bluray.ti.TitleImpl; 31 import org.davic.media.MediaLocator; 32 import org.dvb.application.AppID; 33 import org.dvb.application.AppsDatabase; 34 import org.dvb.application.CurrentServiceFilter; 35 36 import javax.media.Manager; 37 import javax.tv.locator.Locator; 38 39 import org.videolan.bdjo.AppEntry; 40 import org.videolan.bdjo.Bdjo; 41 import org.videolan.bdjo.GraphicsResolution; 42 import org.videolan.bdjo.PlayListTable; 43 import org.videolan.bdjo.TerminalInfo; 44 import org.videolan.media.content.PlayerManager; 45 46 public class BDJLoader { 47 48 private static class FontCacheAction extends BDJAction { FontCacheAction(InputStream is)49 public FontCacheAction(InputStream is) { 50 this.fontPath = null; 51 this.is = is; 52 } 53 FontCacheAction(String fontPath)54 public FontCacheAction(String fontPath) { 55 this.fontPath = fontPath; 56 this.is = null; 57 } 58 doAction()59 protected void doAction() { 60 try { 61 if (this.is != null) { 62 this.cacheFile = addFontImpl(is); 63 } else { 64 this.cacheFile = addFontImpl(fontPath); 65 } 66 } catch (RuntimeException e) { 67 this.exception = e; 68 } 69 } 70 execute()71 public File execute() { 72 BDJActionManager.getInstance().putCommand(this); 73 waitEnd(); 74 if (exception != null) { 75 throw exception; 76 } 77 return cacheFile; 78 } 79 80 private final String fontPath; 81 private final InputStream is; 82 private File cacheFile = null; 83 private RuntimeException exception = null; 84 } 85 86 /* called by org.dvb.ui.FontFactory */ addFont(InputStream is)87 public static File addFont(InputStream is) { 88 if (BDJXletContext.getCurrentContext() == null) 89 return addFontImpl(is); 90 /* dispatch cache request to privileged thread */ 91 return new FontCacheAction(is).execute(); 92 } 93 94 /* called by org.dvb.ui.FontFactory */ addFont(String fontFile)95 public static File addFont(String fontFile) { 96 if (BDJXletContext.getCurrentContext() == null) 97 return addFontImpl(fontFile); 98 /* dispatch cache request to privileged thread */ 99 return new FontCacheAction(fontFile).execute(); 100 } 101 addFontImpl(InputStream is)102 private static File addFontImpl(InputStream is) { 103 VFSCache localCache = vfsCache; 104 if (localCache != null) { 105 return localCache.addFont(is); 106 } 107 return null; 108 } 109 addFontImpl(String fontFile)110 private static File addFontImpl(String fontFile) { 111 VFSCache localCache = vfsCache; 112 if (localCache != null) { 113 return localCache.addFont(fontFile); 114 } 115 return null; 116 } 117 118 /* called by BDJSecurityManager */ accessFile(String file)119 protected static void accessFile(String file) { 120 VFSCache localCache = vfsCache; 121 if (localCache != null) { 122 localCache.accessFile(file); 123 } 124 } 125 getCachedFile(String path)126 public static String getCachedFile(String path) { 127 VFSCache localCache = vfsCache; 128 if (localCache != null) { 129 return localCache.map(path); 130 } 131 return path; 132 } 133 load(TitleImpl title, boolean restart, BDJLoaderCallback callback)134 public static boolean load(TitleImpl title, boolean restart, BDJLoaderCallback callback) { 135 // This method should be called only from ServiceContextFactory 136 137 if (title == null) 138 return false; 139 synchronized (BDJLoader.class) { 140 if (queue == null) 141 queue = BDJActionQueue.create("BDJLoader"); 142 } 143 queue.put(new BDJLoaderAction(title, restart, callback)); 144 return true; 145 } 146 unload(BDJLoaderCallback callback)147 public static boolean unload(BDJLoaderCallback callback) { 148 // This method should be called only from ServiceContextFactory 149 150 synchronized (BDJLoader.class) { 151 if (queue == null) 152 queue = BDJActionQueue.create("BDJLoader"); 153 } 154 queue.put(new BDJLoaderAction(null, false, callback)); 155 return true; 156 } 157 shutdown()158 protected static void shutdown() { 159 try { 160 if (queue != null) { 161 queue.shutdown(); 162 } 163 } catch (Throwable e) { 164 logger.error("shutdown() failed: " + e + "\n" + Logger.dumpStack(e)); 165 } 166 queue = null; 167 vfsCache = null; 168 } 169 loadN(TitleImpl title, boolean restart)170 private static boolean loadN(TitleImpl title, boolean restart) { 171 172 if (vfsCache == null) { 173 vfsCache = VFSCache.createInstance(); 174 } 175 176 TitleInfo ti = title.getTitleInfo(); 177 if (!ti.isBdj()) { 178 logger.info("Not BD-J title - requesting HDMV title start"); 179 unloadN(); 180 return Libbluray.selectHdmvTitle(title.getTitleNum()); 181 } 182 183 try { 184 // load bdjo 185 Bdjo bdjo = Libbluray.getBdjo(ti.getBdjoName()); 186 if (bdjo == null) 187 throw new InvalidObjectException("bdjo not loaded"); 188 AppEntry[] appTable = bdjo.getAppTable(); 189 190 // reuse appProxys 191 BDJAppProxy[] proxys = new BDJAppProxy[appTable.length]; 192 AppsDatabase db = AppsDatabase.getAppsDatabase(); 193 Enumeration ids = db.getAppIDs(new CurrentServiceFilter()); 194 while (ids.hasMoreElements()) { 195 AppID id = (AppID)ids.nextElement(); 196 BDJAppProxy proxy = (BDJAppProxy)db.getAppProxy(id); 197 AppEntry entry = (AppEntry)db.getAppAttributes(id); 198 if (proxy == null) { 199 logger.error("AppsDatabase corrupted!"); 200 continue; 201 } 202 if (entry == null) { 203 logger.error("AppsDatabase corrupted!"); 204 proxy.release(); 205 continue; 206 } 207 for (int i = 0; i < appTable.length; i++) { 208 if (id.equals(appTable[i].getIdentifier()) && 209 entry.getInitialClass().equals(appTable[i].getInitialClass())) { 210 if (restart && appTable[i].getIsServiceBound()) { 211 logger.info("Stopping xlet " + appTable[i].getInitialClass() + " (for restart)"); 212 proxy.stop(true); 213 } else { 214 logger.info("Keeping xlet " + appTable[i].getInitialClass()); 215 proxys[i] = proxy; 216 proxy = null; 217 } 218 break; 219 } 220 } 221 if (proxy != null) { 222 logger.info("Terminating xlet " + entry.getInitialClass()); 223 proxy.release(); 224 } 225 } 226 227 // start bdj window 228 GUIManager gui = GUIManager.createInstance(); 229 TerminalInfo terminfo = bdjo.getTerminalInfo(); 230 GraphicsResolution res = terminfo.getResolution(); 231 gui.setDefaultFont(terminfo.getDefaultFont()); 232 gui.setResizable(true); 233 gui.setSize(res.getWidth(), res.getHeight()); 234 gui.setVisible(true); 235 236 Libbluray.setUOMask(terminfo.getMenuCallMask(), terminfo.getTitleSearchMask()); 237 Libbluray.setKeyInterest(bdjo.getKeyInterestTable()); 238 239 // initialize AppCaches 240 if (vfsCache != null) { 241 vfsCache.add(bdjo.getAppCaches()); 242 } 243 244 try { 245 BDJLoaderAdapter a = Libbluray.getLoaderAdapter(); 246 if (a != null) 247 appTable = a.patchAppTable(appTable, title.getTitleNum()); 248 } catch (Throwable t) { 249 logger.error("" + t); 250 } 251 252 // initialize appProxys 253 for (int i = 0; i < appTable.length; i++) { 254 if (proxys[i] == null) { 255 proxys[i] = BDJAppProxy.newInstance(new BDJXletContext(appTable[i], bdjo.getAppCaches(), gui)); 256 257 /* log startup class, startup parameters and jar file */ 258 String[] params = appTable[i].getParams(); 259 String p = ""; 260 if (params != null && params.length > 0) { 261 p = "(" + StrUtil.Join(params, ",") + ")"; 262 } 263 logger.info("Loaded class: " + appTable[i].getInitialClass() + p + " from " + appTable[i].getBasePath() + ".jar"); 264 } else { 265 proxys[i].getXletContext().update(appTable[i], bdjo.getAppCaches()); 266 logger.info("Reused class: " + appTable[i].getInitialClass() + " from " + appTable[i].getBasePath() + ".jar"); 267 } 268 } 269 270 // change psr 271 Libbluray.writePSR(RegisterAccess.PSR_TITLE_NR, title.getTitleNum()); 272 273 // notify AppsDatabase 274 ((BDJAppsDatabase)BDJAppsDatabase.getAppsDatabase()).newDatabase(bdjo, proxys); 275 276 // auto start playlist 277 try { 278 PlayListTable plt = bdjo.getAccessiblePlaylists(); 279 if ((plt != null) && (plt.isAutostartFirst())) { 280 logger.info("Auto-starting playlist"); 281 String[] pl = plt.getPlayLists(); 282 if (pl.length > 0) 283 Manager.createPlayer(new MediaLocator(new BDLocator("bd://PLAYLIST:" + pl[0]))).start(); 284 } 285 } catch (Exception e) { 286 logger.error("loadN(): autoplaylist failed: " + e + "\n" + Logger.dumpStack(e)); 287 } 288 289 // now run all the xlets 290 for (int i = 0; i < appTable.length; i++) { 291 int code = appTable[i].getControlCode(); 292 if (code == AppEntry.AUTOSTART) { 293 logger.info("Autostart xlet " + i + ": " + appTable[i].getInitialClass()); 294 proxys[i].start(); 295 } else if (code == AppEntry.PRESENT) { 296 logger.info("Init xlet " + i + ": " + appTable[i].getInitialClass()); 297 proxys[i].init(); 298 } else { 299 logger.info("Unsupported xlet code (" +code+") xlet " + i + ": " + appTable[i].getInitialClass()); 300 } 301 } 302 303 logger.info("Finished initializing and starting xlets."); 304 305 return true; 306 307 } catch (Throwable e) { 308 logger.error("loadN() failed: " + e + "\n" + Logger.dumpStack(e)); 309 unloadN(); 310 return false; 311 } 312 } 313 unloadN()314 private static boolean unloadN() { 315 try { 316 try { 317 GUIManager.getInstance().setVisible(false); 318 } catch (Error e) { 319 } 320 321 AppsDatabase db = AppsDatabase.getAppsDatabase(); 322 323 /* stop xlets first */ 324 Enumeration ids = db.getAppIDs(new CurrentServiceFilter()); 325 while (ids.hasMoreElements()) { 326 AppID id = (AppID)ids.nextElement(); 327 BDJAppProxy proxy = (BDJAppProxy)db.getAppProxy(id); 328 if (proxy != null) { 329 proxy.stop(true); 330 } 331 } 332 333 ids = db.getAppIDs(new CurrentServiceFilter()); 334 while (ids.hasMoreElements()) { 335 AppID id = (AppID)ids.nextElement(); 336 BDJAppProxy proxy = (BDJAppProxy)db.getAppProxy(id); 337 if (proxy != null) { 338 proxy.release(); 339 } 340 } 341 342 ((BDJAppsDatabase)db).newDatabase(null, null); 343 344 PlayerManager.getInstance().releaseAllPlayers(true); 345 346 return true; 347 } catch (Throwable e) { 348 logger.error("unloadN() failed: " + e + "\n" + Logger.dumpStack(e)); 349 return false; 350 } 351 } 352 353 private static class BDJLoaderAction extends BDJAction { BDJLoaderAction(TitleImpl title, boolean restart, BDJLoaderCallback callback)354 public BDJLoaderAction(TitleImpl title, boolean restart, BDJLoaderCallback callback) { 355 this.title = title; 356 this.restart = restart; 357 this.callback = callback; 358 } 359 doAction()360 protected void doAction() { 361 boolean succeed; 362 if (title != null) 363 succeed = loadN(title, restart); 364 else 365 succeed = unloadN(); 366 if (callback != null) 367 callback.loaderDone(succeed); 368 } 369 370 private TitleImpl title; 371 private boolean restart; 372 private BDJLoaderCallback callback; 373 } 374 375 private static final Logger logger = Logger.getLogger(BDJLoader.class.getName()); 376 377 private static BDJActionQueue queue = null; 378 private static VFSCache vfsCache = null; 379 } 380