Advertisement
Guest User

RendererConfiguration

a guest
Apr 1st, 2013
156
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 35.18 KB | None | 0 0
  1. package net.pms.configuration;
  2.  
  3. import com.sun.jna.Platform;
  4.  
  5. import net.pms.Messages;
  6. import net.pms.PMS;
  7. import net.pms.dlna.DLNAMediaInfo;
  8. import net.pms.dlna.LibMediaInfoParser;
  9. import net.pms.dlna.RootFolder;
  10. import net.pms.formats.Format;
  11. import net.pms.network.HTTPResource;
  12. import net.pms.network.SpeedStats;
  13. import net.pms.util.PropertiesUtil;
  14.  
  15. import org.apache.commons.configuration.ConfigurationException;
  16. import org.apache.commons.configuration.ConversionException;
  17. import org.apache.commons.configuration.PropertiesConfiguration;
  18. import org.apache.commons.lang.StringUtils;
  19.  
  20. import org.slf4j.Logger;
  21. import org.slf4j.LoggerFactory;
  22.  
  23. import java.io.File;
  24. import java.net.InetAddress;
  25. import java.util.ArrayList;
  26. import java.util.HashMap;
  27. import java.util.Map;
  28. import java.util.StringTokenizer;
  29. import java.util.regex.Pattern;
  30.  
  31. public class RendererConfiguration {
  32. private static final Logger LOGGER = LoggerFactory.getLogger(RendererConfiguration.class);
  33. private static ArrayList<RendererConfiguration> rendererConfs;
  34. private static PmsConfiguration pmsConfiguration;
  35. private static RendererConfiguration defaultConf;
  36. private static Map<InetAddress, RendererConfiguration> addressAssociation = new HashMap<InetAddress, RendererConfiguration>();
  37.  
  38. private RootFolder rootFolder;
  39. private final PropertiesConfiguration configuration;
  40. private FormatConfiguration formatConfiguration;
  41. private int rank;
  42. private final Map<String, String> mimes;
  43. private final Map<String, String> DLNAPN;
  44.  
  45. // property values
  46. private static final String DEPRECATED_MPEGPSAC3 = "MPEGAC3"; // XXX deprecated: old name with missing container
  47. private static final String EXCLUSIVE = "exclusive";
  48. private static final String LPCM = "LPCM";
  49. private static final String MP3 = "MP3";
  50. private static final String MPEGPSAC3 = "MPEGPSAC3";
  51. private static final String MPEGTSAC3 = "MPEGTSAC3";
  52. private static final String WAV = "WAV";
  53. private static final String WMV = "WMV";
  54.  
  55. // property names
  56. private static final String AUDIO = "Audio";
  57. private static final String AUTO_EXIF_ROTATE = "AutoExifRotate";
  58. private static final String BYTE_TO_TIMESEEK_REWIND_SECONDS = "ByteToTimeseekRewindSeconds"; // Ditlew
  59. private static final String CBR_VIDEO_BITRATE = "CBRVideoBitrate"; // Ditlew
  60. private static final String CHUNKED_TRANSFER = "ChunkedTransfer";
  61. private static final String CUSTOM_MENCODER_OPTIONS = "CustomMencoderOptions";
  62. private static final String CUSTOM_MENCODER_QUALITY_SETTINGS = "CustomMencoderQualitySettings";
  63. private static final String DEFAULT_VBV_BUFSIZE = "DefaultVBVBufSize";
  64. private static final String DLNA_LOCALIZATION_REQUIRED = "DLNALocalizationRequired";
  65. private static final String DLNA_ORGPN_USE = "DLNAOrgPN";
  66. private static final String DLNA_PN_CHANGES = "DLNAProfileChanges";
  67. private static final String DLNA_TREE_HACK = "CreateDLNATreeFaster";
  68. private static final String FORCE_JPG_THUMBNAILS = "ForceJPGThumbnails"; // Sony devices require JPG thumbnails
  69. private static final String H264_L41_LIMITED = "H264Level41Limited";
  70. private static final String IMAGE = "Image";
  71. private static final String LONG_FILE_NAME_FORMAT = "LongFileNameFormat";
  72. private static final String MAX_VIDEO_BITRATE = "MaxVideoBitrateMbps";
  73. private static final String MAX_VIDEO_HEIGHT = "MaxVideoHeight";
  74. private static final String MAX_VIDEO_WIDTH = "MaxVideoWidth";
  75. private static final String MEDIAPARSERV2 = "MediaInfo";
  76. private static final String MEDIAPARSERV2_THUMB = "MediaParserV2_ThumbnailGeneration";
  77. private static final String MIME_TYPES_CHANGES = "MimeTypesChanges";
  78. private static final String MUX_DTS_TO_MPEG = "MuxDTSToMpeg";
  79. private static final String MUX_H264_WITH_MPEGTS = "MuxH264ToMpegTS";
  80. private static final String MUX_LPCM_TO_MPEG = "MuxLPCMToMpeg";
  81. private static final String RENDERER_ICON = "RendererIcon";
  82. private static final String RENDERER_NAME = "RendererName";
  83. private static final String SEEK_BY_TIME = "SeekByTime";
  84. private static final String SHORT_FILE_NAME_FORMAT = "ShortFileNameFormat";
  85. private static final String SHOW_AUDIO_METADATA = "ShowAudioMetadata";
  86. private static final String SHOW_DVD_TITLE_DURATION = "ShowDVDTitleDuration"; // Ditlew
  87. private static final String SHOW_SUB_METADATA = "ShowSubMetadata";
  88. private static final String STREAM_EXT = "StreamExtensions";
  89. private static final String SUBTITLE_HTTP_HEADER = "SubtitleHttpHeader";
  90. private static final String SUPPORTED = "Supported";
  91. private static final String TRANSCODE_AUDIO_441KHZ = "TranscodeAudioTo441kHz";
  92. private static final String TRANSCODE_AUDIO = "TranscodeAudio";
  93. private static final String TRANSCODED_SIZE = "TranscodedVideoFileSize";
  94. private static final String TRANSCODE_EXT = "TranscodeExtensions";
  95. private static final String TRANSCODE_FAST_START = "TranscodeFastStart";
  96. private static final String TRANSCODE_VIDEO = "TranscodeVideo";
  97. private static final String USER_AGENT_ADDITIONAL_HEADER = "UserAgentAdditionalHeader";
  98. private static final String USER_AGENT_ADDITIONAL_SEARCH = "UserAgentAdditionalHeaderSearch";
  99. private static final String USER_AGENT = "UserAgentSearch";
  100. private static final String USE_SAME_EXTENSION = "UseSameExtension";
  101. private static final String VIDEO = "Video";
  102. private static final String WRAP_DTS_INTO_PCM = "WrapDTSIntoPCM";
  103.  
  104. public static RendererConfiguration getDefaultConf() {
  105. return defaultConf;
  106. }
  107.  
  108. /**
  109. * Load all renderer configuration files and set up the default renderer.
  110. *
  111. * @param pmsConf
  112. */
  113. public static void loadRendererConfigurations(PmsConfiguration pmsConf) {
  114. LOGGER.info("now we're inside loadRendererConfigurations method. setting pmsConfiguration");
  115. LOGGER.info("pmsConfiguration = pmsConf;");
  116. pmsConfiguration = pmsConf;
  117.  
  118. LOGGER.info("rendererConfs = new ArrayList<RendererConfiguration>();");
  119. rendererConfs = new ArrayList<RendererConfiguration>();
  120.  
  121. try {
  122. LOGGER.info("defaultConf = new RendererConfiguration();");
  123. defaultConf = new RendererConfiguration();
  124. } catch (ConfigurationException e) {
  125. LOGGER.debug("Caught exception", e);
  126. }
  127.  
  128. LOGGER.info("File renderersDir = getRenderersDir();");
  129. File renderersDir = getRenderersDir();
  130.  
  131. LOGGER.info("if (renderersDir != null) {");
  132. if (renderersDir != null) {
  133.  
  134. LOGGER.info("Loading renderer configurations from " + renderersDir.getAbsolutePath());
  135.  
  136. LOGGER.info("File[] confs = renderersDir.listFiles();");
  137. File[] confs = renderersDir.listFiles();
  138. int rank = 1;
  139. for (File f : confs) {
  140. if (f.getName().endsWith(".conf")) {
  141. try {
  142. LOGGER.info("Loading configuration file: " + f.getName());
  143. RendererConfiguration r = new RendererConfiguration(f);
  144. r.rank = rank++;
  145. rendererConfs.add(r);
  146. } catch (ConfigurationException ce) {
  147. LOGGER.info("Error in loading configuration of: " + f.getAbsolutePath());
  148. }
  149.  
  150. }
  151. }
  152. }
  153.  
  154. if (rendererConfs.size() > 0) {
  155. // See if a different default configuration was configured
  156. String rendererFallback = pmsConfiguration.getRendererDefault();
  157.  
  158. if (StringUtils.isNotBlank(rendererFallback)) {
  159. RendererConfiguration fallbackConf = getRendererConfigurationByName(rendererFallback);
  160.  
  161. if (fallbackConf != null) {
  162. // A valid fallback configuration was set, use it as default.
  163. defaultConf = fallbackConf;
  164. }
  165. }
  166. }
  167. }
  168.  
  169. /**
  170. * Returns the list of all renderer configurations.
  171. *
  172. * @return The list of all configurations.
  173. */
  174. public static ArrayList<RendererConfiguration> getAllRendererConfigurations() {
  175. return rendererConfs;
  176. }
  177.  
  178. protected static File getRenderersDir() {
  179. final String[] pathList = PropertiesUtil.getProjectProperties().get("project.renderers.dir").split(",");
  180.  
  181. for (String path : pathList) {
  182. if (path.trim().length() > 0) {
  183. File file = new File(path.trim());
  184.  
  185. if (file.isDirectory()) {
  186. if (file.canRead()) {
  187. return file;
  188. } else {
  189. LOGGER.warn("Can't read directory: {}", file.getAbsolutePath());
  190. }
  191. }
  192. }
  193. }
  194.  
  195. return null;
  196. }
  197.  
  198. public static void resetAllRenderers() {
  199. for (RendererConfiguration rc : rendererConfs) {
  200. rc.rootFolder = null;
  201. }
  202. }
  203.  
  204. public RootFolder getRootFolder() {
  205. if (rootFolder == null) {
  206. rootFolder = new RootFolder();
  207. rootFolder.discoverChildren();
  208. }
  209.  
  210. return rootFolder;
  211. }
  212.  
  213. /**
  214. * Associate an IP address with this renderer. The association will
  215. * persist between requests, allowing the renderer to be recognized
  216. * by its address in later requests.
  217. * @param sa The IP address to associate.
  218. * @see #getRendererConfigurationBySocketAddress(InetAddress)
  219. */
  220. public void associateIP(InetAddress sa) {
  221. addressAssociation.put(sa, this);
  222. SpeedStats.getInstance().getSpeedInMBits(sa, getRendererName());
  223. }
  224.  
  225. public static RendererConfiguration getRendererConfigurationBySocketAddress(InetAddress sa) {
  226. return addressAssociation.get(sa);
  227. }
  228.  
  229. /**
  230. * Tries to find a matching renderer configuration based on a request
  231. * header line with a User-Agent header. These matches are made using
  232. * the "UserAgentSearch" configuration option in a renderer.conf.
  233. * Returns the matched configuration or <code>null</code> if no match
  234. * could be found.
  235. *
  236. * @param userAgentString The request header line.
  237. * @return The matching renderer configuration or <code>null</code>.
  238. */
  239. public static RendererConfiguration getRendererConfigurationByUA(String userAgentString) {
  240. if (pmsConfiguration.isRendererForceDefault()) {
  241. // Force default renderer
  242. LOGGER.trace("Forcing renderer match to \"" + defaultConf.getRendererName() + "\"");
  243. return manageRendererMatch(defaultConf);
  244. } else {
  245. // Try to find a match
  246. for (RendererConfiguration r : rendererConfs) {
  247. if (r.matchUserAgent(userAgentString)) {
  248. return manageRendererMatch(r);
  249. }
  250. }
  251. }
  252.  
  253. return null;
  254. }
  255.  
  256. private static RendererConfiguration manageRendererMatch(RendererConfiguration r) {
  257. if (addressAssociation.values().contains(r)) {
  258. // FIXME: This cannot ever ever happen because of how renderer matching
  259. // is implemented in RequestHandler and RequestHandlerV2. The first header
  260. // match will associate the IP address with the renderer and from then on
  261. // all other requests from the same IP address will be recognized based on
  262. // that association. Headers will be ignored and unfortunately they happen
  263. // to be the only way to get here.
  264. LOGGER.info("Another renderer like " + r.getRendererName() + " was found!");
  265. }
  266.  
  267. return r;
  268. }
  269.  
  270. /**
  271. * Tries to find a matching renderer configuration based on a request
  272. * header line with an additional, non-User-Agent header. These matches
  273. * are made based on the "UserAgentAdditionalHeader" and
  274. * "UserAgentAdditionalHeaderSearch" configuration options in a
  275. * renderer.conf. Returns the matched configuration or <code>null</code>
  276. * if no match could be found.
  277. *
  278. * @param header The request header line.
  279. * @return The matching renderer configuration or <code>null</code>.
  280. */
  281. public static RendererConfiguration getRendererConfigurationByUAAHH(String header) {
  282. if (pmsConfiguration.isRendererForceDefault()) {
  283. // Force default renderer
  284. LOGGER.trace("Forcing renderer match to \"" + defaultConf.getRendererName() + "\"");
  285. return manageRendererMatch(defaultConf);
  286. } else {
  287. // Try to find a match
  288. for (RendererConfiguration r : rendererConfs) {
  289. if (StringUtils.isNotBlank(r.getUserAgentAdditionalHttpHeader()) && header.startsWith(r.getUserAgentAdditionalHttpHeader())) {
  290. String value = header.substring(header.indexOf(":", r.getUserAgentAdditionalHttpHeader().length()) + 1);
  291. if (r.matchAdditionalUserAgent(value)) {
  292. return manageRendererMatch(r);
  293. }
  294. }
  295. }
  296. }
  297.  
  298. return null;
  299. }
  300.  
  301. /**
  302. * Tries to find a matching renderer configuration based on the name of
  303. * the renderer. Returns true if the provided name is equal to or a
  304. * substring of the renderer name defined in a configuration, where case
  305. * does not matter.
  306. *
  307. * @param name The renderer name to match.
  308. * @return The matching renderer configuration or <code>null</code>
  309. *
  310. * @since 1.50.1
  311. */
  312. public static RendererConfiguration getRendererConfigurationByName(String name) {
  313. for (RendererConfiguration conf : rendererConfs) {
  314. if (conf.getRendererName().toLowerCase().contains(name.toLowerCase())) {
  315. return conf;
  316. }
  317. }
  318.  
  319. return null;
  320. }
  321.  
  322. public FormatConfiguration getFormatConfiguration() {
  323. return formatConfiguration;
  324. }
  325.  
  326. public int getRank() {
  327. return rank;
  328. }
  329.  
  330. // FIXME These 'is' methods should disappear. Use feature detection instead.
  331. @Deprecated
  332. public boolean isXBOX() {
  333. return getRendererName().toUpperCase().contains("XBOX");
  334. }
  335.  
  336. @Deprecated
  337. public boolean isXBMC() {
  338. return getRendererName().toUpperCase().contains("XBMC");
  339. }
  340.  
  341. public boolean isPS3() {
  342. return getRendererName().toUpperCase().contains("PLAYSTATION") || getRendererName().toUpperCase().contains("PS3");
  343. }
  344.  
  345. public boolean isBRAVIA() {
  346. return getRendererName().toUpperCase().contains("BRAVIA");
  347. }
  348.  
  349. @Deprecated
  350. public boolean isFDSSDP() {
  351. return getRendererName().toUpperCase().contains("FDSSDP");
  352. }
  353.  
  354. // Ditlew
  355. public int getByteToTimeseekRewindSeconds() {
  356. return getInt(BYTE_TO_TIMESEEK_REWIND_SECONDS, 0);
  357. }
  358.  
  359. // Ditlew
  360. public int getCBRVideoBitrate() {
  361. return getInt(CBR_VIDEO_BITRATE, 0);
  362. }
  363.  
  364. // Ditlew
  365. public boolean isShowDVDTitleDuration() {
  366. return getBoolean(SHOW_DVD_TITLE_DURATION, false);
  367. }
  368.  
  369. private RendererConfiguration() throws ConfigurationException {
  370. this(null);
  371. }
  372.  
  373. public RendererConfiguration(File f) throws ConfigurationException {
  374. LOGGER.info("Inside constructor: public RendererConfiguration(File f) throws ConfigurationException {");
  375.  
  376. LOGGER.info("configuration = new PropertiesConfiguration();");
  377. configuration = new PropertiesConfiguration();
  378.  
  379. LOGGER.info("configuration.setListDelimiter((char) 0);");
  380. configuration.setListDelimiter((char) 0);
  381.  
  382. LOGGER.info("checking if f is null.. F is File f");
  383. if (f != null) {
  384. LOGGER.info("f is not null, loading configuration");
  385. LOGGER.info("configuration.load(f);");
  386. configuration.load(f);
  387. } else {
  388. LOGGER.info("f is null... not going to do configuration.load(f)");
  389. }
  390.  
  391. LOGGER.info("mimes = new HashMap<String, String>();");
  392. mimes = new HashMap<String, String>();
  393.  
  394. LOGGER.info("String mimeTypes = getString(MIME_TYPES_CHANGES, null);");
  395. String mimeTypes = getString(MIME_TYPES_CHANGES, null);
  396.  
  397. LOGGER.info("checking if StringUtils.isNotBlank(mimeTypes)");
  398. if (StringUtils.isNotBlank(mimeTypes)) {
  399. LOGGER.info("StringUtils.isNotBlank(mimeTypes) == true");
  400. LOGGER.info("StringTokenizer st = new StringTokenizer(mimeTypes);");
  401. StringTokenizer st = new StringTokenizer(mimeTypes, "|");
  402.  
  403. while (st.hasMoreTokens()) {
  404. String mime_change = st.nextToken().trim();
  405. int equals = mime_change.indexOf("=");
  406.  
  407. if (equals > -1) {
  408. String old = mime_change.substring(0, equals).trim().toLowerCase();
  409. String nw = mime_change.substring(equals + 1).trim().toLowerCase();
  410. mimes.put(old, nw);
  411. }
  412. }
  413. }
  414.  
  415. DLNAPN = new HashMap<String, String>();
  416. String DLNAPNchanges = getString(DLNA_PN_CHANGES, null);
  417.  
  418. if (DLNAPNchanges != null) {
  419. LOGGER.trace("Config DLNAPNchanges: " + DLNAPNchanges);
  420. }
  421.  
  422. if (StringUtils.isNotBlank(DLNAPNchanges)) {
  423. StringTokenizer st = new StringTokenizer(DLNAPNchanges, "|");
  424. while (st.hasMoreTokens()) {
  425. String DLNAPN_change = st.nextToken().trim();
  426. int equals = DLNAPN_change.indexOf("=");
  427. if (equals > -1) {
  428. String old = DLNAPN_change.substring(0, equals).trim().toUpperCase();
  429. String nw = DLNAPN_change.substring(equals + 1).trim().toUpperCase();
  430. DLNAPN.put(old, nw);
  431. }
  432. }
  433. }
  434.  
  435. if (f == null) {
  436. // the default renderer supports everything !
  437. configuration.addProperty(MEDIAPARSERV2, true);
  438. configuration.addProperty(MEDIAPARSERV2_THUMB, true);
  439. configuration.addProperty(SUPPORTED, "f:.+");
  440. }
  441.  
  442. if (isMediaParserV2()) {
  443. formatConfiguration = new FormatConfiguration(configuration.getList(SUPPORTED));
  444. }
  445. }
  446.  
  447. public String getDLNAPN(String old) {
  448. if (DLNAPN.containsKey(old)) {
  449. return DLNAPN.get(old);
  450. }
  451. return old;
  452. }
  453.  
  454. public boolean supportsFormat(Format f) {
  455. switch (f.getType()) {
  456. case Format.VIDEO:
  457. return isVideoSupported();
  458. case Format.AUDIO:
  459. return isAudioSupported();
  460. case Format.IMAGE:
  461. return isImageSupported();
  462. default:
  463. break;
  464. }
  465.  
  466. return false;
  467. }
  468.  
  469. public boolean isVideoSupported() {
  470. return getBoolean(VIDEO, true);
  471. }
  472.  
  473. public boolean isAudioSupported() {
  474. return getBoolean(AUDIO, true);
  475. }
  476.  
  477. public boolean isImageSupported() {
  478. return getBoolean(IMAGE, true);
  479. }
  480.  
  481. public boolean isTranscodeToWMV() {
  482. return getVideoTranscode().equals(WMV);
  483. }
  484.  
  485. public boolean isTranscodeToAC3() {
  486. return isTranscodeToMPEGPSAC3() || isTranscodeToMPEGTSAC3();
  487. }
  488.  
  489. public boolean isTranscodeToMPEGPSAC3() {
  490. String videoTranscode = getVideoTranscode();
  491. return videoTranscode.equals(MPEGPSAC3) || videoTranscode.equals(DEPRECATED_MPEGPSAC3);
  492. }
  493.  
  494. public boolean isTranscodeToMPEGTSAC3() {
  495. return getVideoTranscode().equals(MPEGTSAC3);
  496. }
  497.  
  498. public boolean isAutoRotateBasedOnExif() {
  499. return getBoolean(AUTO_EXIF_ROTATE, false);
  500. }
  501.  
  502. public boolean isTranscodeToMP3() {
  503. return getAudioTranscode().equals(MP3);
  504. }
  505.  
  506. public boolean isTranscodeToLPCM() {
  507. return getAudioTranscode().equals(LPCM);
  508. }
  509.  
  510. public boolean isTranscodeToWAV() {
  511. return getAudioTranscode().equals(WAV);
  512. }
  513.  
  514. public boolean isTranscodeAudioTo441() {
  515. return getBoolean(TRANSCODE_AUDIO_441KHZ, false);
  516. }
  517.  
  518. public boolean isH264Level41Limited() {
  519. return getBoolean(H264_L41_LIMITED, false);
  520. }
  521.  
  522. public boolean isTranscodeFastStart() {
  523. return getBoolean(TRANSCODE_FAST_START, false);
  524. }
  525.  
  526. public boolean isDLNALocalizationRequired() {
  527. return getBoolean(DLNA_LOCALIZATION_REQUIRED, false);
  528. }
  529.  
  530. public String getMimeType(String mimetype) {
  531. if (isMediaParserV2()) {
  532. if (mimetype != null && mimetype.equals(HTTPResource.VIDEO_TRANSCODE)) {
  533. mimetype = getFormatConfiguration().match(FormatConfiguration.MPEGPS, FormatConfiguration.MPEG2, FormatConfiguration.AC3);
  534. if (isTranscodeToMPEGTSAC3()) {
  535. mimetype = getFormatConfiguration().match(FormatConfiguration.MPEGTS, FormatConfiguration.MPEG2, FormatConfiguration.AC3);
  536. } else if (isTranscodeToWMV()) {
  537. mimetype = getFormatConfiguration().match(FormatConfiguration.WMV, FormatConfiguration.WMV, FormatConfiguration.WMA);
  538. }
  539. } else if (mimetype != null && mimetype.equals(HTTPResource.AUDIO_TRANSCODE)) {
  540. mimetype = getFormatConfiguration().match(FormatConfiguration.LPCM, null, null);
  541.  
  542. if (mimetype != null) {
  543. if (isTranscodeAudioTo441()) {
  544. mimetype += ";rate=44100;channels=2";
  545. } else {
  546. mimetype += ";rate=48000;channels=2";
  547. }
  548. }
  549.  
  550. if (isTranscodeToWAV()) {
  551. mimetype = getFormatConfiguration().match(FormatConfiguration.WAV, null, null);
  552. } else if (isTranscodeToMP3()) {
  553. mimetype = getFormatConfiguration().match(FormatConfiguration.MP3, null, null);
  554. }
  555. }
  556.  
  557. return mimetype;
  558. }
  559.  
  560. if (mimetype != null && mimetype.equals(HTTPResource.VIDEO_TRANSCODE)) {
  561. mimetype = HTTPResource.MPEG_TYPEMIME;
  562. if (isTranscodeToWMV()) {
  563. mimetype = isMediaParserV2()
  564. ? getFormatConfiguration().match(FormatConfiguration.WMV, FormatConfiguration.WMV, FormatConfiguration.WMA)
  565. : HTTPResource.WMV_TYPEMIME;
  566. } else if (isTranscodeToMPEGTSAC3()) {
  567. mimetype = isMediaParserV2()
  568. ? getFormatConfiguration().match(FormatConfiguration.MPEGTS, FormatConfiguration.MPEG2, FormatConfiguration.AC3)
  569. : HTTPResource.MPEG_TYPEMIME;
  570. } else { // default: MPEGPSAC3
  571. mimetype = isMediaParserV2()
  572. ? getFormatConfiguration().match(FormatConfiguration.MPEGPS, FormatConfiguration.MPEG2, FormatConfiguration.AC3)
  573. : HTTPResource.MPEG_TYPEMIME;
  574. }
  575. } else if (mimetype.equals(HTTPResource.AUDIO_TRANSCODE)) {
  576. if (isTranscodeToWAV()) {
  577. mimetype = isMediaParserV2()
  578. ? getFormatConfiguration().match(FormatConfiguration.WAV, null, null)
  579. : HTTPResource.AUDIO_WAV_TYPEMIME;
  580. } else if (isTranscodeToMP3()) {
  581. mimetype = isMediaParserV2()
  582. ? getFormatConfiguration().match(FormatConfiguration.MP3, null, null)
  583. : HTTPResource.AUDIO_MP3_TYPEMIME;
  584. } else { // default: LPCM
  585. mimetype = isMediaParserV2()
  586. ? getFormatConfiguration().match(FormatConfiguration.LPCM, null, null)
  587. : HTTPResource.AUDIO_LPCM_TYPEMIME;
  588.  
  589. if (isTranscodeAudioTo441()) {
  590. mimetype += ";rate=44100;channels=2";
  591. } else {
  592. mimetype += ";rate=48000;channels=2";
  593. }
  594. }
  595.  
  596. if (isTranscodeToMP3()) {
  597. mimetype = HTTPResource.AUDIO_MP3_TYPEMIME;
  598. }
  599.  
  600. if (isTranscodeToWAV()) {
  601. mimetype = HTTPResource.AUDIO_WAV_TYPEMIME;
  602. }
  603. }
  604.  
  605. if (mimes.containsKey(mimetype)) {
  606. return mimes.get(mimetype);
  607. }
  608.  
  609. return mimetype;
  610. }
  611.  
  612. /**
  613. * Pattern match a user agent header string to the "UserAgentSearch"
  614. * expression for this renderer. Will return false when the pattern is
  615. * empty or when no match can be made.
  616. *
  617. * @param header The header containing the user agent.
  618. * @return True if the pattern matches.
  619. */
  620. public boolean matchUserAgent(String header) {
  621. String userAgent = getUserAgent();
  622. Pattern userAgentPattern = null;
  623.  
  624. if (StringUtils.isNotBlank(userAgent)) {
  625. userAgentPattern = Pattern.compile(userAgent, Pattern.CASE_INSENSITIVE);
  626.  
  627. return userAgentPattern.matcher(header).find();
  628. } else {
  629. return false;
  630. }
  631. }
  632.  
  633. /**
  634. * Pattern match a header string to the "UserAgentAdditionalHeaderSearch"
  635. * expression for this renderer. Will return false when the pattern is
  636. * empty or when no match can be made.
  637. *
  638. * @param header The additional header string.
  639. * @return True if the pattern matches.
  640. */
  641. public boolean matchAdditionalUserAgent(String header) {
  642. String userAgentAdditionalHeader = getUserAgentAdditionalHttpHeaderSearch();
  643. Pattern userAgentAddtionalPattern = null;
  644.  
  645. if (StringUtils.isNotBlank(userAgentAdditionalHeader)) {
  646. userAgentAddtionalPattern = Pattern.compile(userAgentAdditionalHeader, Pattern.CASE_INSENSITIVE);
  647.  
  648. return userAgentAddtionalPattern.matcher(header).find();
  649. } else {
  650. return false;
  651. }
  652. }
  653.  
  654. /**
  655. * Returns the pattern to match the User-Agent header to as defined in the
  656. * renderer configuration. Default value is "".
  657. *
  658. * @return The User-Agent search pattern.
  659. */
  660. public String getUserAgent() {
  661. return getString(USER_AGENT, "");
  662. }
  663.  
  664. /**
  665. * RendererName: Determines the name that is displayed in the PMS user
  666. * interface when this renderer connects. Default value is "Unknown
  667. * renderer".
  668. *
  669. * @return The renderer name.
  670. */
  671. public String getRendererName() {
  672. return getString(RENDERER_NAME, Messages.getString("PMS.17"));
  673. }
  674.  
  675. /**
  676. * Returns the icon to use for displaying this renderer in PMS as defined
  677. * in the renderer configurations. Default value is "unknown.png".
  678. *
  679. * @return The renderer icon.
  680. */
  681. public String getRendererIcon() {
  682. return getString(RENDERER_ICON, "unknown.png");
  683. }
  684.  
  685. /**
  686. * LongFileNameFormat: Determines how media file names are formatted in the
  687. * regular folders. All supported formatting options are described in
  688. * {@link net.pms.dlna.DLNAResource#getDisplayName(RendererConfiguration)
  689. * getDisplayName(RendererConfiguration)}.
  690. *
  691. * @return The format for file names in the regular folders.
  692. */
  693. public String getLongFileNameFormat() {
  694. return getString(LONG_FILE_NAME_FORMAT, Messages.getString("DLNAResource.4"));
  695. }
  696.  
  697. /**
  698. * ShortFileNameFormat: Determines how media file names are formatted in the
  699. * transcoding virtual folder. All supported formatting options are described in
  700. * {@link net.pms.dlna.DLNAResource#getDisplayName(RendererConfiguration)
  701. * getDisplayName(RendererConfiguration)}.
  702. *
  703. * @return The format for file names in the transcoding virtual folder.
  704. */
  705. public String getShortFileNameFormat() {
  706. return getString(SHORT_FILE_NAME_FORMAT, Messages.getString("DLNAResource.3"));
  707. }
  708.  
  709. /**
  710. * Returns the the name of an additional HTTP header whose value should
  711. * be matched with the additional header search pattern. The header name
  712. * must be an exact match (read: the header has to start with the exact
  713. * same case sensitive string). The default value is <code>null</code>.
  714. *
  715. * @return The additional HTTP header name.
  716. */
  717. public String getUserAgentAdditionalHttpHeader() {
  718. return getString(USER_AGENT_ADDITIONAL_HEADER, null);
  719. }
  720.  
  721. /**
  722. * Returns the pattern to match additional headers to as defined in the
  723. * renderer configuration. Default value is "".
  724. *
  725. * @return The User-Agent search pattern.
  726. */
  727. public String getUserAgentAdditionalHttpHeaderSearch() {
  728. return getString(USER_AGENT_ADDITIONAL_SEARCH, "");
  729. }
  730.  
  731. public String getUseSameExtension(String file) {
  732. String s = getString(USE_SAME_EXTENSION, null);
  733.  
  734. if (s != null) {
  735. s = file + "." + s;
  736. } else {
  737. s = file;
  738. }
  739.  
  740. return s;
  741. }
  742.  
  743. /**
  744. * Returns true if SeekByTime is set to "true" or "exclusive", false otherwise.
  745. * Default value is false.
  746. *
  747. * @return true if the renderer supports seek-by-time, false otherwise.
  748. */
  749. public boolean isSeekByTime() {
  750. return isSeekByTimeExclusive() || getBoolean(SEEK_BY_TIME, false);
  751. }
  752.  
  753. /**
  754. * Returns true if SeekByTime is set to "exclusive", false otherwise.
  755. * Default value is false.
  756. *
  757. * @return true if the renderer supports seek-by-time exclusively
  758. * (i.e. not in conjunction with seek-by-byte), false otherwise.
  759. */
  760. public boolean isSeekByTimeExclusive() {
  761. return getString(SEEK_BY_TIME, "").equalsIgnoreCase("exclusive");
  762. }
  763.  
  764. public boolean isMuxH264MpegTS() {
  765. boolean muxCompatible = getBoolean(MUX_H264_WITH_MPEGTS, true);
  766. if (isMediaParserV2()) {
  767. muxCompatible = getFormatConfiguration().match(FormatConfiguration.MPEGTS, FormatConfiguration.H264, null) != null;
  768. }
  769.  
  770. if (Platform.isMac() && System.getProperty("os.version") != null && System.getProperty("os.version").contains("10.4.")) {
  771. muxCompatible = false; // no tsMuxeR for 10.4 (yet?)
  772. }
  773.  
  774. return muxCompatible;
  775. }
  776.  
  777. public boolean isDTSPlayable() {
  778. return isMuxDTSToMpeg() || (isWrapDTSIntoPCM() && isMuxLPCMToMpeg());
  779. }
  780.  
  781. public boolean isMuxDTSToMpeg() {
  782. if (isMediaParserV2()) {
  783. return getFormatConfiguration().isDTSSupported();
  784. }
  785.  
  786. return getBoolean(MUX_DTS_TO_MPEG, false);
  787. }
  788.  
  789. public boolean isWrapDTSIntoPCM() {
  790. return getBoolean(WRAP_DTS_INTO_PCM, true);
  791. }
  792.  
  793. public boolean isLPCMPlayable() {
  794. return isMuxLPCMToMpeg();
  795. }
  796.  
  797. public boolean isMuxLPCMToMpeg() {
  798. if (isMediaParserV2()) {
  799. return getFormatConfiguration().isLPCMSupported();
  800. }
  801.  
  802. return getBoolean(MUX_LPCM_TO_MPEG, true);
  803. }
  804.  
  805. public boolean isMpeg2Supported() {
  806. if (isMediaParserV2()) {
  807. return getFormatConfiguration().isMpeg2Supported();
  808. }
  809.  
  810. return isPS3();
  811. }
  812.  
  813. /**
  814. * Returns the codec to use for video transcoding for this renderer as
  815. * defined in the renderer configuration. Default value is "MPEGPSAC3".
  816. *
  817. * @return The codec name.
  818. */
  819. public String getVideoTranscode() {
  820. return getString(TRANSCODE_VIDEO, MPEGPSAC3);
  821. }
  822.  
  823. /**
  824. * Returns the codec to use for audio transcoding for this renderer as
  825. * defined in the renderer configuration. Default value is "LPCM".
  826. *
  827. * @return The codec name.
  828. */
  829. public String getAudioTranscode() {
  830. return getString(TRANSCODE_AUDIO, LPCM);
  831. }
  832.  
  833. /**
  834. * Returns whether or not to use the default DVD buffer size for this
  835. * renderer as defined in the renderer configuration. Default is false.
  836. *
  837. * @return True if the default size should be used.
  838. */
  839. public boolean isDefaultVBVSize() {
  840. return getBoolean(DEFAULT_VBV_BUFSIZE, false);
  841. }
  842.  
  843. /**
  844. * Returns the maximum bitrate (in megabits-per-second) supported by the media renderer as defined
  845. * in the renderer configuration. The default value is <code>null</code>.
  846. *
  847. * @return The bitrate.
  848. */
  849. @Deprecated
  850. // TODO this should return an integer and the units should be bits-per-second
  851. public String getMaxVideoBitrate() {
  852. return getString(MAX_VIDEO_BITRATE, null);
  853. }
  854.  
  855. /**
  856. * Returns the override settings for MEncoder quality settings in PMS as
  857. * defined in the renderer configuration. The default value is "".
  858. *
  859. * @return The MEncoder quality settings.
  860. */
  861. public String getCustomMencoderQualitySettings() {
  862. return getString(CUSTOM_MENCODER_QUALITY_SETTINGS, "");
  863. }
  864.  
  865. /**
  866. * Returns the override settings for MEncoder custom options in PMS as
  867. * defined in the renderer configuration. The default value is "".
  868. *
  869. * @return The MEncoder custom options.
  870. */
  871. public String getCustomMencoderOptions() {
  872. return getString(CUSTOM_MENCODER_OPTIONS, "");
  873. }
  874.  
  875. /**
  876. * Returns the maximum video width supported by the renderer as defined in
  877. * the renderer configuration. The default value 0 means unlimited.
  878. *
  879. * @return The maximum video width.
  880. */
  881. public int getMaxVideoWidth() {
  882. // FIXME why is this 1920 if the default value is 0 (unlimited)?
  883. // XXX we should also require width and height to both be 0 or both be > 0
  884. return getInt(MAX_VIDEO_WIDTH, 1920);
  885. }
  886.  
  887. /**
  888. * Returns the maximum video height supported by the renderer as defined
  889. * in the renderer configuration. The default value 0 means unlimited.
  890. *
  891. * @return The maximum video height.
  892. */
  893. public int getMaxVideoHeight() {
  894. // FIXME why is this 1080 if the default value is 0 (unlimited)?
  895. // XXX we should also require width and height to both be 0 or both be > 0
  896. return getInt(MAX_VIDEO_HEIGHT, 1080);
  897. }
  898.  
  899. /**
  900. * Returns <code>true</code> if the renderer has a maximum supported width
  901. * or height, <code>false</code> otherwise.
  902. *
  903. * @return boolean indicating whether the renderer may need videos to be resized.
  904. */
  905. public boolean isVideoRescale() {
  906. return getMaxVideoWidth() > 0 && getMaxVideoHeight() > 0;
  907. }
  908.  
  909. public boolean isDLNAOrgPNUsed() {
  910. return getBoolean(DLNA_ORGPN_USE, true);
  911. }
  912.  
  913. /**
  914. * Returns the comma separated list of file extensions that are forced to
  915. * be transcoded and never streamed, as defined in the renderer
  916. * configuration. Default value is "".
  917. *
  918. * @return The file extensions.
  919. */
  920. public String getTranscodedExtensions() {
  921. return getString(TRANSCODE_EXT, "");
  922. }
  923.  
  924. /**
  925. * Returns the comma separated list of file extensions that are forced to
  926. * be streamed and never transcoded, as defined in the renderer
  927. * configuration. Default value is "".
  928. *
  929. * @return The file extensions.
  930. */
  931. public String getStreamedExtensions() {
  932. return getString(STREAM_EXT, "");
  933. }
  934.  
  935. /**
  936. * Returns the size to report back to the renderer when transcoding media
  937. * as defined in the renderer configuration. Default value is 0.
  938. *
  939. * @return The size to report.
  940. */
  941. public long getTranscodedSize() {
  942. return getLong(TRANSCODED_SIZE, 0);
  943. }
  944.  
  945. /**
  946. * Some devices (e.g. Samsung) recognize a custom HTTP header for retrieving
  947. * the contents of a subtitles file. This method will return the name of that
  948. * custom HTTP header, or "" if no such header exists. Default value is "".
  949. *
  950. * @return The name of the custom HTTP header.
  951. */
  952. public String getSubtitleHttpHeader() {
  953. return getString(SUBTITLE_HTTP_HEADER, "");
  954. }
  955.  
  956. private int getInt(String key, int def) {
  957. try {
  958. return configuration.getInt(key, def);
  959. } catch (ConversionException e) {
  960. return def;
  961. }
  962. }
  963.  
  964. private long getLong(String key, int def) {
  965. try {
  966. return configuration.getLong(key, def);
  967. } catch (ConversionException e) {
  968. return def;
  969. }
  970. }
  971.  
  972. private boolean getBoolean(String key, boolean def) {
  973. try {
  974. return configuration.getBoolean(key, def);
  975. } catch (ConversionException e) {
  976. return def;
  977. }
  978. }
  979.  
  980. /**
  981. * Return the <code>String</code> value for a given configuration key if the
  982. * value is non-blank (i.e. not null, not an empty string, not all whitespace).
  983. * Otherwise return the supplied default value.
  984. * The value is returned with leading and trailing whitespace removed in both cases.
  985. * @param key The key to look up.
  986. * @param def The default value to return when no valid key value can be found.
  987. * @return The value configured for the key.
  988. */
  989. private String getString(String key, String def) {
  990. return ConfigurationUtil.getNonBlankConfigurationString(configuration, key, def);
  991. }
  992.  
  993. public String toString() {
  994. return getRendererName();
  995. }
  996.  
  997. public boolean isMediaParserV2() {
  998. return getBoolean(MEDIAPARSERV2, false) && LibMediaInfoParser.isValid();
  999. }
  1000.  
  1001. public boolean isMediaParserV2ThumbnailGeneration() {
  1002. return getBoolean(MEDIAPARSERV2_THUMB, false) && LibMediaInfoParser.isValid();
  1003. }
  1004.  
  1005. public boolean isForceJPGThumbnails() {
  1006. return (getBoolean(FORCE_JPG_THUMBNAILS, false) && LibMediaInfoParser.isValid()) || isBRAVIA();
  1007. }
  1008.  
  1009. public boolean isShowAudioMetadata() {
  1010. return getBoolean(SHOW_AUDIO_METADATA, true);
  1011. }
  1012.  
  1013. public boolean isShowSubMetadata() {
  1014. return getBoolean(SHOW_SUB_METADATA, true);
  1015. }
  1016.  
  1017. public boolean isDLNATreeHack() {
  1018. return getBoolean(DLNA_TREE_HACK, false) && LibMediaInfoParser.isValid();
  1019. }
  1020.  
  1021. /**
  1022. * Returns whether or not to omit sending a content length header when the
  1023. * length is unknown, as defined in the renderer configuration. Default
  1024. * value is false.
  1025. * <p>
  1026. * Some renderers are particular about the "Content-Length" headers in
  1027. * requests (e.g. Sony blu-ray players). By default, PMS will send a
  1028. * "Content-Length" that refers to the total media size, even if the exact
  1029. * length is unknown.
  1030. *
  1031. * @return True if sending the content length header should be omitted.
  1032. */
  1033. public boolean isChunkedTransfer() {
  1034. return getBoolean(CHUNKED_TRANSFER, false);
  1035. }
  1036.  
  1037. /**
  1038. * Returns whether or not the renderer can handle the given format
  1039. * natively, based on its configuration in the renderer.conf. If it can
  1040. * handle a format natively, content can be streamed to the renderer. If
  1041. * not, content should be transcoded before sending it to the renderer.
  1042. *
  1043. * @param mediainfo The {@link DLNAMediaInfo} information parsed from the
  1044. * media file.
  1045. * @param format The {@link Format} to test compatibility for.
  1046. * @return True if the renderer natively supports the format, false
  1047. * otherwise.
  1048. */
  1049. public boolean isCompatible(DLNAMediaInfo mediainfo, Format format) {
  1050. // Use the configured "Supported" lines in the renderer.conf
  1051. // to see if any of them match the MediaInfo library
  1052. if (isMediaParserV2() && mediainfo != null && getFormatConfiguration().match(mediainfo) != null) {
  1053. return true;
  1054. }
  1055.  
  1056. if (format != null) {
  1057. String noTranscode = "";
  1058.  
  1059. if (PMS.getConfiguration() != null) {
  1060. noTranscode = PMS.getConfiguration().getNoTranscode();
  1061. }
  1062.  
  1063. // Is the format among the ones to be streamed?
  1064. return format.skip(noTranscode, getStreamedExtensions());
  1065. } else {
  1066. // Not natively supported.
  1067. return false;
  1068. }
  1069. }
  1070. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement