1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.camera.one.v2.photo.zsl; 18 19 import android.hardware.camera2.CameraAccessException; 20 import android.support.v4.util.Pair; 21 22 import com.android.camera.async.BufferQueue; 23 import com.android.camera.async.Updatable; 24 import com.android.camera.debug.Log; 25 import com.android.camera.debug.Logger; 26 import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionClosedException; 27 import com.android.camera.one.v2.camera2proxy.ImageProxy; 28 import com.android.camera.one.v2.camera2proxy.TotalCaptureResultProxy; 29 import com.android.camera.one.v2.core.ResourceAcquisitionFailedException; 30 import com.android.camera.one.v2.imagesaver.ImageSaver; 31 import com.android.camera.one.v2.photo.ImageCaptureCommand; 32 import com.android.camera.one.v2.sharedimagereader.metadatasynchronizer.MetadataPool; 33 import com.google.common.base.Predicate; 34 import com.google.common.util.concurrent.Futures; 35 36 import java.util.ArrayList; 37 import java.util.Collections; 38 import java.util.List; 39 import java.util.concurrent.CancellationException; 40 import java.util.concurrent.ExecutionException; 41 import java.util.concurrent.Future; 42 import java.util.concurrent.TimeUnit; 43 import java.util.concurrent.TimeoutException; 44 45 import javax.annotation.Nullable; 46 import javax.annotation.ParametersAreNonnullByDefault; 47 48 /** 49 * Captures images by first looking to the zsl ring buffer for acceptable (based 50 * on metadata) images. If no such images are available, a fallback 51 * ImageCaptureCommand is used instead. 52 */ 53 @ParametersAreNonnullByDefault 54 public class ZslImageCaptureCommand implements ImageCaptureCommand { 55 private final Logger mLog; 56 private final BufferQueue<ImageProxy> mZslRingBuffer; 57 private final MetadataPool mZslMetadataPool; 58 private final ImageCaptureCommand mFallbackCommand; 59 private final Predicate<TotalCaptureResultProxy> mMetadataFilter; 60 private final long mMaxLookBackNanos; 61 62 public ZslImageCaptureCommand(Logger.Factory logFactory, 63 BufferQueue<ImageProxy> zslRingBuffer, 64 MetadataPool zslMetadataPool, 65 ImageCaptureCommand fallbackCommand, 66 Predicate<TotalCaptureResultProxy> metadataFilter, 67 long maxLookBackNanos) { 68 mZslRingBuffer = zslRingBuffer; 69 mLog = logFactory.create(new Log.Tag("ZSLImageCaptureCmd")); 70 mZslMetadataPool = zslMetadataPool; 71 mFallbackCommand = fallbackCommand; 72 mMetadataFilter = metadataFilter; 73 mMaxLookBackNanos = maxLookBackNanos; 74 } 75 76 /** 77 * @return All images currently in the ring-buffer, ordered from oldest to 78 * most recent. 79 */ 80 private List<ImageProxy> getAllAvailableImages() throws InterruptedException, 81 BufferQueue.BufferQueueClosedException { 82 List<ImageProxy> images = new ArrayList<>(); 83 try { 84 // Keep grabbing images until there are no more immediately 85 // available in the ring buffer. 86 while (true) { 87 try { 88 images.add(mZslRingBuffer.getNext(0, TimeUnit.SECONDS)); 89 } catch (TimeoutException e) { 90 break; 91 } 92 } 93 } catch (Exception e) { 94 // Close the images to avoid leaking them, since they will not be 95 // returned to the caller. 96 for (ImageProxy image : images) { 97 image.close(); 98 } 99 throw e; 100 } 101 return images; 102 } 103 104 private List<ImageProxy> filterImagesWithinMaxLookBack(List<ImageProxy> images) { 105 if (images.isEmpty()) { 106 return Collections.emptyList(); 107 } 108 List<ImageProxy> filtered = new ArrayList<>(); 109 long mostRecentTimestamp = images.get(images.size() - 1).getTimestamp(); 110 long timestampThreshold = mostRecentTimestamp - mMaxLookBackNanos; 111 for (ImageProxy image : images) { 112 if (image.getTimestamp() > timestampThreshold) { 113 filtered.add(image); 114 } else { 115 image.close(); 116 } 117 } 118 return filtered; 119 } 120 121 @Nullable 122 private Pair<ImageProxy, TotalCaptureResultProxy> tryGetZslImage() throws InterruptedException, 123 BufferQueue.BufferQueueClosedException { 124 List<ImageProxy> images = filterImagesWithinMaxLookBack(getAllAvailableImages()); 125 ImageProxy imageToSave = null; 126 TotalCaptureResultProxy metadata = null; 127 try { 128 for (ImageProxy image : images) { 129 Future<TotalCaptureResultProxy> metadataFuture = 130 mZslMetadataPool.removeMetadataFuture(image.getTimestamp()); 131 try { 132 if (mMetadataFilter.apply(metadataFuture.get())) { 133 imageToSave = image; 134 metadata = metadataFuture.get(); 135 } 136 } catch (ExecutionException | CancellationException e) { 137 // If we cannot get metadata for an image, for whatever 138 // reason, assume it is not acceptable for capture. 139 } 140 } 141 } catch (Exception e) { 142 if (imageToSave != null) { 143 imageToSave.close(); 144 } 145 throw e; 146 } finally { 147 for (ImageProxy image : images) { 148 if (image != imageToSave) { 149 image.close(); 150 } 151 } 152 } 153 if (imageToSave == null) { 154 return null; 155 } else { 156 return new Pair<>(imageToSave, metadata); 157 } 158 } 159 160 @Override 161 public void run(Updatable<Void> imageExposeCallback, ImageSaver imageSaver) 162 throws InterruptedException, CameraAccessException, 163 CameraCaptureSessionClosedException, ResourceAcquisitionFailedException { 164 boolean mustCloseImageSaver = true; 165 try { 166 Pair<ImageProxy, TotalCaptureResultProxy> image = tryGetZslImage(); 167 if (image != null) { 168 mLog.i("ZSL image available"); 169 imageExposeCallback.update(null); 170 imageSaver.addFullSizeImage(image.first, Futures.immediateFuture(image.second)); 171 } else { 172 mLog.i("No ZSL image available, using fallback: " + mFallbackCommand); 173 mustCloseImageSaver = false; 174 mFallbackCommand.run(imageExposeCallback, imageSaver); 175 } 176 } catch (BufferQueue.BufferQueueClosedException e) { 177 // The zsl ring buffer has been closed, so do nothing since the 178 // system is shutting down. 179 } finally { 180 if (mustCloseImageSaver) { 181 imageSaver.close(); 182 } 183 } 184 } 185 } 186