Search in sources :

Example 1 with UnknownFormatException

use of net.pms.util.UnknownFormatException in project UniversalMediaServer by UniversalMediaServer.

the class LibMediaInfoParser method parse.

/**
 * Parse media via MediaInfo.
 */
public static synchronized void parse(DLNAMediaInfo media, InputFile inputFile, int type, RendererConfiguration renderer) {
    File file = inputFile.getFile();
    if (!media.isMediaparsed() && file != null && MI.isValid() && MI.Open(file.getAbsolutePath()) > 0) {
        StreamType general = StreamType.General;
        StreamType video = StreamType.Video;
        StreamType audio = StreamType.Audio;
        StreamType image = StreamType.Image;
        StreamType text = StreamType.Text;
        DLNAMediaAudio currentAudioTrack = new DLNAMediaAudio();
        DLNAMediaSubtitle currentSubTrack;
        media.setSize(file.length());
        String value;
        // set General
        getFormat(general, media, currentAudioTrack, MI.Get(general, 0, "Format"), file);
        getFormat(general, media, currentAudioTrack, MI.Get(general, 0, "CodecID").trim(), file);
        media.setDuration(getDuration(MI.Get(general, 0, "Duration/String1")));
        media.setBitrate(getBitrate(MI.Get(general, 0, "OverallBitRate")));
        media.setStereoscopy(MI.Get(general, 0, "StereoscopicLayout"));
        value = MI.Get(general, 0, "Cover_Data");
        if (!value.isEmpty()) {
            try {
                media.setThumb(DLNAThumbnail.toThumbnail(new Base64().decode(value.getBytes(StandardCharsets.US_ASCII)), 640, 480, ScaleType.MAX, ImageFormat.SOURCE, false));
            } catch (EOFException e) {
                LOGGER.debug("Error reading \"{}\" thumbnail from MediaInfo: Unexpected end of stream, probably corrupt or read error.", file.getName());
            } catch (UnknownFormatException e) {
                LOGGER.debug("Could not read \"{}\" thumbnail from MediaInfo: {}", file.getName(), e.getMessage());
            } catch (IOException e) {
                LOGGER.error("Error reading \"{}\" thumbnail from MediaInfo: {}", file.getName(), e.getMessage());
                LOGGER.trace("", e);
            }
        }
        value = MI.Get(general, 0, "Title");
        if (!value.isEmpty()) {
            media.setFileTitleFromMetadata(value);
        }
        // set Video
        media.setVideoTrackCount(MI.Count_Get(video));
        if (media.getVideoTrackCount() > 0) {
            for (int i = 0; i < media.getVideoTrackCount(); i++) {
                // check for DXSA and DXSB subtitles (subs in video format)
                if (MI.Get(video, i, "Title").startsWith("Subtitle")) {
                    currentSubTrack = new DLNAMediaSubtitle();
                    // First attempt to detect subtitle track format
                    currentSubTrack.setType(SubtitleType.valueOfLibMediaInfoCodec(MI.Get(video, i, "Format")));
                    // Second attempt to detect subtitle track format (CodecID usually is more accurate)
                    currentSubTrack.setType(SubtitleType.valueOfLibMediaInfoCodec(MI.Get(video, i, "CodecID")));
                    currentSubTrack.setId(media.getSubtitleTracksList().size());
                    addSub(currentSubTrack, media);
                } else {
                    getFormat(video, media, currentAudioTrack, MI.Get(video, i, "Format"), file);
                    getFormat(video, media, currentAudioTrack, MI.Get(video, i, "Format_Version"), file);
                    getFormat(video, media, currentAudioTrack, MI.Get(video, i, "CodecID"), file);
                    media.setWidth(getPixelValue(MI.Get(video, i, "Width")));
                    media.setHeight(getPixelValue(MI.Get(video, i, "Height")));
                    media.setMatrixCoefficients(MI.Get(video, i, "matrix_coefficients"));
                    if (!media.is3d()) {
                        media.setStereoscopy(MI.Get(video, i, "MultiView_Layout"));
                    }
                    media.setAspectRatioContainer(MI.Get(video, i, "DisplayAspectRatio/String"));
                    media.setAspectRatioVideoTrack(MI.Get(video, i, "DisplayAspectRatio_Original/String"));
                    media.setFrameRate(getFPSValue(MI.Get(video, i, "FrameRate")));
                    media.setFrameRateOriginal(MI.Get(video, i, "FrameRate_Original"));
                    media.setFrameRateMode(getFrameRateModeValue(MI.Get(video, i, "FrameRate_Mode")));
                    media.setFrameRateModeRaw(MI.Get(video, i, "FrameRate_Mode"));
                    media.setReferenceFrameCount(getReferenceFrameCount(MI.Get(video, i, "Format_Settings_RefFrames/String")));
                    media.setVideoTrackTitleFromMetadata(MI.Get(video, i, "Title"));
                    value = MI.Get(video, i, "Format_Settings_QPel");
                    if (!value.isEmpty()) {
                        media.putExtra(FormatConfiguration.MI_QPEL, value);
                    }
                    value = MI.Get(video, i, "Format_Settings_GMC");
                    if (!value.isEmpty()) {
                        media.putExtra(FormatConfiguration.MI_GMC, value);
                    }
                    value = MI.Get(video, i, "Format_Settings_GOP");
                    if (!value.isEmpty()) {
                        media.putExtra(FormatConfiguration.MI_GOP, value);
                    }
                    media.setMuxingMode(MI.Get(video, i, "MuxingMode"));
                    if (!media.isEncrypted()) {
                        media.setEncrypted("encrypted".equals(MI.Get(video, i, "Encryption")));
                    }
                    value = MI.Get(video, i, "BitDepth");
                    if (!value.isEmpty()) {
                        try {
                            media.setVideoBitDepth(Integer.parseInt(value));
                        } catch (NumberFormatException nfe) {
                            LOGGER.debug("Could not parse bits per sample \"" + value + "\"");
                        }
                    }
                    value = MI.Get(video, i, "Format_Profile");
                    if (!value.isEmpty() && media.getCodecV() != null && media.getCodecV().equals(FormatConfiguration.H264)) {
                        media.setAvcLevel(getAvcLevel(value));
                    }
                }
            }
        }
        // set Audio
        int audioTracks = MI.Count_Get(audio);
        if (audioTracks > 0) {
            for (int i = 0; i < audioTracks; i++) {
                currentAudioTrack = new DLNAMediaAudio();
                getFormat(audio, media, currentAudioTrack, MI.Get(audio, i, "Format"), file);
                getFormat(audio, media, currentAudioTrack, MI.Get(audio, i, "Format_Version"), file);
                getFormat(audio, media, currentAudioTrack, MI.Get(audio, i, "Format_Profile"), file);
                getFormat(audio, media, currentAudioTrack, MI.Get(audio, i, "CodecID"), file);
                value = MI.Get(audio, i, "CodecID_Description");
                if (isNotBlank(value) && value.startsWith("Windows Media Audio 10")) {
                    currentAudioTrack.setCodecA(FormatConfiguration.WMA10);
                }
                currentAudioTrack.setLang(getLang(MI.Get(audio, i, "Language/String")));
                currentAudioTrack.setAudioTrackTitleFromMetadata((MI.Get(audio, i, "Title")).trim());
                currentAudioTrack.getAudioProperties().setNumberOfChannels(MI.Get(audio, i, "Channel(s)"));
                currentAudioTrack.setSampleFrequency(getSampleFrequency(MI.Get(audio, i, "SamplingRate")));
                currentAudioTrack.setBitRate(getBitrate(MI.Get(audio, i, "BitRate")));
                currentAudioTrack.setSongname(MI.Get(general, 0, "Track"));
                if (renderer.isPrependTrackNumbers() && currentAudioTrack.getTrack() > 0 && currentAudioTrack.getSongname() != null && currentAudioTrack.getSongname().length() > 0) {
                    currentAudioTrack.setSongname(currentAudioTrack.getTrack() + ": " + currentAudioTrack.getSongname());
                }
                currentAudioTrack.setAlbum(MI.Get(general, 0, "Album"));
                currentAudioTrack.setArtist(MI.Get(general, 0, "Performer"));
                currentAudioTrack.setGenre(MI.Get(general, 0, "Genre"));
                // Try to parse the year from the stored date
                String recordedDate = MI.Get(general, 0, "Recorded_Date");
                Matcher matcher = yearPattern.matcher(recordedDate);
                if (matcher.matches()) {
                    try {
                        currentAudioTrack.setYear(Integer.parseInt(matcher.group(1)));
                    } catch (NumberFormatException nfe) {
                        LOGGER.debug("Could not parse year from recorded date \"" + recordedDate + "\"");
                    }
                }
                // Special check for OGM: MediaInfo reports specific Audio/Subs IDs (0xn) while mencoder does not
                value = MI.Get(audio, i, "ID/String");
                if (!value.isEmpty()) {
                    if (value.contains("(0x") && !FormatConfiguration.OGG.equals(media.getContainer())) {
                        currentAudioTrack.setId(getSpecificID(value));
                    } else {
                        currentAudioTrack.setId(media.getAudioTracksList().size());
                    }
                }
                value = MI.Get(general, i, "Track/Position");
                if (!value.isEmpty()) {
                    try {
                        currentAudioTrack.setTrack(Integer.parseInt(value));
                    } catch (NumberFormatException nfe) {
                        LOGGER.debug("Could not parse track \"" + value + "\"");
                    }
                }
                value = MI.Get(audio, i, "BitDepth");
                if (!value.isEmpty()) {
                    try {
                        currentAudioTrack.setBitsperSample(Integer.parseInt(value));
                    } catch (NumberFormatException nfe) {
                        LOGGER.debug("Could not parse bits per sample \"" + value + "\"");
                    }
                }
                addAudio(currentAudioTrack, media);
            }
        }
        // set Image
        media.setImageCount(MI.Count_Get(image));
        if (media.getImageCount() > 0 || type == Format.IMAGE) {
            boolean parseByMediainfo = false;
            // For images use our own parser instead of MediaInfo which doesn't provide enough information
            try {
                ImagesUtil.parseImage(file, media);
                // so that MI.Count_Get(image) might return 0 even if there is an image.
                if (media.getImageCount() == 0) {
                    media.setImageCount(1);
                }
            } catch (IOException e) {
                if (media.getImageCount() > 0) {
                    LOGGER.debug("Error parsing image ({}), switching to MediaInfo: {}", file.getAbsolutePath(), e.getMessage());
                    LOGGER.trace("", e);
                    parseByMediainfo = true;
                } else {
                    LOGGER.warn("Image parsing for \"{}\" failed both with MediaInfo and internally: {}", file.getAbsolutePath(), e.getMessage());
                    LOGGER.trace("", e);
                    media.setImageCount(1);
                }
            }
            if (parseByMediainfo) {
                getFormat(image, media, currentAudioTrack, MI.Get(image, 0, "Format"), file);
                media.setWidth(getPixelValue(MI.Get(image, 0, "Width")));
                media.setHeight(getPixelValue(MI.Get(image, 0, "Height")));
            }
        }
        // set Subs in text format
        int subTracks = MI.Count_Get(text);
        if (subTracks > 0) {
            for (int i = 0; i < subTracks; i++) {
                currentSubTrack = new DLNAMediaSubtitle();
                currentSubTrack.setType(SubtitleType.valueOfLibMediaInfoCodec(MI.Get(text, i, "Format")));
                currentSubTrack.setType(SubtitleType.valueOfLibMediaInfoCodec(MI.Get(text, i, "CodecID")));
                currentSubTrack.setLang(getLang(MI.Get(text, i, "Language/String")));
                currentSubTrack.setSubtitlesTrackTitleFromMetadata((MI.Get(text, i, "Title")).trim());
                // Special check for OGM: MediaInfo reports specific Audio/Subs IDs (0xn) while mencoder does not
                value = MI.Get(text, i, "ID/String");
                if (!value.isEmpty()) {
                    if (value.contains("(0x") && !FormatConfiguration.OGG.equals(media.getContainer())) {
                        currentSubTrack.setId(getSpecificID(value));
                    } else {
                        currentSubTrack.setId(media.getSubtitleTracksList().size());
                    }
                }
                addSub(currentSubTrack, media);
            }
        }
        /*
			 * Some container formats (like MP4/M4A) can represent both audio
			 * and video media. UMS initially recognized this as video, but this
			 * is corrected here it the content is only audio.
			 */
        if (media.isAudioOrVideoContainer() && media.isAudio()) {
            media.setContainer(media.getAudioVariantFormatConfigurationString());
        }
        /*
			 * Recognize 3D layout from the filename.
			 *
			 * First we check for our custom naming convention, for which the filename
			 * either has to start with "3DSBSLF" or "3DSBSRF" for side-by-side layout
			 * or "3DOULF" or "3DOURF" for over-under layout.
			 * For anaglyph 3D video can be used following combination:
			 * 		3DARCG 	anaglyph_red_cyan_gray
			 *		3DARCH 	anaglyph_red_cyan_half_color
			 *		3DARCC 	anaglyph_red_cyan_color
			 *		3DARCD 	anaglyph_red_cyan_dubois
			 *		3DAGMG 	anaglyph_green_magenta_gray
			 *		3DAGMH 	anaglyph_green_magenta_half_color
			 *		3DAGMC 	anaglyph_green_magenta_color
			 *		3DAGMD 	anaglyph_green_magenta_dubois
			 *		3DAYBG 	anaglyph_yellow_blue_gray
			 *		3DAYBH 	anaglyph_yellow_blue_half_color
			 *		3DAYBC 	anaglyph_yellow_blue_color
			 *		3DAYBD 	anaglyph_yellow_blue_dubois
			 *
			 * Next we check for common naming conventions.
			 */
        if (!media.is3d()) {
            String upperCaseFileName = file.getName().toUpperCase();
            if (upperCaseFileName.startsWith("3DSBS")) {
                LOGGER.debug("3D format SBS detected for " + file.getName());
                media.setStereoscopy(file.getName().substring(2, 7));
            } else if (upperCaseFileName.startsWith("3DOU")) {
                LOGGER.debug("3D format OU detected for " + file.getName());
                media.setStereoscopy(file.getName().substring(2, 6));
            } else if (upperCaseFileName.startsWith("3DA")) {
                LOGGER.debug("3D format Anaglyph detected for " + file.getName());
                media.setStereoscopy(file.getName().substring(2, 6));
            } else if (upperCaseFileName.matches(".*[\\s\\.](H-|H|HALF-|HALF.)SBS[\\s\\.].*")) {
                LOGGER.debug("3D format HSBS detected for " + file.getName());
                media.setStereoscopy("half side by side (left eye first)");
            } else if (upperCaseFileName.matches(".*[\\s\\.](H-|H|HALF-|HALF.)(OU|TB)[\\s\\.].*")) {
                LOGGER.debug("3D format HOU detected for " + file.getName());
                media.setStereoscopy("half top-bottom (left eye first)");
            } else if (upperCaseFileName.matches(".*[\\s\\.]SBS[\\s\\.].*")) {
                if (media.getWidth() > 1920) {
                    LOGGER.debug("3D format SBS detected for " + file.getName());
                    media.setStereoscopy("side by side (left eye first)");
                } else {
                    LOGGER.debug("3D format HSBS detected based on width for " + file.getName());
                    media.setStereoscopy("half side by side (left eye first)");
                }
            } else if (upperCaseFileName.matches(".*[\\s\\.](OU|TB)[\\s\\.].*")) {
                if (media.getHeight() > 1080) {
                    LOGGER.debug("3D format OU detected for " + file.getName());
                    media.setStereoscopy("top-bottom (left eye first)");
                } else {
                    LOGGER.debug("3D format HOU detected based on height for " + file.getName());
                    media.setStereoscopy("half top-bottom (left eye first)");
                }
            }
        }
        media.postParse(type, inputFile);
        MI.Close();
        if (media.getContainer() == null) {
            media.setContainer(DLNAMediaLang.UND);
        }
        if (media.getCodecV() == null) {
            media.setCodecV(DLNAMediaLang.UND);
        }
        media.setMediaparsed(true);
    }
}
Also used : StreamType(net.pms.dlna.MediaInfo.StreamType) Base64(org.apache.commons.codec.binary.Base64) Matcher(java.util.regex.Matcher) IOException(java.io.IOException) EOFException(java.io.EOFException) UnknownFormatException(net.pms.util.UnknownFormatException) File(java.io.File)

