Home | History | Annotate | Download | only in photo
      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;
     18 
     19 import static com.android.camera.one.v2.core.ResponseListeners.forFrameExposure;
     20 import static com.android.camera.one.v2.core.ResponseListeners.forPartialMetadata;
     21 
     22 import android.annotation.TargetApi;
     23 import android.hardware.camera2.CameraAccessException;
     24 import android.hardware.camera2.CaptureRequest;
     25 import android.os.Build;
     26 
     27 import com.android.camera.async.BufferQueue;
     28 import com.android.camera.async.Updatable;
     29 import com.android.camera.one.v2.autofocus.AETriggerResult;
     30 import com.android.camera.one.v2.autofocus.AFTriggerResult;
     31 import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionClosedException;
     32 import com.android.camera.one.v2.camera2proxy.ImageProxy;
     33 import com.android.camera.one.v2.camera2proxy.TotalCaptureResultProxy;
     34 import com.android.camera.one.v2.core.FrameServer;
     35 import com.android.camera.one.v2.core.Request;
     36 import com.android.camera.one.v2.core.RequestBuilder;
     37 import com.android.camera.one.v2.core.RequestTemplate;
     38 import com.android.camera.one.v2.core.ResourceAcquisitionFailedException;
     39 import com.android.camera.one.v2.imagesaver.ImageSaver;
     40 import com.android.camera.one.v2.sharedimagereader.ManagedImageReader;
     41 import com.android.camera.one.v2.sharedimagereader.imagedistributor.ImageStream;
     42 import com.google.common.util.concurrent.ListenableFuture;
     43 
     44 import java.util.ArrayList;
     45 import java.util.Arrays;
     46 import java.util.List;
     47 
     48 import javax.annotation.ParametersAreNonnullByDefault;
     49 
     50 /**
     51  * Captures a burst after waiting for AF and AE convergence.
     52  */
     53 @ParametersAreNonnullByDefault
     54 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
     55 class ConvergedImageCaptureCommand implements ImageCaptureCommand {
     56     private final ManagedImageReader mImageReader;
     57     private final FrameServer mFrameServer;
     58     private final RequestBuilder.Factory mScanRequestTemplate;
     59     private final RequestBuilder.Factory mRepeatingRequestBuilder;
     60     private final int mRepeatingRequestTemplate;
     61     private final int mStillCaptureRequestTemplate;
     62     private final List<RequestBuilder.Factory> mBurst;
     63 
     64     private final boolean mWaitForAEConvergence;
     65     private final boolean mWaitForAFConvergence;
     66 
     67     /**
     68      * Transforms a request template by resetting focus and exposure modes.
     69      */
     70     private static RequestBuilder.Factory resetFocusExposureModes(RequestBuilder.Factory template) {
     71         RequestTemplate result = new RequestTemplate(template);
     72         result.setParam(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
     73         result.setParam(CaptureRequest.CONTROL_AF_MODE,
     74                 CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
     75         result.setParam(CaptureRequest.CONTROL_AF_TRIGGER,
     76                 CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
     77         result.setParam(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
     78                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
     79         return result;
     80     }
     81 
     82     /**
     83      * @param imageReader Creates the {@link ImageStream} used for capturing
     84      *            images to be saved.
     85      * @param frameServer Used for interacting with the camera device.
     86      * @param repeatingRequestBuilder Creates request builders to use for
     87      *            repeating requests sent during the scanning phase and after
     88      *            capture is complete.
     89      * @param repeatingRequestTemplate The template type to use for repeating
     90      *            requests.
     91      * @param burst Creates request builders to use for each image captured from
     92      * @param waitForAEConvergence
     93      * @param waitForAFConvergence
     94      */
     95     public ConvergedImageCaptureCommand(ManagedImageReader imageReader, FrameServer frameServer,
     96             RequestBuilder.Factory repeatingRequestBuilder,
     97             int repeatingRequestTemplate, int stillCaptureRequestTemplate,
     98             List<RequestBuilder.Factory> burst, boolean waitForAEConvergence,
     99             boolean waitForAFConvergence) {
    100         mImageReader = imageReader;
    101         mFrameServer = frameServer;
    102         mRepeatingRequestBuilder = repeatingRequestBuilder;
    103         mRepeatingRequestTemplate = repeatingRequestTemplate;
    104         mStillCaptureRequestTemplate = stillCaptureRequestTemplate;
    105         mBurst = burst;
    106         mWaitForAEConvergence = waitForAEConvergence;
    107         mWaitForAFConvergence = waitForAFConvergence;
    108 
    109         mScanRequestTemplate = resetFocusExposureModes(repeatingRequestBuilder);
    110     }
    111 
    112     /**
    113      * Sends a request to take a picture and blocks until it completes.
    114      */
    115     @Override
    116     public void run(Updatable<Void> imageExposureUpdatable, ImageSaver imageSaver) throws
    117             InterruptedException, CameraAccessException, CameraCaptureSessionClosedException,
    118             ResourceAcquisitionFailedException {
    119         try (FrameServer.Session session = mFrameServer.createExclusiveSession()) {
    120             try (ImageStream imageStream = mImageReader.createPreallocatedStream(mBurst.size())) {
    121                 if (mWaitForAFConvergence) {
    122                     waitForAFConvergence(session);
    123                 }
    124                 if (mWaitForAEConvergence) {
    125                     waitForAEConvergence(session);
    126                 }
    127                 captureBurst(session, imageStream, imageExposureUpdatable, imageSaver);
    128             } finally {
    129                 // Always reset the repeating stream to ensure AF/AE are not
    130                 // locked when this exits.
    131                 // Note that this may still throw if the camera or session is
    132                 // closed.
    133                 resetRepeating(session);
    134             }
    135         } finally {
    136             imageSaver.close();
    137         }
    138     }
    139 
    140     private void waitForAFConvergence(FrameServer.Session session) throws CameraAccessException,
    141             InterruptedException, ResourceAcquisitionFailedException,
    142             CameraCaptureSessionClosedException {
    143         AFTriggerResult afStateMachine = new AFTriggerResult();
    144 
    145         RequestBuilder triggerBuilder = mScanRequestTemplate.create(mRepeatingRequestTemplate);
    146         triggerBuilder.setParam(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest
    147                 .CONTROL_AF_TRIGGER_START);
    148         triggerBuilder.addResponseListener(forPartialMetadata(afStateMachine));
    149 
    150         RequestBuilder idleBuilder = mScanRequestTemplate.create(mRepeatingRequestTemplate);
    151         idleBuilder.addResponseListener(forPartialMetadata(afStateMachine));
    152 
    153         session.submitRequest(Arrays.asList(idleBuilder.build()),
    154                 FrameServer.RequestType.REPEATING);
    155 
    156         session.submitRequest(Arrays.asList(triggerBuilder.build()),
    157                 FrameServer.RequestType.NON_REPEATING);
    158 
    159         // Block until the AF trigger is complete
    160         afStateMachine.get();
    161     }
    162 
    163     private void waitForAEConvergence(FrameServer.Session session) throws CameraAccessException,
    164             InterruptedException, ResourceAcquisitionFailedException,
    165             CameraCaptureSessionClosedException {
    166         AETriggerResult aeStateMachine = new AETriggerResult();
    167 
    168         RequestBuilder triggerBuilder = mScanRequestTemplate.create(mRepeatingRequestTemplate);
    169         triggerBuilder.setParam(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
    170                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
    171         triggerBuilder.addResponseListener(forPartialMetadata(aeStateMachine));
    172 
    173         RequestBuilder idleBuilder = mScanRequestTemplate.create(mRepeatingRequestTemplate);
    174         idleBuilder.addResponseListener(forPartialMetadata(aeStateMachine));
    175 
    176         session.submitRequest(Arrays.asList(idleBuilder.build()),
    177                 FrameServer.RequestType.REPEATING);
    178 
    179         session.submitRequest(Arrays.asList(triggerBuilder.build()),
    180                 FrameServer.RequestType.NON_REPEATING);
    181 
    182         // Wait until the ae state converges to a result.
    183         aeStateMachine.get();
    184     }
    185 
    186     private void captureBurst(FrameServer.Session session, ImageStream imageStream, Updatable<Void>
    187             imageExposureUpdatable, ImageSaver imageSaver) throws CameraAccessException,
    188             InterruptedException, ResourceAcquisitionFailedException,
    189             CameraCaptureSessionClosedException {
    190         List<Request> burstRequest = new ArrayList<>(mBurst.size());
    191         List<ListenableFuture<TotalCaptureResultProxy>> metadata = new ArrayList<>(mBurst.size());
    192         boolean first = true;
    193         for (RequestBuilder.Factory builderTemplate : mBurst) {
    194             RequestBuilder builder = builderTemplate.create(mStillCaptureRequestTemplate);
    195 
    196             builder.setParam(CaptureRequest.CONTROL_AF_MODE, CaptureRequest
    197                     .CONTROL_AF_MODE_CONTINUOUS_PICTURE);
    198             builder.setParam(CaptureRequest.CONTROL_CAPTURE_INTENT,
    199                     CaptureRequest.CONTROL_CAPTURE_INTENT_STILL_CAPTURE);
    200 
    201             if (first) {
    202                 first = false;
    203                 builder.addResponseListener(forFrameExposure(imageExposureUpdatable));
    204             }
    205 
    206             MetadataFuture metadataFuture = new MetadataFuture();
    207             builder.addResponseListener(metadataFuture);
    208             metadata.add(metadataFuture.getMetadata());
    209 
    210             builder.addStream(imageStream);
    211 
    212             burstRequest.add(builder.build());
    213         }
    214 
    215         session.submitRequest(burstRequest, FrameServer.RequestType.NON_REPEATING);
    216 
    217         for (int i = 0; i < mBurst.size(); i++) {
    218             try {
    219                 ImageProxy image = imageStream.getNext();
    220                 imageSaver.addFullSizeImage(image, metadata.get(i));
    221             } catch (BufferQueue.BufferQueueClosedException e) {
    222                 // No more images will be available, so just quit.
    223                 return;
    224             }
    225         }
    226     }
    227 
    228     private void resetRepeating(FrameServer.Session session) throws InterruptedException,
    229             CameraCaptureSessionClosedException, CameraAccessException,
    230             ResourceAcquisitionFailedException {
    231         RequestBuilder repeatingBuilder = mRepeatingRequestBuilder.create
    232                 (mRepeatingRequestTemplate);
    233         session.submitRequest(Arrays.asList(repeatingBuilder.build()),
    234                 FrameServer.RequestType.REPEATING);
    235 
    236         RequestBuilder triggerCancelBuilder = mRepeatingRequestBuilder
    237                 .create(mRepeatingRequestTemplate);
    238         triggerCancelBuilder.setParam(CaptureRequest.CONTROL_AF_TRIGGER,
    239                 CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
    240         session.submitRequest(Arrays.asList(triggerCancelBuilder.build()),
    241                 FrameServer.RequestType.NON_REPEATING);
    242 
    243         // Some devices (e.g. N6) implicitly lock AE after sending an
    244         // AE_PRECAPTURE trigger. (see bug: 19265647)
    245         // The implicit lock is released when a request with
    246         // INTENT_STILL_CAPTURE is taken.
    247 
    248         // However, if we never get to that point (because the command was
    249         // interrupted before the request for a photo was sent), then we must be
    250         // sure to cancel this implicit AE lock to resume normal AE behavior.
    251         // Sending a request for an explicit AE lock (followed, implicitly, by a
    252         // request from the current repeating request, which has AE lock off)
    253         // fixes the issue and results in normal AE behavior.
    254         RequestBuilder hackAETriggerCancelBuilder = mRepeatingRequestBuilder.create
    255                 (mRepeatingRequestTemplate);
    256         hackAETriggerCancelBuilder.setParam(CaptureRequest.CONTROL_AE_LOCK, true);
    257 
    258         session.submitRequest(Arrays.asList(hackAETriggerCancelBuilder.build()),
    259                 FrameServer.RequestType.NON_REPEATING);
    260     }
    261 }
    262