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      */
BurstCaptureCommand(FrameServer frameServer, RequestBuilder.Factory builder, ManagedImageReader managedImageReader, Surface burstInputSurface, Lifetime lifetime, EvictionHandler burstEvictionHandler, BurstController burstController, Runnable restorePreviewCommand, int maxImageCount)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
run()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      */
checkAndApplyNexus5FrameRateWorkaround(RequestBuilder request)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