18a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall/*
28a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall * Copyright (C) 2014 The Android Open Source Project
38a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall *
48a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall * Licensed under the Apache License, Version 2.0 (the "License");
58a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall * you may not use this file except in compliance with the License.
68a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall * You may obtain a copy of the License at
78a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall *
88a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall *      http://www.apache.org/licenses/LICENSE-2.0
98a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall *
108a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall * Unless required by applicable law or agreed to in writing, software
118a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall * distributed under the License is distributed on an "AS IS" BASIS,
128a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall * See the License for the specific language governing permissions and
148a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall * limitations under the License.
158a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall */
168a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall
178a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lallpackage com.android.camera.one.v2.imagesaver;
188a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall
198a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lallimport com.android.camera.one.v2.camera2proxy.ImageProxy;
20d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lallimport com.android.camera.one.v2.camera2proxy.TotalCaptureResultProxy;
218a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lallimport com.google.common.base.Optional;
22d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lallimport com.google.common.util.concurrent.ListenableFuture;
238a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall
248a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lallimport java.util.ArrayList;
258a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lallimport java.util.HashMap;
268a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lallimport java.util.List;
278a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lallimport java.util.Map;
288a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall
298a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lallimport javax.annotation.Nullable;
30d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lallimport javax.annotation.ParametersAreNonnullByDefault;
318a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall
328a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall/**
338a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall * Saves the last image in a burst.
348a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall */
35d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lall@ParametersAreNonnullByDefault
368a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lallpublic class MostRecentImageSaver implements ImageSaver {
378a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall    private final SingleImageSaver mSingleImageSaver;
388a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall    private final Map<Long, ImageProxy> mThumbnails;
39d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lall    private final Map<Long, MetadataImage> mFullSizeImages;
408a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall
41d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lall    public MostRecentImageSaver(SingleImageSaver singleImageSaver) {
428a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        mSingleImageSaver = singleImageSaver;
438a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        mThumbnails = new HashMap<>();
448a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        mFullSizeImages = new HashMap<>();
458a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall    }
468a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall
478a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall    @Override
48d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lall    public void addThumbnail(ImageProxy imageProxy) {
498a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        mThumbnails.put(imageProxy.getTimestamp(), imageProxy);
508a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        closeOlderImages();
518a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall    }
528a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall
538a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall    @Override
54d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lall    public void addFullSizeImage(ImageProxy imageProxy,
55d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lall            ListenableFuture<TotalCaptureResultProxy> metadata) {
56d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lall        mFullSizeImages.put(imageProxy.getTimestamp(), new MetadataImage(imageProxy, metadata));
578a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        closeOlderImages();
588a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall    }
598a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall
608a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall    @Override
618a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall    public void close() {
628a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        try {
63d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lall            MetadataImage fullSize = getLastImage();
648a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall            if (fullSize != null) {
658a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall                // Pop the image out of the map so that closeAllImages() does
668a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall                // not close it.
678a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall                mFullSizeImages.remove(fullSize.getTimestamp());
688a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall            } else {
698a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall                return;
708a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall            }
718a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall
728a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall            ImageProxy thumbnail = getThumbnail(fullSize.getTimestamp());
738a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall            if (thumbnail != null) {
748a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall                // Pop the image out of the map so that closeAllImages() does
758a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall                // not close it.
768a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall                mThumbnails.remove(thumbnail.getTimestamp());
778a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall            }
788a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall
79d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lall            mSingleImageSaver.saveAndCloseImage(fullSize, Optional.fromNullable(thumbnail),
80d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lall                    fullSize.getMetadata());
818a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        } finally {
828a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall            closeAllImages();
838a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        }
848a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall    }
858a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall
868a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall    private void closeAllImages() {
878a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        for (ImageProxy image : mThumbnails.values()) {
888a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall            image.close();
898a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        }
908a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall
918a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        for (ImageProxy image : mFullSizeImages.values()) {
928a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall            image.close();
938a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        }
948a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall    }
958a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall
96d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lall    private void closeOlderImages(long threshold, Map<Long, ? extends ImageProxy> imageMap) {
978a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        List<Long> toRemove = new ArrayList<>();
988a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        for (long imageTimestamp : imageMap.keySet()) {
998a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall            if (imageTimestamp < threshold) {
1008a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall                imageMap.get(imageTimestamp).close();
1018a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall                toRemove.add(imageTimestamp);
1028a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall            }
1038a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        }
1048a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        for (Long timestamp : toRemove) {
1058a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall            imageMap.remove(timestamp);
1068a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        }
1078a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall    }
1088a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall
1098a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall    private void closeOlderImages() {
1108a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        Optional<Long> timestampThreshold = getMostRecentFullSizeImageTimestamp();
1118a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        if (timestampThreshold.isPresent()) {
1128a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall            closeOlderImages(timestampThreshold.get(), mFullSizeImages);
1138a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall            closeOlderImages(timestampThreshold.get(), mThumbnails);
1148a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        }
1158a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall    }
1168a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall
1178a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall    private Optional<Long> getMostRecentFullSizeImageTimestamp() {
1188a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        if (mFullSizeImages.isEmpty()) {
1198a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall            return Optional.absent();
1208a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        }
1218a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        boolean pairFound = false;
1228a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        long oldestTimestamp = 0;
1238a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        for (ImageProxy image : mFullSizeImages.values()) {
1248a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall            long timestamp = image.getTimestamp();
1258a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall            if (!pairFound || timestamp > oldestTimestamp) {
1268a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall                oldestTimestamp = timestamp;
1278a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall                pairFound = true;
1288a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall            }
1298a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        }
1308a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        if (!pairFound) {
1318a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall            return Optional.absent();
1328a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        } else {
1338a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall            return Optional.of(oldestTimestamp);
1348a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        }
1358a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall    }
1368a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall
1378a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall    @Nullable
138d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lall    private MetadataImage getLastImage() {
1398a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        if (mFullSizeImages.isEmpty()) {
1408a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall            return null;
1418a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        }
142d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lall        MetadataImage lastImage = null;
143d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lall        for (MetadataImage image : mFullSizeImages.values()) {
1448a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall            if (lastImage == null || image.getTimestamp() > lastImage.getTimestamp()) {
1458a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall                lastImage = image;
1468a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall            }
1478a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        }
1488a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        return lastImage;
1498a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall    }
1508a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall
1518a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall    @Nullable
1528a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall    private ImageProxy getThumbnail(long timestamp) {
1538a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall        return mThumbnails.get(timestamp);
1548a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall    }
1558a09cc29b273e35128a03794e82dd4f329d35cc9Puneet Lall}
156