1 /* 2 * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.media.jfxmediaimpl; 27 28 import com.sun.glass.utils.NativeLibLoader; 29 import com.sun.media.jfxmedia.*; 30 import com.sun.media.jfxmedia.events.MediaErrorListener; 31 import com.sun.media.jfxmedia.locator.Locator; 32 import com.sun.media.jfxmedia.logging.Logger; 33 import com.sun.media.jfxmediaimpl.platform.PlatformManager; 34 import java.lang.ref.WeakReference; 35 import java.security.AccessController; 36 import java.security.PrivilegedExceptionAction; 37 import java.util.ArrayList; 38 import java.util.List; 39 import java.util.ListIterator; 40 import java.util.Map; 41 import java.util.WeakHashMap; 42 43 /** 44 * A class representing a native media engine. 45 */ 46 public class NativeMediaManager { 47 /** 48 * The NativeMediaManager singleton. 49 */ 50 // If we create NativeMediaManager here we will not be able to catch exception from constructor. 51 private static NativeMediaManager theInstance = null; 52 /** 53 * Whether the native layer has been initialized. 54 */ 55 private static boolean isNativeLayerInitialized = false; 56 /** 57 * The {@link MediaErrorListener}s. 58 */ 59 // FIXME: Change to WeakHashMap<MediaErrorListener,Boolean> as it's more efficient 60 private List<WeakReference<MediaErrorListener>> errorListeners = 61 new ArrayList<WeakReference<MediaErrorListener>>(); 62 private static NativeMediaPlayerDisposer playerDisposer = new NativeMediaPlayerDisposer(); 63 /** 64 * List of all un-disposed players. 65 */ 66 private static Map<MediaPlayer,Boolean> allMediaPlayers = 67 new WeakHashMap<MediaPlayer,Boolean>(); 68 69 // cached content types, so we don't have to poll and sort each time, this list 70 // should never change once we're initialized 71 private final List<String> supportedContentTypes = 72 new ArrayList<String>(); 73 74 /** 75 * Get the default 76 * <code>NativeMediaManager</code>. 77 * 78 * @return the singleton 79 * <code>NativeMediaManager</code> instance. 80 */ 81 public static synchronized NativeMediaManager getDefaultInstance() { 82 if (theInstance == null) { 83 theInstance = new NativeMediaManager(); 84 } 85 return theInstance; 86 } 87 88 //************************************************************************** 89 //***** Constructors 90 //************************************************************************** 91 /** 92 * Create a <code>NativeMediaManager</code>. 93 */ 94 protected NativeMediaManager() {} 95 96 /** 97 * Initialize the native layer if it has not been so already. 98 */ 99 synchronized static void initNativeLayer() { 100 if (!isNativeLayerInitialized) { 101 // preload platforms 102 PlatformManager.getManager().preloadPlatforms(); 103 104 // Load native libraries. 105 try { 106 AccessController.doPrivileged((PrivilegedExceptionAction) () -> { 107 if (HostUtils.isWindows() || HostUtils.isMacOSX()) { 108 NativeLibLoader.loadLibrary("glib-lite"); 109 } 110 111 if (!HostUtils.isLinux() && !HostUtils.isIOS()) { 112 NativeLibLoader.loadLibrary("gstreamer-lite"); 113 } 114 115 NativeLibLoader.loadLibrary("jfxmedia"); 116 return null; 117 }); 118 } catch (Exception e) { 119 MediaUtils.error(null, MediaError.ERROR_MANAGER_ENGINEINIT_FAIL.code(), 120 "Unable to load one or more dependent libraries.", e); 121 return; // abort 122 } 123 124 // Get the Logger native side rolling before we load platforms 125 if (!Logger.initNative()) { 126 MediaUtils.error(null, MediaError.ERROR_MANAGER_LOGGER_INIT.code(), 127 "Unable to init logger", null); 128 return; // abort 129 } 130 131 // load platforms 132 PlatformManager.getManager().loadPlatforms(); 133 134 // Set the native initialization flag, even if initialization failed. 135 isNativeLayerInitialized = true; 136 } 137 } 138 139 //************************************************************************** 140 //***** Public control functions 141 //************************************************************************** 142 143 private synchronized void loadContentTypes() { 144 if (!supportedContentTypes.isEmpty()) { 145 // already populated, just return 146 return; 147 } 148 149 List<String> npt = PlatformManager.getManager().getSupportedContentTypes(); 150 if (null != npt && !npt.isEmpty()) { 151 supportedContentTypes.addAll(npt); 152 } 153 154 if (Logger.canLog(Logger.DEBUG)) { 155 StringBuilder sb = new StringBuilder("JFXMedia supported content types:\n"); 156 for (String type : supportedContentTypes) { 157 sb.append(" "); 158 sb.append(type); 159 sb.append("\n"); 160 } 161 Logger.logMsg(Logger.DEBUG, sb.toString()); 162 } 163 } 164 165 /** 166 * Whether a media source having the indicated content type may be played. 167 * 168 * @see MediaManager#canPlayContentType(java.lang.String) 169 * 170 * @throws IllegalArgumentException if 171 * <code>contentType</code> is 172 * <code>null</code>. 173 */ 174 public boolean canPlayContentType(String contentType) { 175 if (contentType == null) { 176 throw new IllegalArgumentException("contentType == null!"); 177 } 178 179 if (supportedContentTypes.isEmpty()) { 180 loadContentTypes(); 181 } 182 183 /* 184 * Don't just use supportedContentType.contains(contentType) as that 185 * is case sensitive, which we do not want 186 */ 187 for (String type : supportedContentTypes) { 188 if (contentType.equalsIgnoreCase(type)) { 189 return true; 190 } 191 } 192 193 return false; 194 } 195 196 /** 197 * Returns a copy of the array of supported content types. 198 * 199 * @return {@link String} array of supported content types. 200 */ 201 public String[] getSupportedContentTypes() { 202 if (supportedContentTypes.isEmpty()) { 203 loadContentTypes(); 204 } 205 206 return supportedContentTypes.toArray(new String[1]); 207 } 208 209 public static MetadataParser getMetadataParser(Locator locator) { 210 return PlatformManager.getManager().createMetadataParser(locator); 211 } 212 213 /** 214 * @see MediaManager#getPlayer(com.sun.media.jfxmedia.locator.Locator, int) 215 */ 216 public MediaPlayer getPlayer(Locator locator) { 217 // FIXME: remove this 218 initNativeLayer(); 219 220 MediaPlayer player = PlatformManager.getManager().createMediaPlayer(locator); 221 if (null == player) { 222 throw new MediaException("Could not create player!"); 223 } 224 225 // Cache a reference to the player. 226 allMediaPlayers.put(player, Boolean.TRUE); 227 228 return player; 229 } 230 231 /** 232 * Get a player for the media locator. A preference may be set as to whether 233 * to allow a full scan of the media. 234 * 235 * FIXME: Nuke permitFullScan, it is unused and has no effect 236 * 237 * @param locator 238 * @param permitFullScan 239 * @return MediaPlayer object 240 */ 241 public Media getMedia(Locator locator) { 242 initNativeLayer(); 243 return PlatformManager.getManager().createMedia(locator); 244 } 245 246 /** 247 * @see 248 * MediaManager#addMediaErrorListener(com.sun.media.jfxmedia.events.MediaErrorListener) 249 */ 250 public void addMediaErrorListener(MediaErrorListener listener) { 251 if (listener != null) { 252 // Since we have only one instance of NativeMediaManager, all media players 253 // created during application lifecycle will keep weak references to error 254 // listeners in errorListeners. Lets clean up unused references. 255 // FIXME: change to WeakHashMap<MEL,Boolean> as it's more efficient 256 for (ListIterator<WeakReference<MediaErrorListener>> it = errorListeners.listIterator(); it.hasNext();) { 257 MediaErrorListener l = it.next().get(); 258 if (l == null) { 259 it.remove(); 260 } 261 } 262 263 this.errorListeners.add(new WeakReference<MediaErrorListener>(listener)); 264 } 265 } 266 267 /** 268 * @see 269 * MediaManager#removeMediaErrorListener(com.sun.media.jfxmedia.events.MediaErrorListener) 270 */ 271 public void removeMediaErrorListener(MediaErrorListener listener) { 272 if (listener != null) { 273 // FIXME: change to WeakHashMap<MEL,Boolean> as it's more efficient 274 for (ListIterator<WeakReference<MediaErrorListener>> it = errorListeners.listIterator(); it.hasNext();) { 275 MediaErrorListener l = it.next().get(); 276 if (l == null || l == listener) { 277 it.remove(); 278 } 279 } 280 } 281 } 282 283 /** 284 * This function will register MediaPlayer for disposing when obj parameter 285 * does not have any strong reference. 286 * 287 * FIXME: Nuke this and use MediaDisposer instead 288 * 289 * @param obj - Object to watch for strong references 290 * @param player - MediaPlayer to dispose 291 */ 292 public static void registerMediaPlayerForDispose(Object obj, MediaPlayer player) { 293 MediaDisposer.addResourceDisposer(obj, player, playerDisposer); 294 } 295 296 /** 297 * Retrieve all un-disposed {@link MediaPlayer}s. 298 * 299 * @return a {@link List} of all un-disposed players or 300 * <code>null</code>. 301 */ 302 public List<MediaPlayer> getAllMediaPlayers() { 303 List<MediaPlayer> allPlayers = null; 304 305 if (!allMediaPlayers.isEmpty()) { 306 allPlayers = new ArrayList<MediaPlayer>(allMediaPlayers.keySet()); 307 } 308 309 return allPlayers; 310 } 311 312 //************************************************************************** 313 //***** Private functions 314 //************************************************************************** 315 List<WeakReference<MediaErrorListener>> getMediaErrorListeners() { 316 return this.errorListeners; 317 } 318 319 private static class NativeMediaPlayerDisposer implements MediaDisposer.ResourceDisposer { 320 321 public void disposeResource(Object resource) { 322 // resource is a MediaPlayer 323 MediaPlayer player = (MediaPlayer) resource; 324 if (player != null) { 325 player.dispose(); 326 } 327 } 328 } 329 }