Home | History | Annotate | Download | only in burst
      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.burst;
     18 
     19 import android.hardware.camera2.CameraAccessException;
     20 import android.hardware.camera2.CameraDevice;
     21 import android.hardware.camera2.CaptureRequest;
     22 import android.hardware.camera2.TotalCaptureResult;
     23 import android.util.Range;
     24 import android.view.Surface;
     25 
     26 import com.android.camera.async.BufferQueue;
     27 import com.android.camera.async.BufferQueue.BufferQueueClosedException;
     28 import com.android.camera.async.Lifetime;
     29 import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionClosedException;
     30 import com.android.camera.one.v2.commands.CameraCommand;
     31 import com.android.camera.one.v2.core.CaptureStream;
     32 import com.android.camera.one.v2.core.FrameServer;
     33 import com.android.camera.one.v2.core.Request;
     34 import com.android.camera.one.v2.core.RequestBuilder;
     35 import com.android.camera.one.v2.core.RequestTemplate;
     36 import com.android.camera.one.v2.core.ResourceAcquisitionFailedException;
     37 import com.android.camera.one.v2.core.ResponseListener;
     38 import com.android.camera.one.v2.imagesaver.MetadataImage;
     39 import com.android.camera.one.v2.photo.MetadataFuture;
     40 import com.android.camera.one.v2.sharedimagereader.ManagedImageReader;
     41 import com.android.camera.one.v2.sharedimagereader.imagedistributor.ImageStream;
     42 import com.android.camera.util.ApiHelper;
     43 
     44 import java.util.ArrayList;
     45 import java.util.Arrays;
     46 import java.util.List;
     47 
     48 import javax.annotation.ParametersAreNonnullByDefault;
     49 
     50 @ParametersAreNonnullByDefault
     51 public class BurstCaptureCommand implements CameraCommand {
     52     /**
     53      * Template to use for the burst capture.
     54      */
     55     private static final int BURST_TEMPLATE_TYPE = CameraDevice.TEMPLATE_VIDEO_SNAPSHOT;
     56 
     57     private final FrameServer mFrameServer;
     58     private final RequestBuilder.Factory mBuilderFactory;
     59     private final ManagedImageReader mManagedImageReader;
     60     private final Surface mBurstInputSurface;
     61     private final EvictionHandler mBurstEvictionHandler;
     62     private final BurstController mBurstController;
     63     private final Runnable mRestorePreviewCommand;
     64     /**
     65      *  The max images supported by the {@link ImageStream}.
     66      */
     67     private final int mMaxImageCount;
     68     private final Lifetime mBurstLifetime;
     69 
     70     /**
     71      * Initializes a new burst capture command.
     72      *
     73      * @param frameServer the {@link FrameServer} instance for creating session
     74      * @param builder factory to use for creating the {@link Request} for burst
     75      *            capture
     76      * @param managedImageReader the factory to use for creating a stream of
     77      *            images
     78      * @param burstInputSurface the input surface to use for streaming preview
     79      *            frames to burst
     80      * @param lifetime the lifetime of the burst, the burst stops capturing
     81      *            images once the lifetime is closed
     82      * @param burstEvictionHandler the eviction handler to use for
     83      *            {@link RingBuffer}
     84      * @param burstController the burst controller
     85      * @param restorePreviewCommand the command to run to restore the preview,
     86      *            once burst capture is complete
     87      * @param maxImageCount the maximum number of images supported by the image
     88      *            reader
     89      */
     90     public BurstCaptureCommand(FrameServer frameServer, RequestBuilder.Factory builder,
     91             ManagedImageReader managedImageReader, Surface burstInputSurface,
     92             Lifetime lifetime,
     93             EvictionHandler burstEvictionHandler,
     94             BurstController burstController,
     95             Runnable restorePreviewCommand,
     96             int maxImageCount) {
     97         mFrameServer = frameServer;
     98         mBuilderFactory = new RequestTemplate(builder);
     99         mManagedImageReader = managedImageReader;
    100         mBurstInputSurface = burstInputSurface;
    101         mBurstLifetime = lifetime;
    102         mBurstEvictionHandler = burstEvictionHandler;
    103         mBurstController = burstController;
    104         mRestorePreviewCommand = restorePreviewCommand;
    105         mMaxImageCount = maxImageCount;
    106     }
    107 
    108     @Override
    109     public void run() throws InterruptedException, CameraAccessException,
    110             CameraCaptureSessionClosedException, ResourceAcquisitionFailedException {
    111         List<MetadataImage> capturedImages = new ArrayList<>();
    112         try (FrameServer.Session session = mFrameServer.createExclusiveSession()) {
    113             // Create a ring buffer and with the passed burst eviction
    114             // handler and insert images in it from the image stream.
    115             // The ring buffer size is one less than the image count.
    116             int ringBufferSize = mMaxImageCount - 1;
    117             try (RingBuffer<MetadataImage> ringBuffer =
    118                     new RingBuffer<MetadataImage>(ringBufferSize, mBurstEvictionHandler)) {
    119                 try (ImageStream imageStream =
    120                         mManagedImageReader.createStream(mMaxImageCount)) {
    121                     mBurstLifetime.add(imageStream);
    122 
    123                     // Use the video snapshot template for the burst.
    124                     RequestBuilder photoRequest =
    125                             mBuilderFactory.create(BURST_TEMPLATE_TYPE);
    126                     photoRequest.setParam(CaptureRequest.CONTROL_AF_MODE,
    127                             CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO);
    128                     checkAndApplyNexus5FrameRateWorkaround(photoRequest);
    129 
    130                     photoRequest.addStream(imageStream);
    131                     // Hook up the camera stream to burst input surface.
    132                     photoRequest.addStream(new CaptureStream() {
    133                             @Override
    134                         public Surface bind(BufferQueue<Long> timestamps)
    135                                 throws InterruptedException,
    136                                 ResourceAcquisitionFailedException {
    137                             return mBurstInputSurface;
    138                         }
    139                     });
    140 
    141                     // Hook the response listener to invoke eviction handler
    142                     // frame capture result.
    143                     photoRequest.addResponseListener(new ResponseListener() {
    144                         @Override
    145                         public void onCompleted(TotalCaptureResult result) {
    146                             final long timestamp = result.get(TotalCaptureResult.SENSOR_TIMESTAMP);
    147                             mBurstEvictionHandler.onFrameCaptureResultAvailable(timestamp, result);
    148                         }
    149                     });
    150                     session.submitRequest(Arrays.asList(photoRequest.build()),
    151                             FrameServer.RequestType.REPEATING);
    152 
    153                     try {
    154                         while (!imageStream.isClosed()) {
    155                             // metadata
    156                             MetadataFuture metadataFuture = new MetadataFuture();
    157                             photoRequest.addResponseListener(metadataFuture);
    158 
    159                             ringBuffer.insertImage(new MetadataImage(imageStream.getNext(),
    160                                     metadataFuture.getMetadata()));
    161                         }
    162                     } catch (BufferQueueClosedException e) {
    163                         // This is normal. the image stream was closed.
    164                     }
    165                 } finally {
    166                     // Burst was completed call remove the images from the ring
    167                     // buffer.
    168                     capturedImages = ringBuffer.getAndRemoveAllImages();
    169                 }
    170             }
    171         } finally {
    172             try {
    173                 // Note: BurstController will release images after use
    174                 mBurstController.processBurstResults(capturedImages);
    175             } finally {
    176                 // Switch back to the old request.
    177                 mRestorePreviewCommand.run();
    178             }
    179         }
    180     }
    181 
    182     /**
    183      * On Nexus 5 limit frame rate to 24 fps. See b/18950682.
    184      */
    185     private static void checkAndApplyNexus5FrameRateWorkaround(RequestBuilder request) {
    186         if (ApiHelper.IS_NEXUS_5) {
    187             // For burst limit the frame rate to 24 fps.
    188             Range<Integer> frameRateBackOff = new Range<>(7, 24);
    189             request.setParam(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, frameRateBackOff);
    190         }
    191     }
    192 }
    193