Image metadata extraction using only necessary bytes?

219 Views Asked by At

I have a programme that handles thousands of images as well as videos. I want to be able to read date-time metadata from them without having to save the whole image in memory. Currently the images are passed through my programme using an assortment of streams.

Eg. creating a thumbnail image:

public static void createThumbnailImage(Media media, TmpMedia tmpMedia) throws IOException {
        PipedInputStream mediaIn = new PipedInputStream();
        PipedOutputStream mediaOut = new PipedOutputStream(mediaIn);
        new Thread(() -> {
            try {
                App.storage.getMedia(media, mediaOut);
            } catch (IOException e) {
                LOGGER.log(Level.WARNING, "Error getting Media for thumbnail", e);
            }
        }).start();
        // Rest of code...
}

For one of my steps I require the date-time information. I hope that I only have to feed metadata-extractor a necessary number of bytes while storing them in a buffer, before getting the information and continuing as normal. Just saving all the data in a buffer is definitely not an option as my programme would run out of memory.

1

There are 1 best solutions below

0
Hein Gertenbach On

By default the metadata-extractor only reads the necessary segment bytes, therefore it is possible to simply store all the bytes that it uses in a buffer until the metadata-extractor finishes.

Using this class you get the metadata from an InputStream after which you can do with the remaining bytes what you want to.

public class MetadataStreamProcessor {

    private final BufferedInput in;

    public MetadataStreamProcessor(InputStream in) {
        this.in = new BufferedInput(in, 1024); // 1KB to start with
    }

    public Metadata readMetadata() throws IOException, ImageProcessingException {
        return ImageMetadataReader.readMetadata(in);
    }

    public void writeTo(OutputStream out) throws IOException {
        in.buffer.writeTo(out);

        int read;
        byte[] bytes = new byte[1024];
        while ((read = in.in.read(bytes)) != -1) {
            out.write(bytes, 0, read);
        }
    }

    private static final class BufferedInput extends InputStream {

        private final InputStream in;
        private final ByteArrayOutputStream buffer;

        public BufferedInput(InputStream in, int size) {
            this.in = in;
            buffer = new ByteArrayOutputStream(size);
        }

        @Override
        public int read() throws IOException {
            int read = in.read();
            if (read != -1) buffer.write(read);
            return read;
        }
    }
}