use of net.pms.image.ExifInfo in project UniversalMediaServer by UniversalMediaServer.
the class DCRaw method getThumbnail.
/**
* Extracts or generates a thumbnail for {@code fileName}.
*
* @param params the {@link OutputParams} to use. Can be {@code null}.
* @param fileName the path of the image file to process.
* @param imageInfo the {@link ImageInfo} for the image file.
* @return A byte array containing the thumbnail or {@code null}.
* @throws IOException if an IO error occurs.
*/
@Override
public byte[] getThumbnail(OutputParams params, String fileName, ImageInfo imageInfo) {
boolean trace = LOGGER.isTraceEnabled();
if (trace) {
LOGGER.trace("Extracting thumbnail from \"{}\" with DCRaw", fileName);
}
if (params == null) {
params = new OutputParams(PMS.getConfiguration());
}
// Use device-specific pms conf
PmsConfiguration configuration = PMS.getConfiguration(params);
params.log = false;
// This is a wild guess at a decent buffer size for an embedded thumbnail.
// Every time the buffer has to grow, the whole buffer must be copied in memory.
params.outputByteArrayStreamBufferSize = 150000;
// First try to get the embedded thumbnail
String[] cmdArray = new String[6];
cmdArray[0] = configuration.getDCRawPath();
cmdArray[1] = "-e";
cmdArray[2] = "-c";
cmdArray[3] = "-M";
cmdArray[4] = "-w";
cmdArray[5] = fileName;
ProcessWrapperImpl pw = new ProcessWrapperImpl(cmdArray, true, params, false, true);
pw.runInSameThread();
byte[] bytes = pw.getOutputByteArray().toByteArray();
List<String> results = pw.getResults();
if (bytes.length > 0) {
// DCRaw doesn't seem to apply Exif Orientation to embedded thumbnails, handle it
boolean isJPEG = (bytes[0] & 0xFF) == 0xFF && (bytes[1] & 0xFF) == 0xD8;
ExifOrientation thumbnailOrientation = null;
Dimension jpegResolution = null;
int exifOrientationOffset = -1;
if (isJPEG) {
try {
ByteArrayReader reader = new ByteArrayReader(bytes);
exifOrientationOffset = ImagesUtil.getJPEGExifIFDTagOffset(0x112, reader);
jpegResolution = ImagesUtil.getJPEGResolution(reader);
} catch (IOException e) {
exifOrientationOffset = -1;
LOGGER.debug("Unexpected error while trying to find Exif orientation offset in embedded thumbnail for \"{}\": {}", fileName, e.getMessage());
LOGGER.trace("", e);
}
if (exifOrientationOffset > 0) {
thumbnailOrientation = ExifOrientation.typeOf(bytes[exifOrientationOffset]);
} else {
LOGGER.debug("Couldn't find Exif orientation in the thumbnail extracted from \"{}\"", fileName);
}
}
ExifOrientation imageOrientation = imageInfo instanceof ExifInfo ? ((ExifInfo) imageInfo).getOriginalExifOrientation() : null;
if (imageOrientation != null && imageOrientation != thumbnailOrientation) {
if (thumbnailOrientation != null) {
if (imageInfo.getWidth() > 0 && imageInfo.getHeight() > 0 && jpegResolution != null && jpegResolution.getWidth() > 0 && jpegResolution.getHeight() > 0) {
// Try to determine which orientation to trust
double imageAspect, thumbnailAspect;
if (ImagesUtil.isExifAxesSwapNeeded(imageOrientation)) {
imageAspect = (double) imageInfo.getHeight() / imageInfo.getWidth();
} else {
imageAspect = (double) imageInfo.getWidth() / imageInfo.getHeight();
}
if (ImagesUtil.isExifAxesSwapNeeded(thumbnailOrientation)) {
thumbnailAspect = (double) jpegResolution.getHeight() / jpegResolution.getWidth();
} else {
thumbnailAspect = (double) jpegResolution.getWidth() / jpegResolution.getHeight();
}
if (Math.abs(imageAspect - thumbnailAspect) > 0.001d) {
// The image and the thumbnail seems to have different aspect ratios, use that of the image
bytes[exifOrientationOffset] = (byte) imageOrientation.getValue();
}
}
} else if (imageOrientation != ExifOrientation.TOP_LEFT) {
// Apply the orientation to the thumbnail
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Thumbnails.of(new ByteArrayInputStream(bytes)).scale(1.0d).addFilter(ExifFilterUtils.getFilterForOrientation(imageOrientation.getThumbnailatorOrientation())).outputFormat(// PNG here to avoid further degradation from rotation
"PNG").outputQuality(1.0f).toOutputStream(outputStream);
bytes = outputStream.toByteArray();
} catch (IOException e) {
LOGGER.error("Unexpected error when trying to rotate thumbnail for \"{}\" - cancelling rotation: {}", fileName, e.getMessage());
LOGGER.trace("", e);
}
}
}
}
if (bytes.length == 0 || !results.isEmpty() && results.get(0).contains("has no thumbnail")) {
// No embedded thumbnail retrieved, generate thumbnail from the actual file
if (trace) {
LOGGER.trace("No embedded thumbnail found in \"{}\", " + "trying to generate thumbnail from the image itself", fileName);
}
params.outputByteArrayStreamBufferSize = imageInfo != null && imageInfo.getSize() != ImageInfo.SIZE_UNKNOWN ? (int) imageInfo.getSize() / 4 : 500000;
cmdArray[1] = "-h";
pw = new ProcessWrapperImpl(cmdArray, true, params);
pw.runInSameThread();
bytes = pw.getOutputByteArray().toByteArray();
}
if (trace && (bytes == null || bytes.length == 0)) {
LOGGER.trace("Failed to generate thumbnail with DCRaw for image \"{}\"", fileName);
}
return bytes != null && bytes.length > 0 ? bytes : null;
}
use of net.pms.image.ExifInfo in project UniversalMediaServer by UniversalMediaServer.
the class DLNAMediaInfo method parse.
/**
* Parse media without using MediaInfo.
*/
public void parse(InputFile inputFile, Format ext, int type, boolean thumbOnly, boolean resume, RendererConfiguration renderer) {
int i = 0;
while (isParsing()) {
if (i == 5) {
mediaparsed = true;
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
i++;
}
if (isMediaparsed() && !thumbOnly) {
// file could be already parsed by MediaInfo and we need only thumbnail
return;
}
if (inputFile != null) {
File file = inputFile.getFile();
if (file != null) {
size = file.length();
} else {
size = inputFile.getSize();
}
ProcessWrapperImpl pw = null;
boolean ffmpeg_parsing = true;
if (type == Format.AUDIO || ext instanceof AudioAsVideo) {
ffmpeg_parsing = false;
DLNAMediaAudio audio = new DLNAMediaAudio();
if (file != null) {
try {
AudioFile af;
if ("mp2".equals(FileUtil.getExtension(file).toLowerCase(Locale.ROOT))) {
af = AudioFileIO.readAs(file, "mp3");
} else {
af = AudioFileIO.read(file);
}
AudioHeader ah = af.getAudioHeader();
if (ah != null && !thumbOnly) {
int length = ah.getTrackLength();
int rate = ah.getSampleRateAsNumber();
if (ah.getEncodingType() != null && ah.getEncodingType().toLowerCase().contains("flac 24")) {
audio.setBitsperSample(24);
}
audio.setSampleFrequency("" + rate);
durationSec = (double) length;
bitrate = (int) ah.getBitRateAsNumber();
// set default value of channels to 2
audio.getAudioProperties().setNumberOfChannels(2);
String channels = ah.getChannels().toLowerCase(Locale.ROOT);
if (StringUtils.isNotBlank(channels)) {
if (channels.equals("1") || channels.contains("mono")) {
// parse value "1" or "Mono"
audio.getAudioProperties().setNumberOfChannels(1);
} else if (!(channels.equals("2") || channels.equals("0") || channels.contains("stereo"))) {
// No need to parse stereo as it's set as default
try {
audio.getAudioProperties().setNumberOfChannels(Integer.parseInt(channels));
} catch (IllegalArgumentException e) {
LOGGER.debug("Could not parse number of audio channels from \"{}\"", channels);
}
}
}
if (StringUtils.isNotBlank(ah.getEncodingType())) {
audio.setCodecA(ah.getEncodingType());
}
if (audio.getCodecA() != null && audio.getCodecA().contains("(windows media")) {
audio.setCodecA(audio.getCodecA().substring(0, audio.getCodecA().indexOf("(windows media")).trim());
}
}
Tag t = af.getTag();
if (t != null) {
if (t.getArtworkList().size() > 0) {
thumb = DLNAThumbnail.toThumbnail(t.getArtworkList().get(0).getBinaryData(), 640, 480, ScaleType.MAX, ImageFormat.SOURCE, false);
} else if (!configuration.getAudioThumbnailMethod().equals(CoverSupplier.NONE)) {
thumb = DLNAThumbnail.toThumbnail(CoverUtil.get().getThumbnail(t), 640, 480, ScaleType.MAX, ImageFormat.SOURCE, false);
}
if (thumb != null) {
thumbready = true;
}
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);
}
audio.setYear(Integer.parseInt(((y != null && y.length() > 0) ? y : "0")));
y = t.getFirst(FieldKey.TRACK);
audio.setTrack(Integer.parseInt(((y != null && y.length() > 0) ? y : "1")));
audio.setGenre(t.getFirst(FieldKey.GENRE));
} catch (NumberFormatException | KeyNotFoundException e) {
LOGGER.debug("Error parsing unimportant metadata: " + e.getMessage());
}
}
}
} catch (CannotReadException e) {
if (e.getMessage().startsWith(ErrorMessage.NO_READER_FOR_THIS_FORMAT.getMsg().substring(0, ErrorMessage.NO_READER_FOR_THIS_FORMAT.getMsg().indexOf("{")))) {
LOGGER.debug("No audio tag support for audio file \"{}\"", file.getName());
} else {
LOGGER.error("Error reading audio tag for \"{}\": {}", file.getName(), e.getMessage());
LOGGER.trace("", e);
}
} catch (IOException | TagException | ReadOnlyFileException | InvalidAudioFrameException | NumberFormatException | KeyNotFoundException e) {
LOGGER.debug("Error parsing audio file tag for \"{}\": {}", file.getName(), e.getMessage());
LOGGER.trace("", e);
ffmpeg_parsing = false;
}
// Set container for formats that the normal parsing fails to do from Format
if (StringUtils.isBlank(container) && ext != null) {
if (ext.getIdentifier() == Identifier.ADPCM) {
audio.setCodecA(FormatConfiguration.ADPCM);
} else if (ext.getIdentifier() == Identifier.DSD) {
audio.setCodecA(FormatConfiguration.DSD);
}
}
if (StringUtils.isNotBlank(audio.getSongname())) {
if (renderer != null && renderer.isPrependTrackNumbers() && audio.getTrack() > 0) {
audio.setSongname(audio.getTrack() + ": " + audio.getSongname());
}
} else {
audio.setSongname(file.getName());
}
if (!ffmpeg_parsing) {
audioTracks.add(audio);
}
}
if (StringUtils.isBlank(container)) {
container = audio.getCodecA();
}
}
if (type == Format.IMAGE && file != null) {
if (!thumbOnly) {
try {
ffmpeg_parsing = false;
ImagesUtil.parseImage(file, this);
imageCount++;
} catch (IOException e) {
LOGGER.debug("Error parsing image \"{}\", switching to FFmpeg: {}", file.getAbsolutePath(), e.getMessage());
LOGGER.trace("", e);
ffmpeg_parsing = true;
}
}
if (thumbOnly && configuration.isThumbnailGenerationEnabled() && configuration.getImageThumbnailsEnabled()) {
LOGGER.trace("Creating thumbnail for \"{}\"", file.getName());
// Create the thumbnail image
try {
if (imageInfo instanceof ExifInfo && ((ExifInfo) imageInfo).hasExifThumbnail() && !imageInfo.isImageIOSupported()) {
/*
* XXX Extraction of thumbnails was removed in version
* 2.10.0 of metadata-extractor because of a bug in
* related code. This section is deactivated while
* waiting for this to be made available again.
*
* Images supported by ImageIO or DCRaw aren't affected,
* so this only applied to very few images anyway.
* It could extract thumbnails for some "raw" images
* if DCRaw was disabled.
*
// ImageIO can't read the file, try to get the embedded Exif thumbnail if it's there.
Metadata metadata;
try {
metadata = ImagesUtil.getMetadata(new FileInputStream(file), imageInfo.getFormat());
} catch (ImageProcessingException e) {
metadata = null;
LOGGER.debug("Unexpected error reading metadata for \"{}\": {}", file.getName(), e.getMessage());
LOGGER.trace("", e);
}
thumb = DLNAThumbnail.toThumbnail(
ImagesUtil.getThumbnailFromMetadata(file, metadata),
320,
320,
ScaleType.MAX,
ImageFormat.SOURCE,
false
);
if (thumb == null && LOGGER.isTraceEnabled()) {
LOGGER.trace("Exif thumbnail extraction failed, no thumbnail will be generated for \"{}\"", file.getName());
}*/
} else {
// This will fail with UnknownFormatException for any image formats not supported by ImageIO
thumb = DLNAThumbnail.toThumbnail(Files.newInputStream(file.toPath()), 320, 320, ScaleType.MAX, ImageFormat.SOURCE, false);
}
thumbready = true;
} catch (EOFException e) {
LOGGER.debug("Error generating thumbnail for \"{}\": Unexpected end of file, probably corrupt file or read error.", file.getName());
} catch (UnknownFormatException e) {
LOGGER.debug("Could not generate thumbnail for \"{}\" because the format is unknown: {}", file.getName(), e.getMessage());
} catch (IOException e) {
LOGGER.debug("Error generating thumbnail for \"{}\": {}", file.getName(), e.getMessage());
LOGGER.trace("", e);
}
}
}
if (ffmpeg_parsing) {
if (!thumbOnly || (type == Format.VIDEO && !configuration.isUseMplayerForVideoThumbs())) {
pw = getFFmpegThumbnail(inputFile, resume, renderer);
}
boolean dvrms = false;
String input = "-";
if (file != null) {
input = ProcessUtil.getShortFileNameIfWideChars(file.getAbsolutePath());
dvrms = file.getAbsolutePath().toLowerCase().endsWith("dvr-ms");
}
synchronized (ffmpeg_failureLock) {
if (pw != null && !ffmpeg_failure && !thumbOnly) {
parseFFmpegInfo(pw.getResults(), input);
}
}
if (!thumbOnly && container != null && file != null && container.equals("mpegts") && isH264() && getDurationInSeconds() == 0) {
// Parse the duration
try {
int length = MpegUtil.getDurationFromMpeg(file);
if (length > 0) {
durationSec = (double) length;
}
} catch (IOException e) {
LOGGER.trace("Error retrieving length: " + e.getMessage());
}
}
if (configuration.isUseMplayerForVideoThumbs() && type == Format.VIDEO && !dvrms) {
try {
getMplayerThumbnail(inputFile, resume, renderer);
String frameName = "" + inputFile.hashCode();
frameName = configuration.getTempFolder() + "/mplayer_thumbs/" + frameName + "00000001/00000001.jpg";
frameName = frameName.replace(',', '_');
File jpg = new File(frameName);
if (jpg.exists()) {
try (InputStream is = new FileInputStream(jpg)) {
int sz = is.available();
if (sz > 0) {
byte[] bytes = new byte[sz];
is.read(bytes);
thumb = DLNAThumbnail.toThumbnail(bytes, 640, 480, ScaleType.MAX, ImageFormat.SOURCE, false);
thumbready = true;
}
}
if (!jpg.delete()) {
jpg.deleteOnExit();
}
// Try and retry
if (!jpg.getParentFile().delete() && !jpg.getParentFile().delete()) {
LOGGER.debug("Failed to delete \"" + jpg.getParentFile().getAbsolutePath() + "\"");
}
}
} catch (IOException e) {
LOGGER.debug("Caught exception", e);
}
}
if (type == Format.VIDEO && pw != null && thumb == null) {
byte[] bytes = pw.getOutputByteArray().toByteArray();
if (bytes != null && bytes.length > 0) {
try {
thumb = DLNAThumbnail.toThumbnail(bytes);
} catch (IOException e) {
LOGGER.debug("Error while decoding thumbnail: " + e.getMessage());
LOGGER.trace("", e);
}
thumbready = true;
}
}
}
postParse(type, inputFile);
mediaparsed = true;
}
}
Aggregations