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