Example 2 with UnknownFormatException

use of net.pms.util.UnknownFormatException 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;
}
Also used : UnknownFormatException(net.pms.util.UnknownFormatException) ParseException(net.pms.util.ParseException)

Example 3 with UnknownFormatException

use of net.pms.util.UnknownFormatException in project UniversalMediaServer by UniversalMediaServer.

the class ImagesUtil method transcodeImage.

/**
 * Converts and scales an image in one operation. Scaling can be with or
 * without padding. Preserves aspect ratio and rotates/flips the image
 * according to Exif orientation. Format support is limited to that of
 * {@link ImageIO}. Only one of the three input arguments may be used in any
 * given call. Note that {@code outputProfile} overrides
 * {@code outputFormat}.
 * <p>
 * <b> This method consumes and closes {@code inputStream}. </b>
 *
 * @param inputByteArray the source image in a supported format.
 * @param inputImage the source {@link Image}.
 * @param inputStream the source image in a supported format.
 * @param width the new width or 0 to disable scaling.
 * @param height the new height or 0 to disable scaling.
 * @param scaleType the {@link ScaleType} to use when scaling.
 * @param outputFormat the {@link ImageFormat} to convert to or
 *            {@link ImageFormat#SOURCE} to preserve source format.
 *            Overridden by {@code outputProfile}.
 * @param outputProfile the {@link DLNAImageProfile} to convert to. This
 *            overrides {@code outputFormat}.
 * @param dlnaCompliant whether or not the output image should be restricted
 *            to DLNA compliance. This also means that the output can be
 *            safely cast to {@link DLNAImage}.
 * @param dlnaThumbnail whether or not the output image should be restricted
 *            to DLNA thumbnail compliance. This also means that the output
 *            can be safely cast to {@link DLNAThumbnail}.
 * @param padToSize whether padding should be used if source aspect doesn't
 *            match target aspect.
 * @return The scaled and/or converted image or {@code null} if the source
 *         is {@code null}.
 * @throws IOException if the operation fails.
 */
