Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

image thumbnailer:

	- add support for TIFF and other formats
	- trap exception when handling unsupported formats
  • Loading branch information...
commit 5154dc9ef790fbec6d60b0be19081d20e39baf8f 1 parent 4240de6
@chocolateboy chocolateboy authored
View
5 CHANGELOG.txt
@@ -23,7 +23,10 @@ Changelog:
- Updated Brazilian translation (thanks, Bruno Arueira)
Misc:
- fix bug that prevents PMS running multiple profiles concurrently
- - fix NPE for DLNA subtitle requests
+ - fix NPE when responding to DLNA subtitle requests
+ - image thumbnailer:
+ - add thumbnail support for TIFF and other formats
+ - trap exception when handling unsupported formats
1.72.0 - 2012-11-18
View
125 pom.xml
@@ -166,12 +166,24 @@
</snapshots>
</repository>
- <!-- JUnRar by Edmund Wagner -->
- <repository>
- <id>JUnRar mirror repository</id>
- <name>JUnRar mirror repository</name>
- <url>https://junrar-repository.googlecode.com/svn/deploy/</url>
- </repository>
+ <!-- JUnRar by Edmund Wagner -->
+ <repository>
+ <id>JUnRar mirror repository</id>
+ <name>JUnRar mirror repository</name>
+ <url>https://junrar-repository.googlecode.com/svn/deploy/</url>
+ </repository>
+
+ <!-- for jai-imageio-core-standalone, which is used by Thumbnailator -->
+ <repository>
+ <releases />
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ <id>mygrid-repository</id>
+ <name>myGrid Repository</name>
+ <url>http://www.mygrid.org.uk/maven/repository</url>
+ </repository>
+
</repositories>
<dependencies>
@@ -261,7 +273,7 @@
<artifactId>slf4j-api</artifactId>
<version>${slf4j-version}</version>
</dependency>
-
+
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
@@ -274,7 +286,7 @@
<version>2.2.2</version>
</dependency>
- <!-- originally, hamcrest-all 1.2RC2 for the artifact change, see: https://code.google.com/p/hamcrest/issues/detail?id=12#c54
+ <!-- originally, hamcrest-all 1.2RC2 for the artifact change, see: https://code.google.com/p/hamcrest/issues/detail?id=12#c54
FIXME: verify if needed -->
<dependency>
<groupId>org.hamcrest</groupId>
@@ -307,6 +319,25 @@
<version>0.4.2</version>
</dependency>
+ <!--
+ this is not used on its own, but rather provides
+ ImageReader subclasses that make support for additional
+ formats (e.g. TIFF) available to the ImageIO API, and thus
+ to Thumbnailator (our built-in image thumbnailer).
+
+ XXX be careful upgrading this. this version is redistributable;
+ other versions may not be:
+
+ https://code.google.com/p/thumbnailator/issues/detail?id=32
+ https://github.com/stain/jai-imageio-core
+ http://stackoverflow.com/questions/1209583/using-java-advanced-imaging-with-maven
+ -->
+ <dependency>
+ <groupId>net.java.dev.jai-imageio</groupId>
+ <artifactId>jai-imageio-core-standalone</artifactId>
+ <version>1.2-pre-dr-b04-2011-07-04</version>
+ </dependency>
+
<dependency>
<groupId>org</groupId>
<artifactId>jaudiotagger</artifactId>
@@ -386,6 +417,7 @@
<version>1.4</version>
<scope>test</scope>
</dependency>
+
</dependencies>
<build>
@@ -393,16 +425,16 @@
<resources>
<!--
- squashed bug: this needs to be defined explicitly if a <resources>...</resources>
+ squashed bug: this needs to be defined explicitly if a <resources>...</resources>
section is used
-->
<resource>
<directory>src/main/resources</directory>
<!--
- By default Maven packages resources in the top-level of the jar.
- For compatibility with trunk PMS, we preserve that destination. If this is
- ever changed to the Maven default, a handful of files will need to be changed
+ By default, Maven packages resources in the top-level of the jar.
+ For compatibility with trunk PMS, we preserve that destination. If this is
+ ever changed to the Maven default, a handful of files will need to be changed
to reference "/resource.name" rather than "/resources/resource.name"
-->
<targetPath>resources</targetPath>
@@ -531,7 +563,7 @@
</configuration>
</plugin>
</plugins>
- </pluginManagement>
+ </pluginManagement>
<plugins>
<!--
@@ -599,15 +631,15 @@
exist in a public Maven repository. That is why we store some jar files in the
"src/main/external-resources/lib" directory and have this plugin take care of
the installation at build time.
-
+
Note: The plugin does not work automatically in Maven 3. To install the required
dependencies execute the following commands:
-
- mvn com.savage7.maven.plugins:maven-external-dependency-plugin:resolve-external
- mvn com.savage7.maven.plugins:maven-external-dependency-plugin:install-external
-
+
+ mvn com.savage7.maven.plugins:maven-external-dependency-plugin:resolve-external
+ mvn com.savage7.maven.plugins:maven-external-dependency-plugin:install-external
+
Checksums can be calculated with "openssl sha1 [filename]".
-
+
See: http://code.google.com/p/maven-external-dependency-plugin/issues/detail?id=8#c4
-->
<plugin>
@@ -634,7 +666,7 @@
<extractFile>jgoodies-{artifactId}-{version}/jgoodies-{artifactId}-{version}.{packaging}</extractFile>
<extractFileChecksum>f3955d59f2263fb889b02febec74377647a2eaba</extractFileChecksum>
</artifactItem>
-
+
<artifactItem>
<groupId>com.jgoodies</groupId>
<artifactId>forms</artifactId>
@@ -647,7 +679,7 @@
<extractFile>jgoodies-{artifactId}-{version}/jgoodies-{artifactId}-{version}.{packaging}</extractFile>
<extractFileChecksum>de8a67990cbeae62910cc2022f06c466476f2f2b</extractFileChecksum>
</artifactItem>
-
+
<artifactItem>
<groupId>com.jgoodies</groupId>
<artifactId>looks</artifactId>
@@ -660,7 +692,7 @@
<extractFile>jgoodies-{artifactId}-{version}/jgoodies-{artifactId}-{version}.{packaging}</extractFile>
<extractFileChecksum>535745c08e342f7ea78556baad44a4a52ffb7863</extractFileChecksum>
</artifactItem>
-
+
<artifactItem>
<groupId>jwbroek.cuelib</groupId>
<artifactId>cuelib</artifactId>
@@ -734,10 +766,10 @@
<version>1.2</version>
</dependency>
</dependencies>
-
+
<configuration>
<reportPlugins>
-
+
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
@@ -871,7 +903,7 @@
<locale>sl</locale>
<locale>sv</locale>
<locale>zhs</locale>
- <locale>zht</locale>
+ <locale>zht</locale>
</locales>
</configuration>
</plugin>
@@ -915,7 +947,7 @@
when browsing folders with files in them on Windows.
-->
<jna-version>3.2.5</jna-version>
- </properties>
+ </properties>
<pluginRepositories>
<pluginRepository>
@@ -929,14 +961,14 @@
</releases>
</pluginRepository>
</pluginRepositories>
-
+
<build>
<plugins>
<!-- Plugin to assemble a jar with dependencies -->
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
- <version>2.3</version>
+ <version>2.4</version>
<executions>
<execution>
<id>make-jar-with-dependencies-win</id>
@@ -951,13 +983,24 @@
<archive>
<manifest>
<mainClass>net.pms.PMS</mainClass>
+ <!--
+ this is required to make the ImageIo image "plugins" provided by jai-imageio-core-standalone work.
+ without it, the following exception is thrown when PMS starts:
+
+ Configuration error: java.util.ServiceConfigurationError: javax.imageio.spi.ImageInputStreamSpi:
+ Provider com.sun.media.imageioimpl.stream.ChannelImageInputStreamSpi could not be instantiated:
+ java.lang.IllegalArgumentException: vendorName == null!
+
+ See: https://thierrywasyl.wordpress.com/2009/07/24/jai-how-to-solve-vendorname-null-exception/
+ -->
+ <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
-
+
<!--
Plugin to move the pms-x.x.x-jar-with-dependencies.jar to pms.jar
-->
@@ -1088,12 +1131,12 @@
</execution>
</executions>
</plugin>
-
+
<!--
Plugin to build a Windows installer using the nullsoft scriptable install system (NSIS)
The "generate-project" goal generates a file "target/project.nsh", which contains the
definitions for project variables like "${PROJECT_BASEDIR}" that can be used in the
- "pms.nsi" and "setup.nsi" files.
+ "pms.nsi" and "setup.nsi" files.
-->
<plugin>
<groupId>org.codehaus.mojo</groupId>
@@ -1145,12 +1188,12 @@
<family>unix</family>
</os>
</activation>
-
+
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
- <version>2.2.1</version>
+ <version>2.4</version>
<executions>
<!-- Assemble a jar with dependencies -->
<execution>
@@ -1166,6 +1209,7 @@
<archive>
<manifest>
<mainClass>net.pms.PMS</mainClass>
+ <addDefaultImplementationEntries>true</addDefaultImplementationEntries> <!-- see above for documentation -->
</manifest>
</archive>
</configuration>
@@ -1185,6 +1229,7 @@
<archive>
<manifest>
<mainClass>net.pms.PMS</mainClass>
+ <addDefaultImplementationEntries>true</addDefaultImplementationEntries> <!-- see above for documentation -->
</manifest>
</archive>
</configuration>
@@ -1238,7 +1283,7 @@
</plugin>
</plugins>
- </build>
+ </build>
</profile>
@@ -1255,7 +1300,7 @@
</activation>
<build>
<plugins>
-
+
<!--
Plugin to download binaries before assembly
-->
@@ -1330,7 +1375,7 @@
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
- <version>2.2.1</version>
+ <version>2.4</version>
<executions>
<!-- Assemble a jar with dependencies -->
<execution>
@@ -1346,13 +1391,14 @@
<archive>
<manifest>
<mainClass>net.pms.PMS</mainClass>
+ <addDefaultImplementationEntries>true</addDefaultImplementationEntries> <!-- see above for documentation -->
</manifest>
</archive>
</configuration>
</execution>
<!--
- Assemble the PMS files in the right place before creating
+ Assemble the PMS files in the right place before creating
the App Bundle and DMG disk image.
-->
<execution>
@@ -1368,11 +1414,12 @@
<archive>
<manifest>
<mainClass>net.pms.PMS</mainClass>
+ <addDefaultImplementationEntries>true</addDefaultImplementationEntries> <!-- see above for documentation -->
</manifest>
</archive>
</configuration>
</execution>
-
+
</executions>
</plugin>
@@ -1442,7 +1489,7 @@
</plugin>
</plugins>
</build>
- </profile>
+ </profile>
</profiles>
</project>
View
2  src/main/java/net/pms/configuration/FormatConfiguration.java
@@ -273,8 +273,10 @@ public void parse(DLNAMediaInfo media, InputFile file, Format ext, int type) {
}
if (forceV1) {
+ // XXX this path generates thumbnails
media.parse(file, ext, type, false);
} else {
+ // XXX this path doesn't generate thumbnails
LibMediaInfoParser.parse(media, file, type);
}
} else {
View
200 src/main/java/net/pms/dlna/DLNAMediaInfo.java
@@ -18,6 +18,8 @@
*/
package net.pms.dlna;
+import com.sun.jna.Platform;
+
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
@@ -39,8 +41,10 @@
import javax.imageio.ImageIO;
-import net.coobird.thumbnailator.Thumbnails;
+import net.coobird.thumbnailator.tasks.UnsupportedFormatException;
import net.coobird.thumbnailator.Thumbnails.Builder;
+import net.coobird.thumbnailator.Thumbnails;
+
import net.pms.PMS;
import net.pms.configuration.RendererConfiguration;
import net.pms.formats.AudioAsVideo;
@@ -62,19 +66,19 @@
import org.apache.sanselan.formats.jpeg.JpegImageMetadata;
import org.apache.sanselan.formats.tiff.TiffField;
import org.apache.sanselan.formats.tiff.constants.TiffConstants;
+
import org.jaudiotagger.audio.AudioFile;
import org.jaudiotagger.audio.AudioFileIO;
import org.jaudiotagger.audio.AudioHeader;
import org.jaudiotagger.tag.FieldKey;
import org.jaudiotagger.tag.Tag;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.sun.jna.Platform;
-
/**
* This class keeps track of media file metadata scanned by the MediaInfo library.
- *
+ *
* TODO: Change all instance variables to private. For backwards compatibility
* with external plugin code the variables have all been marked as deprecated
* instead of changed to private, but this will surely change in the future.
@@ -82,9 +86,12 @@
* removed.
*/
public class DLNAMediaInfo implements Cloneable {
- private static final Logger logger = LoggerFactory.getLogger(DLNAMediaInfo.class);
+ private static final Logger LOGGER = LoggerFactory.getLogger(DLNAMediaInfo.class);
+ private static final String THUMBNAIL_DIRECTORY_NAME = "thumbs";
+
public static final long ENDFILE_POS = 99999475712L;
public static final long TRANS_SIZE = 100000000000L;
+
private boolean h264_parsed;
// Stored in database
@@ -216,7 +223,7 @@
* @deprecated Use standard getter and setter to access this variable.
*/
@Deprecated
- public boolean thumbready;
+ public boolean thumbready;
/**
* @deprecated Use standard getter and setter to access this variable.
@@ -278,13 +285,16 @@ public String getExtrasAsString() {
if (extras == null) {
return null;
}
+
StringBuilder sb = new StringBuilder();
+
for (Map.Entry<String, String> entry : extras.entrySet()) {
sb.append(entry.getKey());
sb.append("|");
sb.append(entry.getValue());
sb.append("|");
}
+
return sb.toString();
}
@@ -295,7 +305,7 @@ public void setExtrasAsString(String value) {
try {
putExtra(st.nextToken(), st.nextToken());
} catch (NoSuchElementException nsee) {
- logger.debug("Caught exception", nsee);
+ LOGGER.debug("Caught exception", nsee);
}
}
}
@@ -316,17 +326,21 @@ private ProcessWrapperImpl getFFMpegThumbnail(InputFile media) {
String args[] = new String[14];
args[0] = getFfmpegPath();
boolean dvrms = media.getFile() != null && media.getFile().getAbsolutePath().toLowerCase().endsWith("dvr-ms");
+
if (dvrms && StringUtils.isNotBlank(PMS.getConfiguration().getFfmpegAlternativePath())) {
args[0] = PMS.getConfiguration().getFfmpegAlternativePath();
}
+
args[1] = "-ss";
args[2] = "" + PMS.getConfiguration().getThumbnailSeekPos();
args[3] = "-i";
+
if (media.getFile() != null) {
args[4] = ProcessUtil.getShortFileNameIfWideChars(media.getFile().getAbsolutePath());
} else {
args[4] = "-";
}
+
args[5] = "-an";
args[6] = "-an";
args[7] = "-s";
@@ -336,6 +350,7 @@ private ProcessWrapperImpl getFFMpegThumbnail(InputFile media) {
args[11] = "-f";
args[12] = "image2";
args[13] = "pipe:";
+
// FIXME MPlayer should not be used if thumbnail generation is disabled (and it should be disabled in the GUI)
if (!PMS.getConfiguration().isThumbnailGenerationEnabled() || (PMS.getConfiguration().isUseMplayerForVideoThumbs() && !dvrms)) {
args[2] = "0";
@@ -343,12 +358,15 @@ private ProcessWrapperImpl getFFMpegThumbnail(InputFile media) {
args[i] = "-an";
}
}
+
OutputParams params = new OutputParams(PMS.getConfiguration());
params.maxBufferSize = 1;
params.stdin = media.getPush();
params.noexitcheck = true; // not serious if anything happens during the thumbnailer
+
// true: consume stderr on behalf of the caller i.e. parse()
final ProcessWrapperImpl pw = new ProcessWrapperImpl(args, params, false, true);
+
// FAILSAFE
setParsing(true);
Runnable r = new Runnable() {
@@ -361,6 +379,7 @@ public void run() {
setParsing(false);
}
};
+
Thread failsafe = new Thread(r, "FFMpeg Thumbnail Failsafe");
failsafe.start();
pw.runInSameThread();
@@ -375,11 +394,13 @@ private ProcessWrapperImpl getMplayerThumbnail(InputFile media) throws IOExcepti
boolean toolong = getDurationInSeconds() < PMS.getConfiguration().getThumbnailSeekPos();
args[2] = "" + (toolong ? (getDurationInSeconds() / 2) : PMS.getConfiguration().getThumbnailSeekPos());
args[3] = "-quiet";
+
if (media.getFile() != null) {
args[4] = ProcessUtil.getShortFileNameIfWideChars(media.getFile().getAbsolutePath());
} else {
args[4] = "-";
}
+
args[5] = "-msglevel";
args[6] = "all=4";
args[7] = "-vf";
@@ -399,6 +420,7 @@ private ProcessWrapperImpl getMplayerThumbnail(InputFile media) throws IOExcepti
params.log = true;
params.noexitcheck = true; // not serious if anything happens during the thumbnailer
final ProcessWrapperImpl pw = new ProcessWrapperImpl(args, params);
+
// FAILSAFE
setParsing(true);
Runnable r = new Runnable() {
@@ -411,6 +433,7 @@ public void run() {
setParsing(false);
}
};
+
Thread failsafe = new Thread(r, "MPlayer Thumbnail Failsafe");
failsafe.start();
pw.runInSameThread();
@@ -420,15 +443,16 @@ public void run() {
private String getFfmpegPath() {
String value = PMS.getConfiguration().getFfmpegPath();
+
if (value == null) {
- logger.info("No ffmpeg - unable to thumbnail");
+ LOGGER.info("No ffmpeg - unable to thumbnail");
throw new RuntimeException("No ffmpeg - unable to thumbnail");
} else {
return value;
}
}
- public void parse(InputFile f, Format ext, int type, boolean thumbOnly) {
+ public void parse(InputFile inputFile, Format ext, int type, boolean thumbOnly) {
int i = 0;
while (isParsing()) {
@@ -448,11 +472,11 @@ public void parse(InputFile f, Format ext, int type, boolean thumbOnly) {
return;
}
- if (f != null) {
- if (f.getFile() != null) {
- setSize(f.getFile().length());
+ if (inputFile != null) {
+ if (inputFile.getFile() != null) {
+ setSize(inputFile.getFile().length());
} else {
- setSize(f.getSize());
+ setSize(inputFile.getSize());
}
ProcessWrapperImpl pw = null;
@@ -461,20 +485,25 @@ public void parse(InputFile f, Format ext, int type, boolean thumbOnly) {
if (type == Format.AUDIO || ext instanceof AudioAsVideo) {
ffmpeg_parsing = false;
DLNAMediaAudio audio = new DLNAMediaAudio();
- if (f.getFile() != null) {
+
+ if (inputFile.getFile() != null) {
try {
- AudioFile af = AudioFileIO.read(f.getFile());
+ AudioFile af = AudioFileIO.read(inputFile.getFile());
AudioHeader ah = af.getAudioHeader();
+
if (ah != null && !thumbOnly) {
int length = ah.getTrackLength();
int rate = ah.getSampleRateAsNumber();
+
if (ah.getEncodingType().toLowerCase().contains("flac 24")) {
audio.setBitsperSample(24);
}
+
audio.setSampleFrequency("" + rate);
setDuration((double) length);
setBitrate((int) ah.getBitRateAsNumber());
audio.getAudioProperties().setNumberOfChannels(2);
+
if (ah.getChannels() != null && ah.getChannels().toLowerCase().contains("mono")) {
audio.getAudioProperties().setNumberOfChannels(1);
} else if (ah.getChannels() != null && ah.getChannels().toLowerCase().contains("stereo")) {
@@ -482,7 +511,9 @@ public void parse(InputFile f, Format ext, int type, boolean thumbOnly) {
} else if (ah.getChannels() != null) {
audio.getAudioProperties().setNumberOfChannels(Integer.parseInt(ah.getChannels()));
}
+
audio.setCodecA(ah.getEncodingType().toLowerCase());
+
if (audio.getCodecA().contains("(windows media")) {
audio.setCodecA(audio.getCodecA().substring(0, audio.getCodecA().indexOf("(windows media")).trim());
}
@@ -495,14 +526,23 @@ public void parse(InputFile f, Format ext, int type, boolean thumbOnly) {
setThumb(t.getArtworkList().get(0).getBinaryData());
} else {
if (PMS.getConfiguration().getAudioThumbnailMethod() > 0) {
- setThumb(CoverUtil.get().getThumbnailFromArtistAlbum(PMS.getConfiguration().getAudioThumbnailMethod() == 1 ? CoverUtil.AUDIO_AMAZON : CoverUtil.AUDIO_DISCOGS, audio.getArtist(), audio.getAlbum()));
+ setThumb(
+ CoverUtil.get().getThumbnailFromArtistAlbum(
+ PMS.getConfiguration().getAudioThumbnailMethod() == 1 ?
+ CoverUtil.AUDIO_AMAZON :
+ CoverUtil.AUDIO_DISCOGS,
+ audio.getArtist(), audio.getAlbum()
+ )
+ );
}
}
+
if (!thumbOnly) {
audio.setAlbum(t.getFirst(FieldKey.ALBUM));
audio.setArtist(t.getFirst(FieldKey.ARTIST));
audio.setSongname(t.getFirst(FieldKey.TITLE));
String y = t.getFirst(FieldKey.YEAR);
+
try {
if (y.length() > 4) {
y = y.substring(0, 4);
@@ -512,17 +552,17 @@ public void parse(InputFile f, Format ext, int type, boolean thumbOnly) {
audio.setTrack(Integer.parseInt(((y != null && y.length() > 0) ? y : "1")));
audio.setGenre(t.getFirst(FieldKey.GENRE));
} catch (Throwable e) {
- logger.debug("Error parsing unimportant metadata: " + e.getMessage());
+ LOGGER.debug("Error parsing unimportant metadata: " + e.getMessage());
}
}
}
} catch (Throwable e) {
- logger.debug("Error parsing audio file: " + e.getMessage() + " - " + (e.getCause() != null ? e.getCause().getMessage() : ""));
+ LOGGER.debug("Error parsing audio file: {} - {}", e.getMessage(), e.getCause() != null ? e.getCause().getMessage() : "");
ffmpeg_parsing = false;
}
if (audio.getSongname() == null || audio.getSongname().length() == 0) {
- audio.setSongname(f.getFile().getName());
+ audio.setSongname(inputFile.getFile().getName());
}
if (!ffmpeg_parsing) {
@@ -531,20 +571,23 @@ public void parse(InputFile f, Format ext, int type, boolean thumbOnly) {
}
}
- if (type == Format.IMAGE && f.getFile() != null) {
+ if (type == Format.IMAGE && inputFile.getFile() != null) {
try {
ffmpeg_parsing = false;
- ImageInfo info = Sanselan.getImageInfo(f.getFile());
+ ImageInfo info = Sanselan.getImageInfo(inputFile.getFile());
setWidth(info.getWidth());
setHeight(info.getHeight());
setBitsPerPixel(info.getBitsPerPixel());
String formatName = info.getFormatName();
+
if (formatName.startsWith("JPEG")) {
setCodecV("jpg");
- IImageMetadata meta = Sanselan.getMetadata(f.getFile());
+ IImageMetadata meta = Sanselan.getMetadata(inputFile.getFile());
+
if (meta != null && meta instanceof JpegImageMetadata) {
JpegImageMetadata jpegmeta = (JpegImageMetadata) meta;
TiffField tf = jpegmeta.findEXIFValue(TiffConstants.EXIF_TAG_MODEL);
+
if (tf != null) {
setModel(tf.getStringValue().trim());
}
@@ -573,65 +616,71 @@ public void parse(InputFile f, Format ext, int type, boolean thumbOnly) {
} else if (formatName.startsWith("TIF")) {
setCodecV("tiff");
}
+
setContainer(getCodecV());
} catch (Throwable e) {
// ffmpeg_parsing = true;
- logger.info("Error parsing image with Sanselan... switching to FFmpeg: " + e.getMessage());
+ LOGGER.info("Error parsing image ({}) with Sanselan, switching to FFmpeg", inputFile.getFile().getAbsolutePath(), e);
}
+ }
+ if (PMS.getConfiguration().getImageThumbnailsEnabled()) {
try {
- if (PMS.getConfiguration().getImageThumbnailsEnabled()) {
- File thumbDir = new File(PMS.getConfiguration().getTempFolder() + "/thumbs/");
+ File thumbDir = new File(PMS.getConfiguration().getTempFolder(), THUMBNAIL_DIRECTORY_NAME);
- if (!thumbDir.exists() && !thumbDir.mkdirs()) {
- logger.debug("Could not create directory \"" + thumbDir.getAbsolutePath() + "\"");
- } else {
- String thumbFilename = thumbDir + f.getFile().getName() + ".jpg";
+ LOGGER.trace("Generating thumbnail for: {}", inputFile.getFile().getAbsolutePath());
- logger.trace("Creating thumbnail \"" + thumbFilename + "\"");
+ if (!thumbDir.exists() && !thumbDir.mkdirs()) {
+ LOGGER.warn("Could not create thumbnail directory: {}", thumbDir.getAbsolutePath());
+ } else {
+ File thumbFile = new File(thumbDir, inputFile.getFile().getName() + ".jpg");
+ String thumbFilename = thumbFile.getAbsolutePath();
- // Create the thumbnail image using the Thumbnailator library
- final Builder<File> thumbnail = Thumbnails.of(f.getFile());
- thumbnail.size(320, 180);
- thumbnail.outputFormat("jpg");
- thumbnail.outputQuality(1.0f);
- thumbnail.toFile(thumbFilename);
+ LOGGER.trace("Creating (temporary) thumbnail: {}", thumbFilename);
- File jpg = new File(thumbFilename);
+ // Create the thumbnail image using the Thumbnailator library
+ final Builder<File> thumbnail = Thumbnails.of(inputFile.getFile());
+ thumbnail.size(320, 180);
+ thumbnail.outputFormat("jpg");
+ thumbnail.outputQuality(1.0f);
+ thumbnail.toFile(thumbFilename);
- if (jpg.exists()) {
- InputStream is = new FileInputStream(jpg);
- int sz = is.available();
+ File jpg = new File(thumbFilename);
- if (sz > 0) {
- setThumb(new byte[sz]);
- is.read(getThumb());
- }
+ if (jpg.exists()) {
+ InputStream is = new FileInputStream(jpg);
+ int sz = is.available();
- is.close();
+ if (sz > 0) {
+ setThumb(new byte[sz]);
+ is.read(getThumb());
+ }
- if (!jpg.delete()) {
- jpg.deleteOnExit();
- }
+ is.close();
+
+ if (!jpg.delete()) {
+ jpg.deleteOnExit();
}
}
}
+ } catch (UnsupportedFormatException ufe) {
+ LOGGER.warn("Can't create thumbnail for {}: {}", inputFile.getFile().getAbsolutePath(), ufe.getMessage());
} catch (Exception e) {
- logger.info("Error generating thumbnail of image", e);
+ LOGGER.warn("Error generating thumbnail for: {}", inputFile.getFile().getAbsolutePath(), e);
}
}
if (ffmpeg_parsing) {
if (!thumbOnly || !PMS.getConfiguration().isUseMplayerForVideoThumbs()) {
- pw = getFFMpegThumbnail(f);
+ pw = getFFMpegThumbnail(inputFile);
}
String input = "-";
boolean dvrms = false;
- if (f.getFile() != null) {
- input = ProcessUtil.getShortFileNameIfWideChars(f.getFile().getAbsolutePath());
- dvrms = f.getFile().getAbsolutePath().toLowerCase().endsWith("dvr-ms");
+ if (inputFile.getFile() != null) {
+ input = ProcessUtil.getShortFileNameIfWideChars(inputFile.getFile().getAbsolutePath());
+ dvrms = inputFile.getFile().getAbsolutePath().toLowerCase().endsWith("dvr-ms");
}
if (!ffmpeg_failure && !thumbOnly) {
@@ -673,7 +722,7 @@ public void parse(InputFile f, Format ext, int type, boolean thumbOnly) {
} else {
setDuration(parseDurationString(durationStr));
}
-
+
} else if (token.startsWith("bitrate: ")) {
String bitr = token.substring(9);
int spacepos = bitr.indexOf(" ");
@@ -709,7 +758,7 @@ public void parse(InputFile f, Format ext, int type, boolean thumbOnly) {
try {
audio.setId(Integer.parseInt(idString, 16));
} catch (NumberFormatException nfe) {
- logger.debug("Error parsing Stream ID: " + idString);
+ LOGGER.debug("Error parsing Stream ID: " + idString);
}
}
@@ -777,7 +826,7 @@ public void parse(InputFile f, Format ext, int type, boolean thumbOnly) {
}
} catch (NumberFormatException nfe) {
// Could happen if tbc is "1k" or something like that, no big deal
- logger.debug("Could not parse frame rate \"" + frameRateDoubleString + "\"");
+ LOGGER.debug("Could not parse frame rate \"" + frameRateDoubleString + "\"");
}
} else if ((token.indexOf("tbr") > -1 || token.indexOf("tb(r)") > -1) && getFrameRate() == null) {
@@ -792,12 +841,12 @@ public void parse(InputFile f, Format ext, int type, boolean thumbOnly) {
try {
setWidth(Integer.parseInt(resolution.substring(0, resolution.indexOf("x"))));
} catch (NumberFormatException nfe) {
- logger.debug("Could not parse width from \"" + resolution.substring(0, resolution.indexOf("x")) + "\"");
+ LOGGER.debug("Could not parse width from \"" + resolution.substring(0, resolution.indexOf("x")) + "\"");
}
try {
setHeight(Integer.parseInt(resolution.substring(resolution.indexOf("x") + 1)));
} catch (NumberFormatException nfe) {
- logger.debug("Could not parse height from \"" + resolution.substring(resolution.indexOf("x") + 1) + "\"");
+ LOGGER.debug("Could not parse height from \"" + resolution.substring(resolution.indexOf("x") + 1) + "\"");
}
}
}
@@ -843,22 +892,22 @@ public void parse(InputFile f, Format ext, int type, boolean thumbOnly) {
}
}
- if (!thumbOnly && getContainer() != null && f.getFile() != null && getContainer().equals("mpegts") && isH264() && getDurationInSeconds() == 0) {
+ if (!thumbOnly && getContainer() != null && inputFile.getFile() != null && getContainer().equals("mpegts") && isH264() && getDurationInSeconds() == 0) {
// let's do the parsing for getting the duration...
try {
- int length = MpegUtil.getDurationFromMpeg(f.getFile());
+ int length = MpegUtil.getDurationFromMpeg(inputFile.getFile());
if (length > 0) {
setDuration((double) length);
}
} catch (IOException e) {
- logger.trace("Error retrieving length: " + e.getMessage());
+ LOGGER.trace("Error retrieving length: " + e.getMessage());
}
}
if (PMS.getConfiguration().isUseMplayerForVideoThumbs() && type == Format.VIDEO && !dvrms) {
try {
- getMplayerThumbnail(f);
- String frameName = "" + f.hashCode();
+ getMplayerThumbnail(inputFile);
+ String frameName = "" + inputFile.hashCode();
frameName = PMS.getConfiguration().getTempFolder() + "/mplayer_thumbs/" + frameName + "00000001/00000001.jpg";
frameName = frameName.replace(',', '_');
File jpg = new File(frameName);
@@ -880,11 +929,11 @@ public void parse(InputFile f, Format ext, int type, boolean thumbOnly) {
// Try and retry
if (!jpg.getParentFile().delete() && !jpg.getParentFile().delete()) {
- logger.debug("Failed to delete \"" + jpg.getParentFile().getAbsolutePath() + "\"");
+ LOGGER.debug("Failed to delete \"" + jpg.getParentFile().getAbsolutePath() + "\"");
}
}
} catch (IOException e) {
- logger.debug("Caught exception", e);
+ LOGGER.debug("Caught exception", e);
}
}
@@ -919,12 +968,12 @@ public void parse(InputFile f, Format ext, int type, boolean thumbOnly) {
}
}
} catch (IOException e) {
- logger.debug("Error while decoding thumbnail: " + e.getMessage());
+ LOGGER.debug("Error while decoding thumbnail: " + e.getMessage());
}
}
}
- finalize(type, f);
+ finalize(type, inputFile);
setMediaparsed(true);
}
}
@@ -947,7 +996,7 @@ public Double getDuration() {
}
/**
- *
+ *
* @return 0 if nothing is specified, otherwise the duration
*/
public double getDurationInSeconds() {
@@ -978,7 +1027,7 @@ public static Double parseDurationString(String duration) {
double s = Double.parseDouble(st.nextToken());
return h * 3600 + m * 60 + s;
} catch (NumberFormatException nfe) {
- logger.debug("Failed to parse duration \"" + duration + "\"");
+ LOGGER.debug("Failed to parse duration \"" + duration + "\"");
}
return null;
@@ -1040,7 +1089,7 @@ public boolean isVideoPS3Compatible(InputFile f) {
if (getCodecV().equals("h264") && getContainer() != null && (getContainer().equals("matroska") || getContainer().equals("mkv") || getContainer().equals("mov") || getContainer().equals("mp4"))) { // containers without h264_annexB
byte headers[][] = getAnnexBFrameHeader(f);
if (ffmpeg_annexb_failure) {
- logger.info("Fatal error when retrieving AVC informations !");
+ LOGGER.info("Fatal error when retrieving AVC informations !");
}
if (headers != null) {
@@ -1054,7 +1103,7 @@ public boolean isVideoPS3Compatible(InputFile f) {
System.arraycopy(getH264AnnexB(), skip, header, 0, header.length);
AVCHeader avcHeader = new AVCHeader(header);
avcHeader.parse();
- logger.trace("H264 file: " + f.getFilename() + ": Profile: " + avcHeader.getProfile() + " / level: " + avcHeader.getLevel() + " / ref frames: " + avcHeader.getRef_frames());
+ LOGGER.trace("H264 file: " + f.getFilename() + ": Profile: " + avcHeader.getProfile() + " / level: " + avcHeader.getLevel() + " / ref frames: " + avcHeader.getRef_frames());
muxable = true;
// Check if file is compliant with Level4.1
@@ -1065,7 +1114,7 @@ public boolean isVideoPS3Compatible(InputFile f) {
}
}
if (!muxable) {
- logger.debug("H264 file: " + f.getFilename() + " is not ps3 compatible !");
+ LOGGER.debug("H264 file: " + f.getFilename() + " is not ps3 compatible !");
}
} else {
muxable = false;
@@ -1092,15 +1141,18 @@ public boolean isLossless(String codecA) {
public String toString() {
String s = "container: " + getContainer() + " / bitrate: " + getBitrate() + " / size: " + getSize() + " / codecV: " + getCodecV() + " / duration: " + getDurationString() + " / width: " + getWidth() + " / height: " + getHeight() + " / frameRate: " + getFrameRate() + " / thumb size : " + (getThumb() != null ? getThumb().length : 0) + " / muxingMode: " + getMuxingMode();
+
for (DLNAMediaAudio audio : getAudioTracksList()) {
s += "\n\taudio: id=" + audio.getId() + " / lang: " + audio.getLang() + " / flavor: " + audio.getFlavor() + " / codec: " + audio.getCodecA() + " / sf:" + audio.getSampleFrequency() + " / na: " + (audio.getAudioProperties() != null ? audio.getAudioProperties().getNumberOfChannels() : "-") + " / bs: " + audio.getBitsperSample();
if (audio.getArtist() != null) {
s += " / " + audio.getArtist() + "|" + audio.getAlbum() + "|" + audio.getSongname() + "|" + audio.getYear() + "|" + audio.getTrack();
}
}
+
for (DLNAMediaSubtitle sub : getSubtitleTracksList()) {
s += "\n\tsub: id=" + sub.getId() + " / lang: " + sub.getLang() + " / flavor: " + sub.getFlavor() + " / type: " + (sub.getType() != null ? sub.getType().toString() : "null");
}
+
return s;
}
@@ -1137,7 +1189,7 @@ public String getValidFps(boolean ratios) {
validFrameRate = "60";
}
} catch (NumberFormatException nfe) {
- logger.error(null, nfe);
+ LOGGER.error(null, nfe);
}
}
@@ -1291,7 +1343,7 @@ public void run() {
returnData[1] = header;
}
} catch (IOException e) {
- logger.debug("Caught exception", e);
+ LOGGER.debug("Caught exception", e);
}
return returnData;
View
51 src/main/java/net/pms/dlna/DLNAResource.java
@@ -467,9 +467,11 @@ public void addChild(DLNAResource child) {
boolean forceTranscodeV2 = false;
boolean parserV2 = child.getMedia() != null && getDefaultRenderer() != null && getDefaultRenderer().isMediaParserV2();
+
if (parserV2) {
// We already have useful info, just need to layout folders
String mimeType = getDefaultRenderer().getFormatConfiguration().match(child.getMedia());
+
if (mimeType != null) {
// This is streamable
child.getMedia().setMimeType(mimeType.equals(FormatConfiguration.MIMETYPE_AUTO) ? child.getMedia().getMimeType() : mimeType);
@@ -577,7 +579,7 @@ public void addChild(DLNAResource child) {
try {
((AdditionalResourceFolderListener) listener).addAdditionalFolder(this, child);
} catch (Throwable t) {
- LOGGER.error(String.format("Failed to add add additional folder for listener of type=%s", listener.getClass()), t);
+ LOGGER.error("Failed to add additional folder for listener of type: {}", listener.getClass(), t);
}
}
}
@@ -586,7 +588,12 @@ public void addChild(DLNAResource child) {
}
}
- if (child.getFormat() != null && child.getFormat().getSecondaryFormat() != null && child.getMedia() != null && getDefaultRenderer() != null && getDefaultRenderer().supportsFormat(child.getFormat().getSecondaryFormat())) {
+ if (child.getFormat() != null &&
+ child.getFormat().getSecondaryFormat() != null &&
+ child.getMedia() != null &&
+ getDefaultRenderer() != null &&
+ getDefaultRenderer().supportsFormat(child.getFormat().getSecondaryFormat())
+ ) {
DLNAResource newChild = child.clone();
newChild.setFormat(newChild.getFormat().getSecondaryFormat());
newChild.first = child;
@@ -603,8 +610,7 @@ public void addChild(DLNAResource child) {
}
}
} catch (Throwable t) {
- LOGGER.error(String.format("Failed to add child '%s'", child.getName()), t);
-
+ LOGGER.error("Error adding child: {}", child.getName(), t);
child.setParent(null);
getChildren().remove(child);
}
@@ -706,10 +712,16 @@ protected synchronized void addChildInternal(DLNAResource child) {
int parallel_thread_number = 3;
if (resource instanceof DVDISOFile) {
- parallel_thread_number = 1; // my dvd drive is dying wih 3 parallel threads
+ parallel_thread_number = 1; // my DVD drive is dying wih 3 parallel threads
}
- ThreadPoolExecutor tpe = new ThreadPoolExecutor(Math.min(count, parallel_thread_number), count, 20, TimeUnit.SECONDS, queue);
+ ThreadPoolExecutor tpe = new ThreadPoolExecutor(
+ Math.min(count, parallel_thread_number),
+ count,
+ 20,
+ TimeUnit.SECONDS,
+ queue
+ );
for (int i = start; i < start + count; i++) {
if (i < resource.getChildren().size()) {
@@ -752,10 +764,11 @@ protected void notifyRefresh() {
}
final protected void discoverWithRenderer(RendererConfiguration renderer, int count, boolean forced) {
- // Discovering if not already done.
+ // discover children if it hasn't been done already
if (!isDiscovered()) {
discoverChildren();
boolean ready = true;
+
if (renderer.isMediaParserV2() && renderer.isDLNATreeHack()) {
ready = analyzeChildren(count);
} else {
@@ -1626,7 +1639,7 @@ public void startPlaying(final String rendererId) {
Runnable r = new Runnable() {
@Override
public void run() {
- LOGGER.info(String.format("renderer: %s, file: %s", rendererId, getSystemName()));
+ LOGGER.info("renderer: {}, file: {}", rendererId, getSystemName());
for (final ExternalListener listener : ExternalFactory.getExternalListeners()) {
if (listener instanceof StartStopListener) {
@@ -1637,7 +1650,7 @@ public void run() {
try {
((StartStopListener) listener).nowPlaying(getMedia(), self);
} catch (Throwable t) {
- LOGGER.error(String.format("Notification of startPlaying event failed for StartStopListener %s", listener.getClass()), t);
+ LOGGER.error("Notification of startPlaying event failed for StartStopListener {}", listener.getClass(), t);
}
}
};
@@ -1679,7 +1692,7 @@ public void run() {
@Override
public void run() {
if (refCount == 1) {
- LOGGER.info(String.format("renderer: %s, file: %s", rendererId, getSystemName()));
+ LOGGER.info("renderer: {}, file: {}", rendererId, getSystemName());
PMS.get().getFrame().setStatusLine("");
for (final ExternalListener listener : ExternalFactory.getExternalListeners()) {
@@ -1691,7 +1704,7 @@ public void run() {
try {
((StartStopListener) listener).donePlaying(getMedia(), self);
} catch (Throwable t) {
- LOGGER.error(String.format("Notification of donePlaying event failed for StartStopListener %s", listener.getClass()), t);
+ LOGGER.error("Notification of donePlaying event failed for StartStopListener {}", listener.getClass(), t);
}
}
};
@@ -1942,16 +1955,20 @@ public void checkThumbnail() {
// need to override if some thumbnail work is to be done when mediaparserv2 enabled
}
- /**Checks if a thumbnail exists, and if possible, generates one.
- * @param input InputFile to check or generate the thumbnail that is being asked for.
+ /**
+ * Checks if a thumbnail exists, and, if not, generates one (if possible).
+ * Called from Request/RequestV2 in response to thumbnail requests e.g. HEAD /get/0$1$0$42$3/thumbnail0000%5BExample.mkv
+ * Calls DLNAMediaInfo.generateThumbnail, which in turn calls DLNAMediaInfo.parse.
+ *
+ * @param input InputFile to check or generate the thumbnail from.
*/
- protected void checkThumbnail(InputFile input) {
+ protected void checkThumbnail(InputFile inputFile) {
if (getMedia() != null && !getMedia().isThumbready() && PMS.getConfiguration().isThumbnailGenerationEnabled()) {
getMedia().setThumbready(true);
- getMedia().generateThumbnail(input, getFormat(), getType());
+ getMedia().generateThumbnail(inputFile, getFormat(), getType());
- if (getMedia().getThumb() != null && PMS.getConfiguration().getUseCache() && input.getFile() != null) {
- PMS.get().getDatabase().updateThumbnail(input.getFile().getAbsolutePath(), input.getFile().lastModified(), getType(), getMedia());
+ if (getMedia().getThumb() != null && PMS.getConfiguration().getUseCache() && inputFile.getFile() != null) {
+ PMS.get().getDatabase().updateThumbnail(inputFile.getFile().getAbsolutePath(), inputFile.getFile().lastModified(), getType(), getMedia());
}
}
}
View
14 src/main/java/net/pms/dlna/InputFile.java
@@ -2,14 +2,14 @@
import java.io.File;
-/**
- * TODO: Change all instance variables to private. For backwards compatibility
- * with external plugin code the variables have all been marked as deprecated
- * instead of changed to private, but this will surely change in the future.
- * When everything has been changed to private, the deprecated note can be
- * removed.
- */
public class InputFile {
+ /**
+ * TODO: Change all instance variables to private. For backwards compatibility
+ * with external plugin code the variables have all been marked as deprecated
+ * rather than being changed to private, but this will change in the future.
+ * When everything has been changed to private, this note can be
+ * removed.
+ */
/**
* @deprecated Use standard getter and setter to access this variable.
View
150 src/main/java/net/pms/dlna/LibMediaInfoParser.java
@@ -2,8 +2,10 @@
import net.pms.configuration.FormatConfiguration;
import net.pms.formats.v2.SubtitleType;
+
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -11,16 +13,18 @@
import java.util.StringTokenizer;
public class LibMediaInfoParser {
- private static final Logger logger = LoggerFactory.getLogger(LibMediaInfoParser.class);
+ private static final Logger LOGGER = LoggerFactory.getLogger(LibMediaInfoParser.class);
private static MediaInfo MI;
private static Base64 base64;
static {
MI = new MediaInfo();
+
if (MI.isValid()) {
MI.Option("Complete", "1");
MI.Option("Language", "raw");
}
+
base64 = new Base64();
}
@@ -32,84 +36,90 @@ public static void close() {
try {
MI.finalize();
} catch (Throwable e) {
- logger.debug("Caught exception", e);
+ LOGGER.debug("Caught exception", e);
}
}
- public synchronized static void parse(DLNAMediaInfo media, InputFile file, int type) {
- File filename = file.getFile();
- if (!media.isMediaparsed() && filename != null && MI.isValid() && MI.Open(filename.getAbsolutePath()) > 0) {
+ public synchronized static void parse(DLNAMediaInfo media, InputFile inputFile, int type) {
+ File file = inputFile.getFile();
+
+ if (!media.isMediaparsed() && file != null && MI.isValid() && MI.Open(file.getAbsolutePath()) > 0) {
try {
String info = MI.Inform();
- MediaInfo.StreamKind step = MediaInfo.StreamKind.General;
+ MediaInfo.StreamKind streamType = MediaInfo.StreamKind.General;
DLNAMediaAudio currentAudioTrack = new DLNAMediaAudio();
boolean audioPrepped = false;
DLNAMediaSubtitle currentSubTrack = new DLNAMediaSubtitle();
boolean subPrepped = false;
+
if (StringUtils.isNotBlank(info)) {
- media.setSize(filename.length());
+ media.setSize(file.length());
StringTokenizer st = new StringTokenizer(info, "\n\r");
+
while (st.hasMoreTokens()) {
String line = st.nextToken().trim();
if (line.equals("Video") || line.startsWith("Video #")) {
- step = MediaInfo.StreamKind.Video;
+ streamType = MediaInfo.StreamKind.Video;
} else if (line.equals("Audio") || line.startsWith("Audio #")) {
if (audioPrepped) {
addAudio(currentAudioTrack, media);
currentAudioTrack = new DLNAMediaAudio();
}
audioPrepped = true;
- step = MediaInfo.StreamKind.Audio;
+ streamType = MediaInfo.StreamKind.Audio;
} else if (line.equals("Text") || line.startsWith("Text #")) {
if (subPrepped) {
addSub(currentSubTrack, media);
currentSubTrack = new DLNAMediaSubtitle();
}
subPrepped = true;
- step = MediaInfo.StreamKind.Text;
+ streamType = MediaInfo.StreamKind.Text;
} else if (line.equals("Menu") || line.startsWith("Menu #")) {
- step = MediaInfo.StreamKind.Menu;
+ streamType = MediaInfo.StreamKind.Menu;
} else if (line.equals("Chapters")) {
- step = MediaInfo.StreamKind.Chapters;
+ streamType = MediaInfo.StreamKind.Chapters;
}
+
int point = line.indexOf(":");
+
if (point > -1) {
String key = line.substring(0, point).trim();
String ovalue = line.substring(point + 1).trim();
String value = ovalue.toLowerCase();
+
if (key.equals("Format") || key.startsWith("Format_Version") || key.startsWith("Format_Profile")) {
- if (step == MediaInfo.StreamKind.Text) {
+ if (streamType == MediaInfo.StreamKind.Text) {
// first attempt to detect subtitle track format
currentSubTrack.setType(SubtitleType.valueOfLibMediaInfoCodec(value));
} else {
- getFormat(step, media, currentAudioTrack, value);
+ getFormat(streamType, media, currentAudioTrack, value, file);
}
- } else if (key.equals("Duration/String1") && step == MediaInfo.StreamKind.General) {
+ } else if (key.equals("Duration/String1") && streamType == MediaInfo.StreamKind.General) {
media.setDuration(getDuration(value));
- } else if (key.equals("Codec_Settings_QPel") && step == MediaInfo.StreamKind.Video) {
+ } else if (key.equals("Codec_Settings_QPel") && streamType == MediaInfo.StreamKind.Video) {
media.putExtra(FormatConfiguration.MI_QPEL, value);
- } else if (key.equals("Codec_Settings_GMC") && step == MediaInfo.StreamKind.Video) {
+ } else if (key.equals("Codec_Settings_GMC") && streamType == MediaInfo.StreamKind.Video) {
media.putExtra(FormatConfiguration.MI_GMC, value);
- } else if (key.equals("MuxingMode") && step == MediaInfo.StreamKind.Video) {
+ } else if (key.equals("MuxingMode") && streamType == MediaInfo.StreamKind.Video) {
media.setMuxingMode(ovalue);
} else if (key.equals("CodecID")) {
- if (step == MediaInfo.StreamKind.Text) {
+ if (streamType == MediaInfo.StreamKind.Text) {
// second attempt to detect subtitle track format (CodecID usually is more accurate)
currentSubTrack.setType(SubtitleType.valueOfLibMediaInfoCodec(value));
} else {
- getFormat(step, media, currentAudioTrack, value);
+ getFormat(streamType, media, currentAudioTrack, value, file);
}
} else if (key.equals("Language/String")) {
- if (step == MediaInfo.StreamKind.Audio) {
+ if (streamType == MediaInfo.StreamKind.Audio) {
currentAudioTrack.setLang(getLang(value));
- } else if (step == MediaInfo.StreamKind.Text) {
+ } else if (streamType == MediaInfo.StreamKind.Text) {
currentSubTrack.setLang(getLang(value));
}
} else if (key.equals("Title")) {
- if (step == MediaInfo.StreamKind.Audio) {
+ if (streamType == MediaInfo.StreamKind.Audio) {
currentAudioTrack.setFlavor(getFlavor(value));
- } else if (step == MediaInfo.StreamKind.Text) {
+ } else if (streamType == MediaInfo.StreamKind.Text) {
currentSubTrack.setFlavor(getFlavor(value));
}
} else if (key.equals("Width")) {
@@ -123,83 +133,86 @@ public synchronized static void parse(DLNAMediaInfo media, InputFile file, int t
} else if (key.equals("FrameRateMode")) {
media.setFrameRateMode(getFrameRateModeValue(value));
} else if (key.equals("OverallBitRate")) {
- if (step == MediaInfo.StreamKind.General) {
+ if (streamType == MediaInfo.StreamKind.General) {
media.setBitrate(getBitrate(value));
}
} else if (key.equals("Channel(s)")) {
- if (step == MediaInfo.StreamKind.Audio) {
+ if (streamType == MediaInfo.StreamKind.Audio) {
currentAudioTrack.getAudioProperties().setNumberOfChannels(value);
}
} else if (key.equals("BitRate")) {
- if (step == MediaInfo.StreamKind.Audio) {
+ if (streamType == MediaInfo.StreamKind.Audio) {
currentAudioTrack.setBitRate(getBitrate(value));
}
} else if (key.equals("SamplingRate")) {
- if (step == MediaInfo.StreamKind.Audio) {
+ if (streamType == MediaInfo.StreamKind.Audio) {
currentAudioTrack.setSampleFrequency(getSampleFrequency(value));
}
} else if (key.equals("ID/String")) {
// Special check for OGM: MediaInfo reports specific Audio/Subs IDs (0xn) while mencoder does not
if (value.contains("(0x") && !FormatConfiguration.OGG.equals(media.getContainer())) {
- if (step == MediaInfo.StreamKind.Audio) {
+ if (streamType == MediaInfo.StreamKind.Audio) {
currentAudioTrack.setId(getSpecificID(value));
- } else if (step == MediaInfo.StreamKind.Text) {
+ } else if (streamType == MediaInfo.StreamKind.Text) {
currentSubTrack.setId(getSpecificID(value));
}
} else {
- if (step == MediaInfo.StreamKind.Audio) {
+ if (streamType == MediaInfo.StreamKind.Audio) {
currentAudioTrack.setId(media.getAudioTracksList().size());
- } else if (step == MediaInfo.StreamKind.Text) {
+ } else if (streamType == MediaInfo.StreamKind.Text) {
currentSubTrack.setId(media.getSubtitleTracksList().size());
}
}
- } else if (key.equals("Cover_Data") && step == MediaInfo.StreamKind.General) {
+ } else if (key.equals("Cover_Data") && streamType == MediaInfo.StreamKind.General) {
media.setThumb(getCover(ovalue));
- } else if (key.equals("Track") && step == MediaInfo.StreamKind.General) {
+ } else if (key.equals("Track") && streamType == MediaInfo.StreamKind.General) {
currentAudioTrack.setSongname(ovalue);
- } else if (key.equals("Album") && step == MediaInfo.StreamKind.General) {
+ } else if (key.equals("Album") && streamType == MediaInfo.StreamKind.General) {
currentAudioTrack.setAlbum(ovalue);
- } else if (key.equals("Performer") && step == MediaInfo.StreamKind.General) {
+ } else if (key.equals("Performer") && streamType == MediaInfo.StreamKind.General) {
currentAudioTrack.setArtist(ovalue);
- } else if (key.equals("Genre") && step == MediaInfo.StreamKind.General) {
+ } else if (key.equals("Genre") && streamType == MediaInfo.StreamKind.General) {
currentAudioTrack.setGenre(ovalue);
- } else if (key.equals("Recorded_Date") && step == MediaInfo.StreamKind.General) {
+ } else if (key.equals("Recorded_Date") && streamType == MediaInfo.StreamKind.General) {
try {
currentAudioTrack.setYear(Integer.parseInt(value));
} catch (NumberFormatException nfe) {
- logger.debug("Could not parse year \"" + value + "\"");
+ LOGGER.debug("Could not parse year \"" + value + "\"");
}
- } else if (key.equals("Track/Position") && step == MediaInfo.StreamKind.General) {
+ } else if (key.equals("Track/Position") && streamType == MediaInfo.StreamKind.General) {
try {
currentAudioTrack.setTrack(Integer.parseInt(value));
} catch (NumberFormatException nfe) {
- logger.debug("Could not parse track \"" + value + "\"");
+ LOGGER.debug("Could not parse track \"" + value + "\"");
}
- } else if (key.equals("Resolution") && step == MediaInfo.StreamKind.Audio) {
+ } else if (key.equals("Resolution") && streamType == MediaInfo.StreamKind.Audio) {
try {
currentAudioTrack.setBitsperSample(Integer.parseInt(value));
} catch (NumberFormatException nfe) {
- logger.debug("Could not parse bits per sample \"" + value + "\"");
+ LOGGER.debug("Could not parse bits per sample \"" + value + "\"");
}
- } else if (key.equals("Video_Delay") && step == MediaInfo.StreamKind.Audio) {
+ } else if (key.equals("Video_Delay") && streamType == MediaInfo.StreamKind.Audio) {
try {
currentAudioTrack.getAudioProperties().setAudioDelay(value);
} catch (NumberFormatException nfe) {
- logger.debug("Could not parse delay \"" + value + "\"");
+ LOGGER.debug("Could not parse delay \"" + value + "\"");
}
}
}
}
}
+
if (audioPrepped) {
addAudio(currentAudioTrack, media);
}
+
if (subPrepped) {
addSub(currentSubTrack, media);
}
- media.finalize(type, file);
+
+ media.finalize(type, inputFile);
} catch (Exception e) {
- logger.error("Error in MediaInfo parsing:", e);
+ LOGGER.error("Error in MediaInfo parsing:", e);
} finally {
MI.Close();
if (media.getContainer() == null) {
@@ -219,9 +232,11 @@ public static void addAudio(DLNAMediaAudio currentAudioTrack, DLNAMediaInfo medi
if (currentAudioTrack.getLang() == null) {
currentAudioTrack.setLang(DLNAMediaLang.UND);
}
+
if (currentAudioTrack.getCodecA() == null) {
currentAudioTrack.setCodecA(DLNAMediaLang.UND);
}
+
media.getAudioTracksList().add(currentAudioTrack);
}
@@ -229,14 +244,23 @@ public static void addSub(DLNAMediaSubtitle currentSubTrack, DLNAMediaInfo media
if (currentSubTrack.getType() == SubtitleType.UNSUPPORTED) {
return;
}
+
if (currentSubTrack.getLang() == null) {
currentSubTrack.setLang(DLNAMediaLang.UND);
}
+
media.getSubtitleTracksList().add(currentSubTrack);
}
- public static void getFormat(MediaInfo.StreamKind current, DLNAMediaInfo media, DLNAMediaAudio audio, String value) {
+ @Deprecated
+ // FIXME this is obsolete (replaced by the private method below) and isn't called from anywhere outside this class
+ public static void getFormat(MediaInfo.StreamKind streamType, DLNAMediaInfo media, DLNAMediaAudio audio, String value) {
+ getFormat(streamType, media, audio, value, null);
+ }
+
+ private static void getFormat(MediaInfo.StreamKind streamType, DLNAMediaInfo media, DLNAMediaAudio audio, String value, File file) {
String format = null;
+
if (value.equals("matroska")) {
format = FormatConfiguration.MATROSKA;
} else if (value.equals("avi") || value.equals("opendml")) {
@@ -350,11 +374,11 @@ public static void getFormat(MediaInfo.StreamKind current, DLNAMediaInfo media,
}
if (format != null) {
- if (current == MediaInfo.StreamKind.General) {
+ if (streamType == MediaInfo.StreamKind.General) {
media.setContainer(format);
- } else if (current == MediaInfo.StreamKind.Video) {
+ } else if (streamType == MediaInfo.StreamKind.Video) {
media.setCodecV(format);
- } else if (current == MediaInfo.StreamKind.Audio) {
+ } else if (streamType == MediaInfo.StreamKind.Audio) {
audio.setCodecA(format);
}
}
@@ -364,6 +388,7 @@ public static int getPixelValue(String value) {
if (value.indexOf("pixel") > -1) {
value = value.substring(0, value.indexOf("pixel"));
}
+
value = value.trim();
// Value can look like "512 / 512" at this point
@@ -379,10 +404,11 @@ public static int getBitrate(String value) {
if (value.contains("/")) {
value = value.substring(0, value.indexOf("/")).trim();
}
+
try {
return Integer.parseInt(value);
} catch (NumberFormatException ex) {
- logger.info("Unknown bitrate detected. Returning 0.");
+ LOGGER.info("Unknown bitrate detected. Returning 0.");
return 0;
}
}
@@ -391,6 +417,7 @@ public static int getSpecificID(String value) {
if (value.indexOf("(0x") > -1) {
value = value.substring(0, value.indexOf("(0x"));
}
+
value = value.trim();
int id = Integer.parseInt(value);
return id;
@@ -398,13 +425,15 @@ public static int getSpecificID(String value) {
public static String getSampleFrequency(String value) {
// some tracks show several values like "48000 / 48000 / 24000" for HE-AAC
- // store only first value
+ // store only the first value
if (value.indexOf("/") > -1) {
value = value.substring(0, value.indexOf("/"));
}
+
if (value.indexOf("khz") > -1) {
value = value.substring(0, value.indexOf("khz"));
}
+
value = value.trim();
return value;
}
@@ -413,6 +442,7 @@ public static String getFPSValue(String value) {
if (value.indexOf("fps") > -1) {
value = value.substring(0, value.indexOf("fps"));
}
+
value = value.trim();
return value;
}
@@ -421,6 +451,7 @@ public static String getFrameRateModeValue(String value) {
if (value.indexOf("/") > -1) {
value = value.substring(0, value.indexOf("/"));
}
+
value = value.trim();
return value;
}
@@ -429,9 +460,11 @@ public static String getLang(String value) {
if (value.indexOf("(") > -1) {
value = value.substring(0, value.indexOf("("));
}
+
if (value.indexOf("/") > -1) {
value = value.substring(0, value.indexOf("/"));
}
+
value = value.trim();
return value;
}
@@ -444,25 +477,33 @@ public static String getFlavor(String value) {
private static double getDuration(String value) {
int h = 0, m = 0, s = 0;
StringTokenizer st = new StringTokenizer(value, " ");
+
while (st.hasMoreTokens()) {
String token = st.nextToken();
int hl = token.indexOf("h");
+
if (hl > -1) {
h = Integer.parseInt(token.substring(0, hl).trim());
}
+
int mnl = token.indexOf("mn");
+
if (mnl > -1) {
m = Integer.parseInt(token.substring(0, mnl).trim());
}
+
int msl = token.indexOf("ms");
+
if (msl == -1) {
// Only check if ms was not found
int sl = token.indexOf("s");
+
if (sl > -1) {
s = Integer.parseInt(token.substring(0, sl).trim());
}
}
}
+
return (h * 3600) + (m * 60) + s;
}
@@ -472,8 +513,9 @@ private static double getDuration(String value) {
return base64.decode(based64Value.getBytes());
}
} catch (Exception e) {
- logger.error("Error in decoding thumbnail data", e);
+ LOGGER.error("Error in decoding thumbnail data", e);
}
+
return null;
}
}
View
5 src/main/java/net/pms/dlna/MapFile.java
@@ -179,7 +179,8 @@ public boolean isValid() {
public boolean analyzeChildren(int count) {
int currentChildrenCount = getChildren().size();
int vfolder = 0;
- while ((getChildren().size() - currentChildrenCount) < count || count == -1) {
+
+ while (((getChildren().size() - currentChildrenCount) < count) || (count == -1)) {
if (vfolder < getConf().getChildren().size()) {
addChild(new MapFile(getConf().getChildren().get(vfolder)));
++vfolder;
@@ -187,9 +188,11 @@ public boolean analyzeChildren(int count) {
if (discoverable.isEmpty()) {
break;
}
+
manageFile(discoverable.remove(0));
}
}
+
return discoverable.isEmpty();
}
View
20 src/main/java/net/pms/dlna/MediaInfo.java
@@ -36,7 +36,7 @@
import static java.util.Collections.singletonMap;
public class MediaInfo {
- private static final Logger logger = LoggerFactory.getLogger(MediaInfo.class);
+ private static final Logger LOGGER = LoggerFactory.getLogger(MediaInfo.class);
static String libraryName;
static {
@@ -53,7 +53,7 @@
// If we do not, the system will look for dependencies, but only in the library path.
NativeLibrary.getInstance("zen");
} catch (LinkageError e) {
- logger.warn("Error loading libzen: " + e.getMessage());
+ LOGGER.warn("Error loading libzen: " + e.getMessage());
}
}
}
@@ -96,6 +96,8 @@ public String getFunctionName(NativeLibrary lib, Method method) {
}
private Pointer Handle;
+ @Deprecated
+ // FIXME rename StreamType
public enum StreamKind {
General,
Video,
@@ -107,6 +109,8 @@ public String getFunctionName(NativeLibrary lib, Method method) {
}
// Enums
+ @Deprecated
+ // FIXME rename InfoType
public enum InfoKind {
/**
* Unique name of parameter.
@@ -147,17 +151,19 @@ public String getFunctionName(NativeLibrary lib, Method method) {
// Constructor/Destructor
public MediaInfo() {
try {
- logger.info("Loading MediaInfo library");
+ LOGGER.info("Loading MediaInfo library");
Handle = MediaInfoDLL_Internal.INSTANCE.New();
- logger.info("Loaded " + Option_Static("Info_Version"));
+ LOGGER.info("Loaded " + Option_Static("Info_Version"));
} catch (Throwable e) {
if (e != null) {
- logger.info("Error loading MediaInfo library: " + e.getMessage());
+ LOGGER.info("Error loading MediaInfo library: " + e.getMessage());
}
+
if (!Platform.isWindows() && !Platform.isMac()) {
- logger.info("Make sure you have libmediainfo and libzen installed");
+ LOGGER.info("Make sure you have libmediainfo and libzen installed");
}
- logger.info("The server will now use the less accurate ffmpeg parsing method");
+
+ LOGGER.info("The server will now use the less accurate ffmpeg parsing method");
}
}
View
40 src/main/java/net/pms/dlna/RealFile.java
@@ -31,7 +31,7 @@
import java.util.ArrayList;
public class RealFile extends MapFile {
- private static final Logger logger = LoggerFactory.getLogger(RealFile.class);
+ private static final Logger LOGGER = LoggerFactory.getLogger(RealFile.class);
public RealFile(File file) {
getConf().getFiles().add(file);
@@ -49,33 +49,40 @@ public RealFile(File file, String name) {
public boolean isValid() {
File file = this.getFile();
checktype();
+
if (getType() == Format.VIDEO && file.exists() && PMS.getConfiguration().isAutoloadSubtitles() && file.getName().length() > 4) {
setSrtFile(FileUtil.doesSubtitlesExists(file, null));
}
+
boolean valid = file.exists() && (getFormat() != null || file.isDirectory());
if (valid && getParent().getDefaultRenderer() != null && getParent().getDefaultRenderer().isMediaParserV2()) {
- // we need to resolve the dlna resource now
+ // we need to resolve the DLNA resource now
run();
- if (getMedia() != null && getMedia().getThumb() == null && getType() != Format.AUDIO) // MediaInfo retrieves cover art now
- {
+
+ if (getMedia() != null && getMedia().getThumb() == null && getType() != Format.AUDIO) { // MediaInfo retrieves cover art now
getMedia().setThumbready(false);
}
+
// Given that here getFormat() has already matched some (possibly plugin-defined) format:
// Format.UNKNOWN + bad parse = inconclusive
// known types + bad parse = bad/encrypted file
if (getType() != Format.UNKNOWN && getMedia() != null && (getMedia().isEncrypted() || getMedia().getContainer() == null || getMedia().getContainer().equals(DLNAMediaLang.UND))) {
valid = false;
+
if (getMedia().isEncrypted()) {
- logger.info("The file " + file.getAbsolutePath() + " is encrypted. It will be hidden");
+ LOGGER.info("The file {} is encrypted. It will be hidden", file.getAbsolutePath());
} else {
- logger.info("The file " + file.getAbsolutePath() + " was badly parsed. It will be hidden");
+ LOGGER.info("The file {} was badly parsed. It will be hidden", file.getAbsolutePath());
}
}
+
+ // XXX isMediaParserV2ThumbnailGeneration is only true for the "default renderer"
if (getParent().getDefaultRenderer().isMediaParserV2ThumbnailGeneration()) {
checkThumbnail();
}
}
+
return valid;
}
@@ -84,8 +91,9 @@ public InputStream getInputStream() {
try {
return new FileInputStream(getFile());
} catch (FileNotFoundException e) {
- logger.debug("File not found: \"" + getFile().getAbsolutePath() + "\"");
+ LOGGER.debug("File not found: {}", getFile().getAbsolutePath());
}
+
return null;
}
@@ -96,6 +104,7 @@ public long length() {
} else if (getMedia() != null && getMedia().isMediaparsed()) {
return getMedia().getSize();
}
+
return getFile().length();
}
@@ -112,10 +121,12 @@ public String getName() {
if (this.getConf().getName() == null) {
String name = null;
File file = getFile();
+
if (file.getName().trim().equals("")) {
if (PMS.get().isWindows()) {
name = PMS.get().getRegistry().getDiskLabel(file);
}
+
if (name != null && name.length() > 0) {
name = file.getAbsolutePath().substring(0, 1) + ":\\ [" + name + "]";
} else {
@@ -124,6 +135,7 @@ public String getName() {
} else {
name = file.getName();
}
+
this.getConf().setName(name);
}
return this.getConf().getName();
@@ -146,6 +158,7 @@ public String getSystemName() {
@Override
public void resolve() {
File file = getFile();
+
if (file.isFile() && (getMedia() == null || !getMedia().isMediaparsed())) {
boolean found = false;
InputFile input = new InputFile();
@@ -173,13 +186,15 @@ public void resolve() {
if (getMedia() == null) {
setMedia(new DLNAMediaInfo());
}
+
found = !getMedia().isMediaparsed() && !getMedia().isParsing();
+
if (getFormat() != null) {
getFormat().parse(getMedia(), input, getType(), getParent().getDefaultRenderer());
- } else //don't think that will ever happen
- {
+ } else { // don't think this will ever happen
getMedia().parse(input, getFormat(), getType(), false);
}
+
if (found && PMS.getConfiguration().getUseCache()) {
DLNAMediaDatabase database = PMS.get().getDatabase();
@@ -189,6 +204,7 @@ public void resolve() {
}
}
}
+
super.resolve();
}
@@ -269,13 +285,14 @@ public void checkThumbnail() {
@Override
protected String getThumbnailURL() {
- if (getType() == Format.IMAGE && !PMS.getConfiguration().getImageThumbnailsEnabled())
- {
+ if (getType() == Format.IMAGE && !PMS.getConfiguration().getImageThumbnailsEnabled()) {
return null;
}
+
StringBuilder sb = new StringBuilder();
sb.append(PMS.get().getServer().getURL());
sb.append("/");
+
if (getMedia() != null && getMedia().getThumb() != null) {
return super.getThumbnailURL();
} else if (getType() == Format.AUDIO) {
@@ -284,6 +301,7 @@ protected String getThumbnailURL() {
}
return null;
}
+
return super.getThumbnailURL();
}
}
View
9 src/main/java/net/pms/formats/Format.java
@@ -33,7 +33,7 @@
* Abstract class to store known information about a given format.
*/
public abstract class Format implements Cloneable {
- private static final Logger logger = LoggerFactory.getLogger(Format.class);
+ private static final Logger LOGGER = LoggerFactory.getLogger(Format.class);
public enum Identifier {
AUDIO_AS_VIDEO,
@@ -196,11 +196,13 @@ public boolean isUnknown() {
@Override
protected Object clone() {
Object o = null;
+
try {
o = super.clone();
} catch (CloneNotSupportedException e) {
- logger.error(null, e);
+ LOGGER.error(null, e);
}
+
return o;
}
@@ -220,7 +222,8 @@ public void parse(DLNAMediaInfo media, InputFile file, int type, RendererConfigu
} else {
media.parse(file, this, type, false);
}
- logger.trace("Parsing results: " + file + " / " + media);
+
+ LOGGER.trace("Parsing results: " + file + " / " + media);
}
/**
View
2  src/main/java/net/pms/formats/TIF.java
@@ -18,7 +18,6 @@
*/
package net.pms.formats;
-
public class TIF extends JPG {
/**
* {@inheritDoc}
@@ -28,7 +27,6 @@ public Identifier getIdentifier() {
return Identifier.TIF;
}
-
/**
* {@inheritDoc}
*/
Please sign in to comment.
Something went wrong with that request. Please try again.