/*
* Copyright (c) 2010, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.jfxmediaimpl;
import com.sun.glass.utils.NativeLibLoader;
import com.sun.media.jfxmedia.*;
import com.sun.media.jfxmedia.events.MediaErrorListener;
import com.sun.media.jfxmedia.locator.Locator;
import com.sun.media.jfxmedia.logging.Logger;
import com.sun.media.jfxmediaimpl.platform.PlatformManager;
import java.lang.ref.WeakReference;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.WeakHashMap;
A class representing a native media engine.
/**
* A class representing a native media engine.
*/
public class NativeMediaManager {
Whether the native layer has been initialized.
/**
* Whether the native layer has been initialized.
*/
private static boolean isNativeLayerInitialized = false;
The MediaErrorListener
s. /**
* The {@link MediaErrorListener}s.
*/
// FIXME: Change to WeakHashMap<MediaErrorListener,Boolean> as it's more efficient
private final List<WeakReference<MediaErrorListener>> errorListeners =
new ArrayList();
private final static NativeMediaPlayerDisposer playerDisposer =
new NativeMediaPlayerDisposer();
List of all un-disposed players.
/**
* List of all un-disposed players.
*/
private final static Map<MediaPlayer,Boolean> allMediaPlayers =
new WeakHashMap();
// cached content types, so we don't have to poll and sort each time, this list
// should never change once we're initialized
private final List<String> supportedContentTypes =
new ArrayList();
private final List<String> supportedProtocols =
new ArrayList<>();
The NativeMediaManager singleton.
/**
* The NativeMediaManager singleton.
*/
private static class NativeMediaManagerInitializer {
private static final NativeMediaManager globalInstance
= new NativeMediaManager();
}
Get the default
NativeMediaManager
.
Returns: the singleton
NativeMediaManager
instance.
/**
* Get the default
* <code>NativeMediaManager</code>.
*
* @return the singleton
* <code>NativeMediaManager</code> instance.
*/
public static NativeMediaManager getDefaultInstance() {
return NativeMediaManagerInitializer.globalInstance;
}
//**************************************************************************
//***** Constructors
//**************************************************************************
Create a NativeMediaManager
.
/**
* Create a <code>NativeMediaManager</code>.
*/
protected NativeMediaManager() {
/*
* Load native libraries. This must be done early as platforms may need
* to attempt loading their own native libs that are dependent on these
* This is a slight performance hit, but necessary otherwise we could
* erroneously report content types for platforms that cannot be loaded
*/
try {
AccessController.doPrivileged((PrivilegedExceptionAction) () -> {
ArrayList<String> dependencies = new ArrayList<>();
if (HostUtils.isWindows() || HostUtils.isMacOSX()) {
NativeLibLoader.loadLibrary("glib-lite");
}
if (!HostUtils.isLinux() && !HostUtils.isIOS()) {
NativeLibLoader.loadLibrary("gstreamer-lite");
} else {
dependencies.add("gstreamer-lite");
}
if (HostUtils.isLinux()) {
dependencies.add("fxplugins");
dependencies.add("avplugin");
dependencies.add("avplugin-54");
dependencies.add("avplugin-56");
dependencies.add("avplugin-57");
dependencies.add("avplugin-ffmpeg-56");
dependencies.add("avplugin-ffmpeg-57");
dependencies.add("avplugin-ffmpeg-58");
}
if (HostUtils.isMacOSX()) {
dependencies.add("fxplugins");
dependencies.add("glib-lite");
dependencies.add("jfxmedia_avf");
}
if (HostUtils.isWindows()) {
dependencies.add("fxplugins");
dependencies.add("glib-lite");
}
NativeLibLoader.loadLibrary("jfxmedia", dependencies);
return null;
});
} catch (PrivilegedActionException pae) {
MediaUtils.error(null, MediaError.ERROR_MANAGER_ENGINEINIT_FAIL.code(),
"Unable to load one or more dependent libraries.", pae);
}
// Get the Logger native side rolling before we load platforms
if (!Logger.initNative()) {
MediaUtils.error(null, MediaError.ERROR_MANAGER_LOGGER_INIT.code(),
"Unable to init logger", null);
}
}
Initialize the native layer if it has not been so already.
/**
* Initialize the native layer if it has not been so already.
*/
synchronized static void initNativeLayer() {
if (!isNativeLayerInitialized) {
// load platforms
PlatformManager.getManager().loadPlatforms();
// Set the native initialization flag, even if initialization failed.
isNativeLayerInitialized = true;
}
}
//**************************************************************************
//***** Public control functions
//**************************************************************************
private synchronized void loadContentTypes() {
if (!supportedContentTypes.isEmpty()) {
// already populated, just return
return;
}
List<String> npt = PlatformManager.getManager().getSupportedContentTypes();
if (null != npt && !npt.isEmpty()) {
supportedContentTypes.addAll(npt);
}
if (Logger.canLog(Logger.DEBUG)) {
StringBuilder sb = new StringBuilder("JFXMedia supported content types:\n");
for (String type : supportedContentTypes) {
sb.append(" ");
sb.append(type);
sb.append("\n");
}
Logger.logMsg(Logger.DEBUG, sb.toString());
}
}
private synchronized void loadProtocols() {
if (!supportedProtocols.isEmpty()) {
// already populated, just return
return;
}
List<String> npt = PlatformManager.getManager().getSupportedProtocols();
if (null != npt && !npt.isEmpty()) {
supportedProtocols.addAll(npt);
}
if (Logger.canLog(Logger.DEBUG)) {
StringBuilder sb = new StringBuilder("JFXMedia supported protocols:\n");
for (String type : supportedProtocols) {
sb.append(" ");
sb.append(type);
sb.append("\n");
}
Logger.logMsg(Logger.DEBUG, sb.toString());
}
}
Whether a media source having the indicated content type may be played.
Throws: - IllegalArgumentException – if
contentType
is
null
.
See Also: - canPlayContentType.canPlayContentType(String)
/**
* Whether a media source having the indicated content type may be played.
*
* @see MediaManager#canPlayContentType(java.lang.String)
*
* @throws IllegalArgumentException if
* <code>contentType</code> is
* <code>null</code>.
*/
public boolean canPlayContentType(String contentType) {
if (contentType == null) {
throw new IllegalArgumentException("contentType == null!");
}
if (supportedContentTypes.isEmpty()) {
loadContentTypes();
}
/*
* Don't just use supportedContentType.contains(contentType) as that
* is case sensitive, which we do not want
*/
for (String type : supportedContentTypes) {
if (contentType.equalsIgnoreCase(type)) {
return true;
}
}
return false;
}
public String[] getSupportedContentTypes() {
if (supportedContentTypes.isEmpty()) {
loadContentTypes();
}
return supportedContentTypes.toArray(new String[1]);
}
Whether a media source having the indicated protocol may be played.
Throws: - IllegalArgumentException – if
protocol
is
null
.
See Also: - canPlayProtocol.canPlayProtocol(String)
/**
* Whether a media source having the indicated protocol may be played.
*
* @see MediaManager#canPlayProtocol(java.lang.String)
*
* @throws IllegalArgumentException if
* <code>protocol</code> is
* <code>null</code>.
*/
public boolean canPlayProtocol(String protocol) {
if (protocol == null) {
throw new IllegalArgumentException("protocol == null!");
}
if (supportedProtocols.isEmpty()) {
loadProtocols();
}
/*
* Don't just use supportedProtocols.contains(protocol) as that
* is case sensitive, which we do not want
*/
for (String type : supportedProtocols) {
if (protocol.equalsIgnoreCase(type)) {
return true;
}
}
return false;
}
public static MetadataParser getMetadataParser(Locator locator) {
return PlatformManager.getManager().createMetadataParser(locator);
}
See Also: - getPlayer.getPlayer(Locator, int)
/**
* @see MediaManager#getPlayer(com.sun.media.jfxmedia.locator.Locator, int)
*/
public MediaPlayer getPlayer(Locator locator) {
// FIXME: remove this
initNativeLayer();
MediaPlayer player = PlatformManager.getManager().createMediaPlayer(locator);
if (null == player) {
throw new MediaException("Could not create player!");
}
// Cache a reference to the player.
allMediaPlayers.put(player, Boolean.TRUE);
return player;
}
Get a player for the media locator. A preference may be set as to whether
to allow a full scan of the media.
FIXME: Nuke permitFullScan, it is unused and has no effect
Params: - locator –
- permitFullScan –
Returns: MediaPlayer object
/**
* Get a player for the media locator. A preference may be set as to whether
* to allow a full scan of the media.
*
* FIXME: Nuke permitFullScan, it is unused and has no effect
*
* @param locator
* @param permitFullScan
* @return MediaPlayer object
*/
public Media getMedia(Locator locator) {
initNativeLayer();
return PlatformManager.getManager().createMedia(locator);
}
See Also: - addMediaErrorListener.addMediaErrorListener(MediaErrorListener)
/**
* @see
* MediaManager#addMediaErrorListener(com.sun.media.jfxmedia.events.MediaErrorListener)
*/
public void addMediaErrorListener(MediaErrorListener listener) {
if (listener != null) {
// Since we have only one instance of NativeMediaManager, all media players
// created during application lifecycle will keep weak references to error
// listeners in errorListeners. Lets clean up unused references.
// FIXME: change to WeakHashMap<MEL,Boolean> as it's more efficient
for (ListIterator<WeakReference<MediaErrorListener>> it = errorListeners.listIterator(); it.hasNext();) {
MediaErrorListener l = it.next().get();
if (l == null) {
it.remove();
}
}
this.errorListeners.add(new WeakReference<MediaErrorListener>(listener));
}
}
See Also: - removeMediaErrorListener.removeMediaErrorListener(MediaErrorListener)
/**
* @see
* MediaManager#removeMediaErrorListener(com.sun.media.jfxmedia.events.MediaErrorListener)
*/
public void removeMediaErrorListener(MediaErrorListener listener) {
if (listener != null) {
// FIXME: change to WeakHashMap<MEL,Boolean> as it's more efficient
for (ListIterator<WeakReference<MediaErrorListener>> it = errorListeners.listIterator(); it.hasNext();) {
MediaErrorListener l = it.next().get();
if (l == null || l == listener) {
it.remove();
}
}
}
}
This function will register MediaPlayer for disposing when obj parameter
does not have any strong reference.
FIXME: Nuke this and use MediaDisposer instead
Params: - obj – - Object to watch for strong references
- player – - MediaPlayer to dispose
/**
* This function will register MediaPlayer for disposing when obj parameter
* does not have any strong reference.
*
* FIXME: Nuke this and use MediaDisposer instead
*
* @param obj - Object to watch for strong references
* @param player - MediaPlayer to dispose
*/
public static void registerMediaPlayerForDispose(Object obj, MediaPlayer player) {
MediaDisposer.addResourceDisposer(obj, player, playerDisposer);
}
Retrieve all un-disposed MediaPlayer
s. Returns: a List
of all un-disposed players or null
.
/**
* Retrieve all un-disposed {@link MediaPlayer}s.
*
* @return a {@link List} of all un-disposed players or
* <code>null</code>.
*/
public List<MediaPlayer> getAllMediaPlayers() {
List<MediaPlayer> allPlayers = null;
if (!allMediaPlayers.isEmpty()) {
allPlayers = new ArrayList<MediaPlayer>(allMediaPlayers.keySet());
}
return allPlayers;
}
//**************************************************************************
//***** Private functions
//**************************************************************************
List<WeakReference<MediaErrorListener>> getMediaErrorListeners() {
return this.errorListeners;
}
private static class NativeMediaPlayerDisposer implements MediaDisposer.ResourceDisposer {
public void disposeResource(Object resource) {
// resource is a MediaPlayer
MediaPlayer player = (MediaPlayer) resource;
if (player != null) {
player.dispose();
}
}
}
}