protected static Image transcodeImage(byte[] inputByteArray, Image inputImage, InputStream inputStream, int width, int height, ScaleType scaleType, ImageFormat outputFormat, DLNAImageProfile outputProfile, boolean dlnaCompliant, boolean dlnaThumbnail, boolean padToSize) throws IOException {
    if (inputByteArray == null && inputStream == null && inputImage == null) {
        return null;
    }
    if ((inputByteArray != null & inputImage != null) || (inputByteArray != null & inputStream != null) || (inputImage != null & inputStream != null)) {
        throw new IllegalArgumentException("Use either inputByteArray, inputImage or inputStream");
    }
    boolean trace = LOGGER.isTraceEnabled();
    if (trace) {
        StringBuilder sb = new StringBuilder();
        if (scaleType != null) {
            sb.append("ScaleType = ").append(scaleType);
        }
        if (width > 0 && height > 0) {
            if (sb.length() > 0) {
                sb.append(", ");
            }
            sb.append("Width = ").append(width).append(", Height = ").append(height);
        }
        if (sb.length() > 0) {
            sb.append(", ");
        }
        sb.append("PadToSize = ").append(padToSize ? "True" : "False");
        LOGGER.trace("Converting {} image source to {} format and type {} using the following parameters: {}", inputByteArray != null ? "byte array" : inputImage != null ? "Image" : "input stream", outputProfile != null ? outputProfile : outputFormat, dlnaThumbnail ? "DLNAThumbnail" : dlnaCompliant ? "DLNAImage" : "Image", sb);
    }
    ImageIO.setUseCache(false);
    dlnaCompliant = dlnaCompliant || dlnaThumbnail;
    if (inputImage != null) {
        inputByteArray = inputImage.getBytes(false);
    } else if (inputStream != null) {
        inputByteArray = ImagesUtil.toByteArray(inputStream);
    }
    // outputProfile overrides outputFormat
    if (outputProfile != null) {
        if (dlnaThumbnail && outputProfile.equals(DLNAImageProfile.GIF_LRG)) {
            outputProfile = DLNAImageProfile.JPEG_LRG;
        }
        // Default to correct ScaleType for the profile
        if (scaleType == null) {
            if (DLNAImageProfile.JPEG_RES_H_V.equals(outputProfile)) {
                scaleType = ScaleType.EXACT;
            } else {
                scaleType = ScaleType.MAX;
            }
        }
        outputFormat = ImageFormat.toImageFormat(outputProfile);
        width = width > 0 ? width : outputProfile.getMaxWidth();
        height = height > 0 ? height : outputProfile.getMaxHeight();
    } else if (scaleType == null) {
        scaleType = ScaleType.MAX;
    }
    ImageReaderResult inputResult;
    try {
        inputResult = ImageIOTools.read(new ByteArrayInputStream(inputByteArray));
    } catch (IIOException e) {
        throw new UnknownFormatException("Unable to read image format", e);
    }
    if (inputResult.bufferedImage == null || inputResult.imageFormat == null) {
        // ImageIO doesn't support the image format
        throw new UnknownFormatException("Failed to transform image because the source format is unknown");
    }
    if (outputFormat == null || outputFormat == ImageFormat.SOURCE) {
        outputFormat = inputResult.imageFormat;
    }
    BufferedImage bufferedImage = inputResult.bufferedImage;
    boolean reencode = false;
    if (outputProfile == null && dlnaCompliant) {
        // if the source image has alpha and JPEG if not.
        switch(outputFormat) {
            case GIF:
                if (dlnaThumbnail) {
                    outputFormat = ImageFormat.JPEG;
                }
                break;
            case JPEG:
            case PNG:
                break;
            default:
                if (bufferedImage.getColorModel().hasAlpha()) {
                    outputFormat = ImageFormat.PNG;
                } else {
                    outputFormat = ImageFormat.JPEG;
                }
        }
    }
    Metadata metadata = null;
    ExifOrientation orientation;
    if (inputImage != null && inputImage.getImageInfo() != null) {
        orientation = inputImage.getImageInfo().getExifOrientation();
    } else {
        try {
            metadata = getMetadata(inputByteArray, inputResult.imageFormat);
        } catch (IOException | ImageProcessingException e) {
            LOGGER.error("Failed to read input image metadata: {}", e.getMessage());
            LOGGER.trace("", e);
            metadata = new Metadata();
        }
        if (metadata == null) {
            metadata = new Metadata();
        }
        orientation = parseExifOrientation(metadata);
    }
    if (orientation != ExifOrientation.TOP_LEFT) {
        // Rotate the image before doing all the other checks
        BufferedImage oldBufferedImage = bufferedImage;
        bufferedImage = Thumbnails.of(bufferedImage).scale(1.0d).addFilter(ExifFilterUtils.getFilterForOrientation(orientation.getThumbnailatorOrientation())).asBufferedImage();
        oldBufferedImage.flush();
        // Re-parse the metadata after rotation as these are newly generated.
        ByteArrayOutputStream tmpOutputStream = new ByteArrayOutputStream(inputByteArray.length);
        Thumbnails.of(bufferedImage).scale(1.0d).outputFormat(outputFormat.toString()).toOutputStream(tmpOutputStream);
        try {
            metadata = getMetadata(tmpOutputStream.toByteArray(), outputFormat);
        } catch (IOException | ImageProcessingException e) {
            LOGGER.debug("Failed to read rotated image metadata: {}", e.getMessage());
            LOGGER.trace("", e);
            metadata = new Metadata();
        }
        if (metadata == null) {
            metadata = new Metadata();
        }
        reencode = true;
    }
    if (outputProfile == null && dlnaCompliant) {
        // Set a suitable output profile.
        if (width < 1 || height < 1) {
            outputProfile = DLNAImageProfile.getClosestDLNAProfile(bufferedImage.getWidth(), bufferedImage.getHeight(), outputFormat, true);
            width = outputProfile.getMaxWidth();
            height = outputProfile.getMaxHeight();
        } else {
            outputProfile = DLNAImageProfile.getClosestDLNAProfile(calculateScaledResolution(bufferedImage.getWidth(), bufferedImage.getHeight(), scaleType, width, height), outputFormat, true);
            width = Math.min(width, outputProfile.getMaxWidth());
            height = Math.min(height, outputProfile.getMaxHeight());
        }
        if (DLNAImageProfile.JPEG_RES_H_V.equals(outputProfile)) {
            scaleType = ScaleType.EXACT;
        }
    }
    boolean convertColors = bufferedImage.getType() == BufferedImageType.TYPE_CUSTOM.getTypeId() || bufferedImage.getType() == BufferedImageType.TYPE_BYTE_BINARY.getTypeId() || bufferedImage.getColorModel().getColorSpace().getType() != ColorSpaceType.TYPE_RGB.getTypeId();
    // Impose DLNA format restrictions
    if (!reencode && outputFormat == inputResult.imageFormat && outputProfile != null) {
        DLNAComplianceResult complianceResult;
        switch(outputFormat) {
            case GIF:
            case JPEG:
            case PNG:
                ImageInfo imageInfo;
                // metadata is only null at this stage if inputImage != null and no rotation was necessary
                if (metadata == null) {
                    imageInfo = inputImage.getImageInfo();
                }
                imageInfo = ImageInfo.create(bufferedImage.getWidth(), bufferedImage.getHeight(), inputResult.imageFormat, ImageInfo.SIZE_UNKNOWN, bufferedImage.getColorModel(), metadata, false, true);
                complianceResult = DLNAImageProfile.checkCompliance(imageInfo, outputProfile);
                break;
            default:
                throw new IllegalStateException("Unexpected image format: " + outputFormat);
        }
        reencode = reencode || convertColors || !complianceResult.isFormatCorrect() || !complianceResult.isColorsCorrect();
        ;
        if (!complianceResult.isResolutionCorrect()) {
            width = width > 0 && complianceResult.getMaxWidth() > 0 ? Math.min(width, complianceResult.getMaxWidth()) : width > 0 ? width : complianceResult.getMaxWidth();
            height = height > 0 && complianceResult.getMaxHeight() > 0 ? Math.min(height, complianceResult.getMaxHeight()) : height > 0 ? height : complianceResult.getMaxHeight();
        }
        if (trace) {
            if (complianceResult.isAllCorrect()) {
                LOGGER.trace("Image conversion DLNA compliance check: The source image is compliant");
            } else {
                LOGGER.trace("Image conversion DLNA compliance check for {}: " + "The source image colors are {}, format is {} and resolution ({} x {}) is {}.\nFailures:\n  {}", outputProfile, complianceResult.isColorsCorrect() ? "compliant" : "non-compliant", complianceResult.isFormatCorrect() ? "compliant" : "non-compliant", bufferedImage.getWidth(), bufferedImage.getHeight(), complianceResult.isResolutionCorrect() ? "compliant" : "non-compliant", StringUtils.join(complianceResult.getFailures(), "\n  "));
            }
        }
    }
    if (convertColors) {
        // Preserve alpha channel if the output format supports it
        BufferedImageType outputImageType;
        if ((outputFormat == ImageFormat.PNG || outputFormat == ImageFormat.PSD) && bufferedImage.getColorModel().getNumComponents() == 4) {
            outputImageType = bufferedImage.isAlphaPremultiplied() ? BufferedImageType.TYPE_4BYTE_ABGR_PRE : BufferedImageType.TYPE_4BYTE_ABGR;
        } else {
            outputImageType = BufferedImageType.TYPE_3BYTE_BGR;
        }
        BufferedImage convertedImage = new BufferedImage(bufferedImage.getWidth(), bufferedImage.getHeight(), outputImageType.getTypeId());
        ColorConvertOp colorConvertOp = new ColorConvertOp(null);
        colorConvertOp.filter(bufferedImage, convertedImage);
        bufferedImage.flush();
        bufferedImage = convertedImage;
        reencode = true;
    }
    if (width < 1 || height < 1 || (scaleType == ScaleType.MAX && bufferedImage.getWidth() <= width && bufferedImage.getHeight() <= height) || (scaleType == ScaleType.EXACT && bufferedImage.getWidth() == width && bufferedImage.getHeight() == height)) {
        // No resize, just convert
        if (!reencode && inputResult.imageFormat == outputFormat) {
            // Nothing to do, just return source
            // metadata is only null at this stage if inputImage != null
            Image result;
            if (dlnaThumbnail) {
                result = metadata == null ? new DLNAThumbnail(inputImage, outputProfile, false) : new DLNAThumbnail(inputByteArray, outputFormat, bufferedImage, metadata, outputProfile, false);
            } else if (dlnaCompliant) {
                result = metadata == null ? new DLNAImage(inputImage, outputProfile, false) : new DLNAImage(inputByteArray, outputFormat, bufferedImage, metadata, outputProfile, false);
            } else {
                result = metadata == null ? new Image(inputImage, false) : new Image(inputByteArray, outputFormat, bufferedImage, metadata, false);
            }
            bufferedImage.flush();
            if (trace) {
                LOGGER.trace("No conversion is needed, returning source image with width = {}, height = {} and output {}.", bufferedImage.getWidth(), bufferedImage.getHeight(), dlnaCompliant && outputProfile != null ? "profile: " + outputProfile : "format: " + outputFormat);
            }
            return result;
        } else if (!reencode) {
            // Convert format
            reencode = true;
        }
    } else {
        boolean force = DLNAImageProfile.JPEG_RES_H_V.equals(outputProfile);
        BufferedImage oldBufferedImage = bufferedImage;
        if (padToSize && force) {
            bufferedImage = Thumbnails.of(bufferedImage).forceSize(width, height).addFilter(new Canvas(width, height, Positions.CENTER, Color.BLACK)).asBufferedImage();
        } else if (padToSize) {
            bufferedImage = Thumbnails.of(bufferedImage).size(width, height).addFilter(new Canvas(width, height, Positions.CENTER, Color.BLACK)).asBufferedImage();
        } else if (force) {
            bufferedImage = Thumbnails.of(bufferedImage).forceSize(width, height).asBufferedImage();
        } else {
            bufferedImage = Thumbnails.of(bufferedImage).size(width, height).asBufferedImage();
        }
        oldBufferedImage.flush();
    }
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    Thumbnails.of(bufferedImage).scale(1.0d).outputFormat(outputFormat.toString()).outputQuality(1.0f).toOutputStream(outputStream);
    byte[] outputByteArray = outputStream.toByteArray();
    Image result;
    if (dlnaThumbnail) {
        result = new DLNAThumbnail(outputByteArray, bufferedImage.getWidth(), bufferedImage.getHeight(), outputFormat, null, null, outputProfile, false);
    } else if (dlnaCompliant) {
        result = new DLNAImage(outputByteArray, bufferedImage.getWidth(), bufferedImage.getHeight(), outputFormat, null, null, outputProfile, false);
    } else {
        result = new Image(outputByteArray, bufferedImage.getWidth(), bufferedImage.getHeight(), outputFormat, null, null, true, false);
    }
    if (trace) {
        StringBuilder sb = new StringBuilder();
        sb.append("Convert colors = ").append(convertColors ? "True" : "False").append(", Re-encode = ").append(reencode ? "True" : "False");
        LOGGER.trace("Finished converting {} {} image{}. Output image resolution: {}, {}. Flags: {}", inputResult.width + "×" + inputResult.height, inputResult.imageFormat, orientation != ExifOrientation.TOP_LEFT ? " with orientation " + orientation : "", bufferedImage.getWidth() + "×" + bufferedImage.getHeight(), dlnaCompliant && outputProfile != null ? "profile: " + outputProfile : "format: " + outputFormat, sb);
    }
    bufferedImage.flush();
    return result;
}
Also used : ImageProcessingException(com.drew.imaging.ImageProcessingException) BufferedImageType(net.pms.util.BufferedImageType) ImageReaderResult(net.pms.image.ImageIOTools.ImageReaderResult) Canvas(net.coobird.thumbnailator.filters.Canvas) Metadata(com.drew.metadata.Metadata) IIOException(javax.imageio.IIOException) IIOException(javax.imageio.IIOException) BufferedImage(java.awt.image.BufferedImage) DLNAImage(net.pms.dlna.DLNAImage) BufferedImage(java.awt.image.BufferedImage) DLNAThumbnail(net.pms.dlna.DLNAThumbnail) DLNAImage(net.pms.dlna.DLNAImage) ColorConvertOp(java.awt.image.ColorConvertOp) UnknownFormatException(net.pms.util.UnknownFormatException) DLNAComplianceResult(net.pms.dlna.DLNAImageProfile.DLNAComplianceResult)

