Home | History | Annotate | Download | only in zsl
      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