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