Example 4 with UnknownFormatException

use of net.pms.util.UnknownFormatException in project UniversalMediaServer by UniversalMediaServer.

the class ImageIOTools method readImageInfo.

/**
 * Tries to gather the data needed to populate a {@link ImageInfo} instance
 * describing the input image.
 *
 * <p>
 * This method does not close {@code inputStream}.
 *
 * @param inputStream the image whose information to gather.
 * @param size the size of the image in bytes or
 *             {@link ImageInfo#SIZE_UNKNOWN} if it can't be determined.
 * @param metadata the {@link Metadata} instance to embed in the resulting
 *                 {@link ImageInfo} instance.
 * @param applyExifOrientation whether or not Exif orientation should be
 *            compensated for when setting width and height. This will also
 *            reset the Exif orientation information. <b>Changes will be
 *            applied to the {@code metadata} argument instance</b>.
 * @return An {@link ImageInfo} instance describing the input image.
 * @throws UnknownFormatException if the format could not be determined.
 * @throws IOException if an IO error occurred.
 */
public static ImageInfo readImageInfo(InputStream inputStream, long size, Metadata metadata, boolean applyExifOrientation) throws IOException {
    if (inputStream == null) {
        throw new IllegalArgumentException("input == null!");
    }
    try (ImageInputStream stream = createImageInputStream(inputStream)) {
        Iterator<?> iter = ImageIO.getImageReaders(stream);
        if (!iter.hasNext()) {
            throw new UnknownFormatException("Unable to find a suitable image reader");
        }
        ImageReader reader = (ImageReader) iter.next();
        try {
            int width = -1;
            int height = -1;
            ImageFormat format = ImageFormat.toImageFormat(reader.getFormatName());
            if (format == null) {
                throw new UnknownFormatException("Unable to determine image format");
            }
            ColorModel colorModel = null;
            try {
                reader.setInput(stream, true, true);
                Iterator<ImageTypeSpecifier> iterator = reader.getImageTypes(0);
                if (iterator.hasNext()) {
                    colorModel = iterator.next().getColorModel();
                }
                width = reader.getWidth(0);
                height = reader.getHeight(0);
            } catch (RuntimeException e) {
                throw new ImageIORuntimeException("Error reading image information: " + e.getMessage(), e);
            }
            boolean imageIOSupport;
            if (format == ImageFormat.TIFF) {
                // but fails when it actually tries, so we have to test it.
                try {
                    ImageReadParam param = reader.getDefaultReadParam();
                    param.setSourceRegion(new Rectangle(1, 1));
                    reader.read(0, param);
                    imageIOSupport = true;
                } catch (Exception e) {
                    // Catch anything here, we simply want to test if it fails.
                    imageIOSupport = false;
                }
            } else {
                imageIOSupport = true;
            }
            ImageInfo imageInfo = ImageInfo.create(width, height, format, size, colorModel, metadata, applyExifOrientation, imageIOSupport);
            return imageInfo;
        } finally {
            reader.dispose();
        }
    }
}
Also used : ImageInputStream(javax.imageio.stream.ImageInputStream) Rectangle(java.awt.Rectangle) ImageTypeSpecifier(javax.imageio.ImageTypeSpecifier) IOException(java.io.IOException) IIOException(javax.imageio.IIOException) ImageIORuntimeException(net.pms.image.ImageIORuntimeException) UnknownFormatException(net.pms.util.UnknownFormatException) ImageIORuntimeException(net.pms.image.ImageIORuntimeException) ImageReadParam(javax.imageio.ImageReadParam) ImageIORuntimeException(net.pms.image.ImageIORuntimeException) ColorModel(java.awt.image.ColorModel) UnknownFormatException(net.pms.util.UnknownFormatException) ImageReader(javax.imageio.ImageReader)

