/*
* PS3 Media Server, for streaming any medias to your PS3.
* Copyright (C) 2008 A.Brochard
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; version 2
* of the License only.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package net.pms.encoders;
import com.sun.jna.Platform;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import net.pms.PMS;
import net.pms.configuration.PmsConfiguration;
import net.pms.dlna.DLNAResource;
import net.pms.formats.Format;
import net.pms.formats.FormatFactory;
import net.pms.io.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class handles players. Creating an instance will initialize the list of
* known players.
*
* @since 1.51.0
*/
public final class PlayerFactory {
/**
* Logger used for all logging.
*/
private static final Logger LOGGER = LoggerFactory
.getLogger(FormatFactory.class);
/**
* List of registered and approved {@link Player} objects.
*/
private static ArrayList<Player> players = new ArrayList<>();
/**
* List of registered {@link Player} objects.
*/
private static ArrayList<Player> allPlayers = new ArrayList<>();
/**
* Interface to Windows specific functions, like Windows Registry. The
* registry is set by the constructor.
*/
private static SystemUtils utils;
private static PmsConfiguration configuration = PMS.getConfiguration();
/**
* This takes care of sorting the players by the given PMS configuration.
*/
@SuppressWarnings("serial")
private static class PlayerSort implements Comparator<Player>, Serializable {
@Override
public int compare(Player player1, Player player2) {
List<String> prefs = configuration.getEnginesAsList(utils);
Integer index1 = prefs.indexOf(player1.id());
Integer index2 = prefs.indexOf(player2.id());
// Not being in the configuration settings will sort the player as last.
if (index1 == -1) {
index1 = 999;
}
if (index2 == -1) {
index2 = 999;
}
return index1.compareTo(index2);
}
}
/**
* This class is not meant to be instantiated.
*/
private PlayerFactory() {
}
@Deprecated
public static void initialize(final PmsConfiguration configuration) {
initialize();
}
/**
* Constructor that registers all players based on the given configuration,
* frame and registry.
*/
public static void initialize() {
utils = PMS.get().getRegistry();
registerPlayers();
}
/**
* Register a known set of audio or video transcoders.
*/
private static void registerPlayers() {
if (Platform.isWindows()) {
registerPlayer(new AviSynthFFmpeg());
}
registerPlayer(new FFmpegAudio());
registerPlayer(new MEncoderVideo());
if (Platform.isWindows()) {
registerPlayer(new AviSynthMEncoder());
}
registerPlayer(new FFMpegVideo());
registerPlayer(new VLCVideo());
registerPlayer(new FFmpegWebVideo());
registerPlayer(new MEncoderWebVideo());
registerPlayer(new VLCWebVideo());
registerPlayer(new TsMuxeRVideo());
registerPlayer(new TsMuxeRAudio());
registerPlayer(new VideoLanAudioStreaming());
registerPlayer(new VideoLanVideoStreaming());
if (Platform.isWindows()) {
registerPlayer(new FFmpegDVRMSRemux());
}
registerPlayer(new DCRaw());
// Sort the players according to the configuration settings
Collections.sort(allPlayers, new PlayerSort());
Collections.sort(players, new PlayerSort());
}
/**
* Adds a single {@link Player} to the list of Players. Before the player is
* added to the list, it is verified to be okay.
*
* @param player Player to be added to the list.
*/
public static synchronized void registerPlayer(final Player player) {
if (allPlayers.contains(player)) {
LOGGER.info("Transcoding engine " + player + " already exists, skipping...");
return;
}
boolean ok = false;
allPlayers.add(player);
if (Player.NATIVE.equals(player.executable())) {
ok = true;
} else {
if (Platform.isWindows()) {
if (player.executable() == null) {
LOGGER.info("Executable of transcoder profile " + player
+ " not defined");
return;
}
File executable = new File(player.executable());
File executable2 = new File(player.executable() + ".exe");
if (executable.exists() || executable2.exists()) {
ok = true;
} else {
LOGGER.info("Executable of transcoder profile " + player
+ " not found");
return;
}
if (player.avisynth()) {
ok = false;
if (utils.isAvis()) {
ok = true;
} else {
LOGGER.info("Transcoder profile " + player
+ " will not be used because AviSynth was not found");
}
}
} else if (!player.avisynth()) {
ok = true;
}
}
if (ok) {
LOGGER.info("Registering transcoding engine: " + player);
players.add(player);
}
}
/**
* Returns the list of all players. This includes the ones not verified as
* being okay.
*
* @return The list of players.
*/
public static ArrayList<Player> getAllPlayers() {
return allPlayers;
}
/**
* Returns the list of players that have been verified as okay.
*
* @return The list of players.
*/
public static ArrayList<Player> getPlayers() {
return players;
}
/**
* Returns the {@link Player} that matches the given class and format if it is
* enabled. If no {@link Player} is found or the {@link Player} isn't
* enabled, {@code null} is returned.
*
* @param profileClass
* The class to match.
* @param ext
* The format to match.
* @return The {@link Player} if found and enabled, otherwise {@code null}.
*/
public static Player getEnabledPlayer(final Class<? extends Player> profileClass, final Format ext) {
Player player = getPlayer(profileClass, ext);
if (player != null && configuration.getEnginesAsList(utils).contains(player.id())) {
return player;
} else {
return null;
}
}
/**
* @deprecated Use {@link #getPlayer(DLNAResource)} instead.
*
* Returns the player that matches the given class and format.
*
* @param profileClass
* The class to match.
* @param ext
* The format to match.
* @return The player if a match could be found, <code>null</code>
* otherwise.
*/
@Deprecated
public static Player getPlayer(final Class<? extends Player> profileClass, final Format ext) {
for (Player player : players) {
if (player.getClass().equals(profileClass)
&& player.type() == ext.getType()
&& !player.excludeFormat(ext)) {
return player;
}
}
return null;
}
/**
* Returns the first {@link Player} that matches the given mediaInfo or
* format. Each of the available players is passed the provided information
* and the first that reports it is compatible will be returned.
*
* @param resource
* The {@link DLNAResource} to match
* @return The player if a match could be found, <code>null</code>
* otherwise.
* @since 1.60.0
*/
public static Player getPlayer(final DLNAResource resource) {
if (resource == null) {
LOGGER.warn("Invalid resource (null): no player found");
return null;
} else {
LOGGER.trace("Getting player for resource \"{}\"", resource.getName());
}
boolean isImage = resource.getMedia() != null ? resource.getMedia().isImage() : false;
List<String> enabledEngines = configuration.getEnginesAsList(utils);
for (Player player : players) {
boolean enabled = enabledEngines.contains(player.id());
if (enabled && (!isImage || player instanceof ImagePlayer)) {
boolean compatible = player.isCompatible(resource);
if (compatible) {
// Player is enabled and compatible
LOGGER.trace("Returning compatible player \"{}\"", player.name());
return player;
} else {
LOGGER.trace("Player \"{}\" is incompatible", player.name());
}
} else {
if (!isImage || player instanceof ImagePlayer) {
LOGGER.trace("Player \"{}\" is disabled", player.name());
}
}
}
LOGGER.trace("No player found for {}", resource.getName());
return null;
}
/**
* @deprecated Use {@link #getPlayer(DLNAResource)} instead.
*
* Returns the players matching the given classes and type.
*
* @param profileClasses
* The classes to match.
* @param type
* The type to match.
* @return The list of players that match. If no players match, an empty
* list is returned.
*/
@Deprecated
public static ArrayList<Player> getPlayers(
final ArrayList<Class<? extends Player>> profileClasses,
final int type) {
ArrayList<Player> compatiblePlayers = new ArrayList<>();
for (Player player : players) {
if (profileClasses.contains(player.getClass())
&& player.type() == type) {
compatiblePlayers.add(player);
}
}
return compatiblePlayers;
}
/**
* Returns all {@link Player}s that match the given resource and are
* enabled. Each of the available players is passed the provided information
* and each player that reports it is compatible will be returned.
*
* @param resource
* The {@link DLNAResource} to match
* @return The list of compatible players if a match could be found,
* <code>null</code> otherwise.
* @since 1.60.0
*/
public static ArrayList<Player> getPlayers(final DLNAResource resource) {
if (resource == null) {
return null;
}
List<String> enabledEngines = configuration.getEnginesAsList(utils);
ArrayList<Player> compatiblePlayers = new ArrayList<>();
for (Player player : players) {
if (enabledEngines.contains(player.id()) && player.isCompatible(resource)) {
// Player is enabled and compatible
LOGGER.trace("Player " + player.name() + " is compatible with resource " + resource.getName());
compatiblePlayers.add(player);
}
}
return compatiblePlayers;
}
/**
* @deprecated Use {@link #getPlayers(DLNAResource)} instead.
*
* @param resource The resource to match
* @return The list of players if a match could be found, null otherwise.
*/
@Deprecated
public static ArrayList<Player> getEnabledPlayers(final DLNAResource resource) {
return getPlayers(resource);
}
}