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