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 }