use of net.pms.util.ParseException in project UniversalMediaServer by UniversalMediaServer.
the class ImagesUtil method getJPEGExifIFDTagOffset.
/**
* Finds the offset of the given Exif tag. Only the first IFD is searched.
*
* @param tagId the tag id to look for.
* @param reader a {@link RandomAccessReader} with a JPEG image.
* @return the offset of the given tag's value, or -1 if not found.
* @throws UnknownFormatException if the content isn't a JPEG.
* @throws IOException if any error occurs while reading.
*/
public static int getJPEGExifIFDTagOffset(int tagId, RandomAccessReader reader) throws UnknownFormatException, IOException {
reader.setMotorolaByteOrder(true);
if (reader.getUInt16(0) != 0xFFD8) {
throw new UnknownFormatException("Content isn't JPEG");
}
byte SEGMENT_IDENTIFIER = (byte) 0xFF;
byte MARKER_EOI = (byte) 0xD9;
byte APP1 = (byte) 0xE1;
final String EXIF_SEGMENT_PREAMBLE = "Exif\0\0";
byte segmentIdentifier = reader.getInt8(2);
byte segmentType = reader.getInt8(3);
int pos = 4;
while (segmentIdentifier != SEGMENT_IDENTIFIER || segmentType != APP1 && segmentType != MARKER_EOI || segmentType == APP1 && !EXIF_SEGMENT_PREAMBLE.equals(new String(reader.getBytes(pos + 2, EXIF_SEGMENT_PREAMBLE.length()), 0, EXIF_SEGMENT_PREAMBLE.length(), StandardCharsets.US_ASCII))) {
segmentIdentifier = segmentType;
segmentType = reader.getInt8(pos++);
}
if (segmentType == MARKER_EOI) {
// Reached the end of the image without finding an Exif segment
return -1;
}
int segmentLength = reader.getUInt16(pos) - 2;
pos += 2 + EXIF_SEGMENT_PREAMBLE.length();
if (segmentLength < EXIF_SEGMENT_PREAMBLE.length()) {
throw new ParseException("Exif segment is too small");
}
int exifHeaderOffset = pos;
short byteOrderIdentifier = reader.getInt16(pos);
// Skip TIFF marker
pos += 4;
if (byteOrderIdentifier == 0x4d4d) {
// "MM"
reader.setMotorolaByteOrder(true);
} else if (byteOrderIdentifier == 0x4949) {
// "II"
reader.setMotorolaByteOrder(false);
} else {
throw new ParseException("Can't determine Exif endianness from: 0x" + Integer.toHexString(byteOrderIdentifier));
}
pos = reader.getInt32(pos) + exifHeaderOffset;
int tagCount = reader.getUInt16(pos);
for (int tagNumber = 0; tagNumber < tagCount; tagNumber++) {
int tagOffset = pos + 2 + (12 * tagNumber);
int curTagId = reader.getUInt16(tagOffset);
if (curTagId == tagId) {
// tag found
return tagOffset + 8;
}
}
return -1;
}
use of net.pms.util.ParseException in project UniversalMediaServer by UniversalMediaServer.
the class RAW method parse.
@Override
public void parse(DLNAMediaInfo media, InputFile file, int type, RendererConfiguration renderer) {
boolean trace = LOGGER.isTraceEnabled();
if (media == null || file == null || file.getFile() == null) {
// Parsing is impossible
if (trace) {
if (file != null && file.getFile() != null) {
LOGGER.trace("Not parsing RAW file \"{}\" because media is null", file.getFile().getName());
} else {
LOGGER.error("Not parsing RAW file because file is null");
}
}
return;
}
PmsConfiguration configuration = PMS.getConfiguration(renderer);
try {
// Only parse using DCRaw if it is enabled
DCRaw dcraw = (DCRaw) PlayerFactory.getEnabledPlayer(DCRaw.class, this);
if (dcraw != null) {
if (trace) {
LOGGER.trace("Parsing RAW image \"{}\" with DCRaw", file.getFile().getName());
}
dcraw.parse(media, file.getFile());
media.setCodecV(FormatConfiguration.RAW);
media.setContainer(FormatConfiguration.RAW);
ImageInfo imageInfo = null;
Metadata metadata = null;
FileType fileType = null;
try (BufferedInputStream inputStream = new BufferedInputStream(Files.newInputStream(file.getFile().toPath()))) {
fileType = FileTypeDetector.detectFileType(inputStream);
metadata = ImagesUtil.getMetadata(inputStream, fileType);
} catch (IOException e) {
metadata = new Metadata();
LOGGER.debug("Error reading \"{}\": {}", file.getFile().getAbsolutePath(), e.getMessage());
LOGGER.trace("", e);
} catch (ImageProcessingException e) {
metadata = new Metadata();
LOGGER.debug("Error parsing {} metadata for \"{}\": {}", fileType.toString().toUpperCase(Locale.ROOT), file.getFile().getAbsolutePath(), e.getMessage());
LOGGER.trace("", e);
}
if (fileType == FileType.Arw && !ImagesUtil.isARW(metadata)) {
fileType = FileType.Tiff;
}
ImageFormat format = ImageFormat.toImageFormat(fileType);
if (format == null || format == ImageFormat.TIFF) {
format = ImageFormat.toImageFormat(metadata);
if (format == null || format == ImageFormat.TIFF) {
format = ImageFormat.RAW;
}
}
try {
imageInfo = ImageInfo.create(media.getWidth(), media.getHeight(), metadata, format, file.getSize(), true, false);
if (trace) {
LOGGER.trace("Parsing of RAW image \"{}\" completed: {}", file.getFile().getName(), imageInfo);
}
} catch (ParseException e) {
LOGGER.warn("Unable to parse \"{}\": {}", file.getFile().getAbsolutePath(), e.getMessage());
LOGGER.trace("", e);
}
media.setImageInfo(imageInfo);
if (media.getWidth() > 0 && media.getHeight() > 0 && configuration.getImageThumbnailsEnabled()) {
byte[] image = new DCRaw().getThumbnail(null, file.getFile().getAbsolutePath(), imageInfo);
media.setThumb(DLNAThumbnail.toThumbnail(image, 320, 320, ScaleType.MAX, ImageFormat.JPEG, false));
}
} else {
if (trace) {
LOGGER.trace("Parsing RAW image \"{}\" as a regular image because DCRaw is disabled", file.getFile().getName());
}
ImagesUtil.parseImage(file.getFile(), media);
}
media.setSize(file.getSize());
media.setImageCount(1);
media.postParse(type, file);
media.setMediaparsed(true);
} catch (IOException e) {
LOGGER.error("Error parsing RAW file \"{}\": {}", file.getFile().getAbsolutePath(), e.getMessage());
LOGGER.trace("", e);
}
}
use of net.pms.util.ParseException in project UniversalMediaServer by UniversalMediaServer.
the class ImagesUtil method parseImage.
/**
* Parses an image file and stores the results in the given
* {@link DLNAMediaInfo}. Parsing is performed using both
* <a href=https://github.com/drewnoakes/metadata-extractor>Metadata Extractor</a>
* and {@link ImageIO}. While Metadata Extractor offers more detailed
* information, {@link ImageIO} offers information that is convenient for
* image transformation with {@link ImageIO}. Parsing will be performed if
* just one of the two methods produces results, but some details will be
* missing if either one failed.
* <p><b>
* This method consumes and closes {@code inputStream}.
* </b>
* @param file the {@link File} to parse.
* @param media the {@link DLNAMediaInfo} instance to store the parsing
* results to.
* @throws IOException if an IO error occurs or no information can be parsed.
*/
public static void parseImage(File file, DLNAMediaInfo media) throws IOException {
// 1 MB
final int MAX_BUFFER = 1048576;
if (file == null) {
throw new IllegalArgumentException("parseImage: file cannot be null");
}
if (media == null) {
throw new IllegalArgumentException("parseImage: media cannot be null");
}
boolean trace = LOGGER.isTraceEnabled();
if (trace) {
LOGGER.trace("Parsing image file \"{}\"", file.getAbsolutePath());
}
long size = file.length();
ResettableInputStream inputStream = new ResettableInputStream(Files.newInputStream(file.toPath()), MAX_BUFFER);
try {
Metadata metadata = null;
FileType fileType = null;
try {
fileType = FileTypeDetector.detectFileType(inputStream);
metadata = getMetadata(inputStream, fileType);
} catch (IOException e) {
metadata = new Metadata();
LOGGER.debug("Error reading \"{}\": {}", file.getAbsolutePath(), e.getMessage());
LOGGER.trace("", e);
} catch (ImageProcessingException e) {
metadata = new Metadata();
LOGGER.debug("Error parsing {} metadata for \"{}\": {}", fileType.toString().toUpperCase(Locale.ROOT), file.getAbsolutePath(), e.getMessage());
LOGGER.trace("", e);
}
ImageFormat format = ImageFormat.toImageFormat(fileType);
if (format == null || format == ImageFormat.TIFF) {
ImageFormat tmpformat = ImageFormat.toImageFormat(metadata);
if (tmpformat != null) {
format = tmpformat;
}
}
if (inputStream.isFullResetAvailable()) {
inputStream.fullReset();
} else {
// If we can't reset it, close it and create a new
inputStream.close();
inputStream = new ResettableInputStream(Files.newInputStream(file.toPath()), MAX_BUFFER);
}
ImageInfo imageInfo = null;
try {
imageInfo = ImageIOTools.readImageInfo(inputStream, size, metadata, false);
} catch (UnknownFormatException | IIOException | ParseException e) {
if (format == null) {
throw new UnknownFormatException("Unable to recognize image format for \"" + file.getAbsolutePath() + "\" - parsing failed", e);
}
LOGGER.debug("Unable to parse \"{}\" with ImageIO because the format is unsupported, image information will be limited", file.getAbsolutePath());
LOGGER.trace("ImageIO parse failure reason: {}", e.getMessage());
// Gather basic information from the data we have
if (metadata != null) {
try {
imageInfo = ImageInfo.create(metadata, format, size, true, true);
} catch (ParseException pe) {
LOGGER.debug("Unable to parse metadata for \"{}\": {}", file.getAbsolutePath(), pe.getMessage());
LOGGER.trace("", pe);
}
}
}
if (imageInfo == null && format == null) {
throw new ParseException("Parsing of \"" + file.getAbsolutePath() + "\" failed");
}
if (format == null) {
format = imageInfo.getFormat();
} else if (imageInfo != null && imageInfo.getFormat() != null && format != imageInfo.getFormat()) {
if (imageInfo.getFormat() == ImageFormat.TIFF && format.isRaw()) {
if (format == ImageFormat.ARW && !isARW(metadata)) {
// XXX Remove this if https://github.com/drewnoakes/metadata-extractor/issues/217 is fixed
// Metadata extractor misidentifies some Photoshop created TIFFs for ARW, correct it
format = ImageFormat.toImageFormat(metadata);
if (format == null) {
format = ImageFormat.TIFF;
}
LOGGER.trace("Correcting misidentified image format ARW to {} for \"{}\"", format, file.getAbsolutePath());
} else {
/*
* ImageIO recognizes many RAW formats as TIFF because
* of their close relationship let's treat them as what
* they really are.
*/
imageInfo = ImageInfo.create(imageInfo.getWidth(), imageInfo.getHeight(), format, size, imageInfo.getBitDepth(), imageInfo.getNumComponents(), imageInfo.getColorSpace(), imageInfo.getColorSpaceType(), metadata, false, imageInfo.isImageIOSupported());
LOGGER.trace("Correcting misidentified image format TIFF to {} for \"{}\"", format.toString(), file.getAbsolutePath());
}
} else {
LOGGER.debug("Image parsing for \"{}\" was inconclusive, metadata parsing " + "detected {} format while ImageIO detected {}. Choosing {}.", file.getAbsolutePath(), format, imageInfo.getFormat(), imageInfo.getFormat());
format = imageInfo.getFormat();
}
}
media.setImageInfo(imageInfo);
if (format != null) {
media.setCodecV(format.toFormatConfiguration());
media.setContainer(format.toFormatConfiguration());
}
if (trace) {
LOGGER.trace("Parsing of image \"{}\" completed", file.getName());
}
} finally {
inputStream.close();
}
}
use of net.pms.util.ParseException in project UniversalMediaServer by UniversalMediaServer.
the class PNGInfo method parseMetadata.
@Override
protected void parseMetadata(Metadata metadata) throws ParseException {
if (metadata == null) {
return;
}
for (Directory directory : metadata.getDirectories()) {
if (directory instanceof PngDirectory && PngChunkType.IHDR.equals(((PngDirectory) directory).getPngChunkType())) {
parsedInfo.format = ImageFormat.PNG;
if (((PngDirectory) directory).containsTag(PngDirectory.TAG_IMAGE_WIDTH) && ((PngDirectory) directory).containsTag(PngDirectory.TAG_IMAGE_HEIGHT)) {
parsedInfo.width = ((PngDirectory) directory).getInteger(PngDirectory.TAG_IMAGE_WIDTH);
parsedInfo.height = ((PngDirectory) directory).getInteger(PngDirectory.TAG_IMAGE_HEIGHT);
}
if (((PngDirectory) directory).containsTag(PngDirectory.TAG_BITS_PER_SAMPLE)) {
parsedInfo.bitDepth = ((PngDirectory) directory).getInteger(PngDirectory.TAG_BITS_PER_SAMPLE);
}
if (((PngDirectory) directory).containsTag(PngDirectory.TAG_INTERLACE_METHOD)) {
Integer i = ((PngDirectory) directory).getInteger(PngDirectory.TAG_INTERLACE_METHOD);
if (i != null) {
((PNGParseInfo) parsedInfo).interlaceMethod = InterlaceMethod.typeOf(i);
}
}
if (((PngDirectory) directory).containsTag(PngDirectory.TAG_COLOR_TYPE)) {
Integer i = ((PngDirectory) directory).getInteger(PngDirectory.TAG_COLOR_TYPE);
if (i != null) {
((PNGParseInfo) parsedInfo).colorType = PngColorType.fromNumericValue(i);
switch(((PNGParseInfo) parsedInfo).colorType) {
case // Grayscale without alpha
Greyscale:
parsedInfo.numComponents = 1;
parsedInfo.colorSpaceType = ColorSpaceType.TYPE_GRAY;
break;
case // RGB without alpha
TrueColor:
parsedInfo.numComponents = 3;
parsedInfo.colorSpaceType = ColorSpaceType.TYPE_RGB;
break;
case // Palette index
IndexedColor:
parsedInfo.numComponents = 3;
parsedInfo.colorSpaceType = ColorSpaceType.TYPE_RGB;
break;
case // Grayscale with alpha
GreyscaleWithAlpha:
parsedInfo.numComponents = 2;
parsedInfo.colorSpaceType = ColorSpaceType.TYPE_GRAY;
break;
case // RGB with alpha
TrueColorWithAlpha:
parsedInfo.numComponents = 4;
parsedInfo.colorSpaceType = ColorSpaceType.TYPE_RGB;
break;
default:
}
}
}
}
if (directory instanceof PngDirectory && PngChunkType.tRNS.equals(((PngDirectory) directory).getPngChunkType())) {
((PNGParseInfo) parsedInfo).hasTransparencyChunk = true;
if (((PNGParseInfo) parsedInfo).colorType == null || ((PNGParseInfo) parsedInfo).numComponents == null) {
throw new ParseException("PNG parsing failed with ancillary chunk tRNS appearing before critical chunk IHDR");
}
if (((PNGParseInfo) parsedInfo).colorType == PngColorType.GreyscaleWithAlpha || ((PNGParseInfo) parsedInfo).colorType == PngColorType.TrueColorWithAlpha) {
throw new ParseException(String.format("PNG parsing failed with illegal combination of %s color type and tRNS transparancy chunk", ((PNGParseInfo) parsedInfo).colorType));
}
parsedInfo.numComponents++;
}
if (directory instanceof PngDirectory && PngChunkType.sBIT.equals(((PngDirectory) directory).getPngChunkType())) {
((PNGParseInfo) parsedInfo).isModifiedBitDepth = true;
}
}
}
use of net.pms.util.ParseException in project UniversalMediaServer by UniversalMediaServer.
the class GIFInfo method parseMetadata.
@Override
protected void parseMetadata(Metadata metadata) throws ParseException {
if (metadata == null) {
return;
}
for (Directory directory : metadata.getDirectories()) {
if (directory instanceof GifHeaderDirectory) {
parsedInfo.format = ImageFormat.GIF;
parsedInfo.numComponents = 3;
if (((GifHeaderDirectory) directory).containsTag(GifHeaderDirectory.TAG_IMAGE_WIDTH) && ((GifHeaderDirectory) directory).containsTag(GifHeaderDirectory.TAG_IMAGE_HEIGHT)) {
parsedInfo.width = ((GifHeaderDirectory) directory).getInteger(GifHeaderDirectory.TAG_IMAGE_WIDTH);
parsedInfo.height = ((GifHeaderDirectory) directory).getInteger(GifHeaderDirectory.TAG_IMAGE_HEIGHT);
}
if (((GifHeaderDirectory) directory).containsTag(GifHeaderDirectory.TAG_BITS_PER_PIXEL)) {
parsedInfo.colorSpaceType = ColorSpaceType.TYPE_RGB;
Integer i = ((GifHeaderDirectory) directory).getInteger(GifHeaderDirectory.TAG_BITS_PER_PIXEL);
if (i != null) {
parsedInfo.bitDepth = i.intValue();
}
}
if (((GifHeaderDirectory) directory).containsTag(GifHeaderDirectory.TAG_GIF_FORMAT_VERSION)) {
String s = ((GifHeaderDirectory) directory).getString(GifHeaderDirectory.TAG_GIF_FORMAT_VERSION, "US-ASCII");
if (s != null) {
((GIFParseInfo) parsedInfo).formatVersion = s;
}
}
} else if (directory instanceof GifControlDirectory) {
boolean hasTransparency = ((GifControlDirectory) directory).isTransparent();
((GIFParseInfo) parsedInfo).hasTransparency = hasTransparency;
if (hasTransparency) {
if (parsedInfo.numComponents == null) {
throw new ParseException("Invalid GIF image - Graphic Control Extension block encountered before the Header block");
}
parsedInfo.numComponents = 4;
}
}
}
}
Aggregations