Example 5 with UnknownFormatException

use of net.pms.util.UnknownFormatException in project UniversalMediaServer by UniversalMediaServer.

the class ImageIOTools method read.

/**
 * A copy of {@link ImageIO#read(ImageInputStream)} that returns
 * {@link ImageReaderResult} instead of {@link BufferedImage}. This lets
 * information about the detected format be retained.
 *
 * <b>
 * This method consumes and closes {@code stream}.
 * </b>
 *
 * @param stream an {@link ImageInputStream} to read from.
 *
 * @see ImageIO#read(ImageInputStream)
 */
public static ImageReaderResult read(ImageInputStream stream) throws IOException {
    if (stream == null) {
        throw new IllegalArgumentException("stream == null!");
    }
    try {
        Iterator<?> iter = ImageIO.getImageReaders(stream);
        if (!iter.hasNext()) {
            throw new UnknownFormatException("Unable to find a suitable image reader");
        }
        ImageFormat inputFormat = null;
        BufferedImage bufferedImage = null;
        ImageReader reader = (ImageReader) iter.next();
        try {
            // Store the parsing result
            inputFormat = ImageFormat.toImageFormat(reader.getFormatName());
            reader.setInput(stream, true, true);
            bufferedImage = reader.read(0, reader.getDefaultReadParam());
        } finally {
            reader.dispose();
        }
        return bufferedImage != null ? new ImageReaderResult(bufferedImage, inputFormat) : null;
    } catch (RuntimeException e) {
        throw new ImageIORuntimeException("An error occurred while trying to read image: " + e.getMessage(), e);
    } finally {
        stream.close();
    }
}
Also used : ImageIORuntimeException(net.pms.image.ImageIORuntimeException) ImageIORuntimeException(net.pms.image.ImageIORuntimeException) UnknownFormatException(net.pms.util.UnknownFormatException) ImageReader(javax.imageio.ImageReader) BufferedImage(java.awt.image.BufferedImage)

Aggregations

UnknownFormatException (net.pms.util.UnknownFormatException)8 IIOException (javax.imageio.IIOException)3 ImageReader (javax.imageio.ImageReader)3 ImageIORuntimeException (net.pms.image.ImageIORuntimeException)3 ImageProcessingException (com.drew.imaging.ImageProcessingException)2 Metadata (com.drew.metadata.Metadata)2 BufferedImage (java.awt.image.BufferedImage)2 IOException (java.io.IOException)2 ImageInputStream (javax.imageio.stream.ImageInputStream)2 ParseException (net.pms.util.ParseException)2 FileType (com.drew.imaging.FileType)1 Rectangle (java.awt.Rectangle)1 ColorConvertOp (java.awt.image.ColorConvertOp)1 ColorModel (java.awt.image.ColorModel)1 EOFException (java.io.EOFException)1 File (java.io.File)1 Matcher (java.util.regex.Matcher)1 ImageReadParam (javax.imageio.ImageReadParam)1 ImageTypeSpecifier (javax.imageio.ImageTypeSpecifier)1 Canvas (net.coobird.thumbnailator.filters.Canvas)1