1 /* 2 * Copyright (C) 2014 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.sharedimagereader; 18 19 import android.media.ImageReader; 20 import android.view.Surface; 21 22 import com.android.camera.async.BufferQueue; 23 import com.android.camera.async.BufferQueueController; 24 import com.android.camera.async.ConcurrentBufferQueue; 25 import com.android.camera.async.Lifetime; 26 import com.android.camera.one.v2.camera2proxy.ImageProxy; 27 import com.android.camera.one.v2.core.CaptureStream; 28 import com.android.camera.one.v2.core.FrameServer; 29 import com.android.camera.one.v2.core.RequestBuilder; 30 import com.android.camera.one.v2.core.ResourceAcquisitionFailedException; 31 import com.android.camera.one.v2.sharedimagereader.imagedistributor.ImageDistributor; 32 import com.android.camera.one.v2.sharedimagereader.imagedistributor.ImageStream; 33 import com.android.camera.one.v2.sharedimagereader.ticketpool.ReservableTicketPool; 34 import com.android.camera.one.v2.sharedimagereader.ticketpool.TicketPool; 35 import com.android.camera.one.v2.sharedimagereader.util.ImageCloser; 36 37 /** 38 * Builds {@link CaptureStream}s which can share the same underlying 39 * {@link ImageReader}. 40 * <p> 41 * Example usage: 42 * 43 * <pre> 44 * RequestBuilder builder = ...; 45 * 46 * // Create a stream which holds 3 images. 47 * ImageStream stream = managedImageReader.createStream(3); 48 * 49 * builder.addStream(stream); 50 * builder.setParam(...); 51 * 52 * frameServer.sendRequest(builder.build()); 53 * frameServer.sendRequest(builder.build()); 54 * frameServer.sendRequest(builder.build()); 55 * 56 * // Synchronously receive the images as they arrive... 57 * ImageProxy image1 = stream.getNext(); 58 * ImageProxy image2 = stream.getNext(); 59 * ImageProxy image3 = stream.getNext(); 60 * 61 * // Close the stream when no more images are expected. 62 * stream.close(); 63 * 64 * // Process the Images... 65 * 66 * // Close the images. 67 * image1.close(); 68 * image2.close(); 69 * image3.close(); 70 * </pre> 71 */ 72 public class ManagedImageReader { 73 private final Lifetime mLifetime; 74 private final TicketPool mTicketPool; 75 /** 76 * The {@link ImageReader} surface. 77 */ 78 private final Surface mSurface; 79 80 private final ImageDistributor mImageDistributor; 81 82 /** 83 * @param lifetime 84 * @param ticketPool 85 * @param surface 86 * @param imageDistributor 87 */ ManagedImageReader(Lifetime lifetime, TicketPool ticketPool, Surface surface, ImageDistributor imageDistributor)88 public ManagedImageReader(Lifetime lifetime, TicketPool ticketPool, Surface surface, 89 ImageDistributor imageDistributor) { 90 mLifetime = lifetime; 91 mTicketPool = ticketPool; 92 mSurface = surface; 93 mImageDistributor = imageDistributor; 94 } 95 createUnallocatedStream(int capacity)96 private AllocatingImageStream createUnallocatedStream(int capacity) { 97 ReservableTicketPool ticketPool = new ReservableTicketPool(mTicketPool); 98 mLifetime.add(ticketPool); 99 100 ConcurrentBufferQueue<ImageProxy> imageStream = new ConcurrentBufferQueue<>(new 101 ImageCloser()); 102 mLifetime.add(imageStream); 103 104 BufferQueueController<ImageProxy> imageStreamController = new 105 TicketRequiredFilter(ticketPool, imageStream); 106 mLifetime.add(imageStreamController); 107 108 AllocatingImageStream stream = new AllocatingImageStream(capacity, 109 ticketPool, imageStream, imageStreamController, mImageDistributor, mSurface); 110 mLifetime.add(stream); 111 112 return stream; 113 } 114 115 /** 116 * Creates a logical bounded stream of images with the specified capacity. 117 * Note that the required image space will be allocated/acquired the first 118 * time {@link CaptureStream#bind(BufferQueue)} is called, but it will be 119 * reused on subsequent invocations. So, for example, the stream provider 120 * may be attached to multiple {@link RequestBuilder}s and the images for 121 * those requests will share the same ticket pool with size specified by the 122 * given capacity. 123 * 124 * @param capacity The maximum number of images which can be simultaneously 125 * held from the resulting image queue before images are dropped. 126 */ createStream(int capacity)127 public ImageStream createStream(int capacity) { 128 return createUnallocatedStream(capacity); 129 } 130 131 /** 132 * Creates a logical bounded stream of images with the specified capacity, 133 * blocking until the required capacity can be reserved. 134 * <p> 135 * Note: Unlike using {@link #createStream} with a {@link FrameServer}, this 136 * method cannot guarantee non-circular-wait to eliminate the possibility of 137 * deadlock. FrameServer's session lock has already been acquired. Thus, 138 * there is a possibility of deadlock if callers have not already acquired 139 * an exclusive session before calling this method. 140 * <p> 141 * TODO Use a CycleDetectingLockFactory to detect deadlock-prone misuse of 142 * this method, and document the required order of resource acquisition at a 143 * higher-level. 144 * 145 * @param capacity The maximum number of images which can be simultaneously 146 * held from the resulting image queue before images are dropped. 147 */ createPreallocatedStream(int capacity)148 public ImageStream createPreallocatedStream(int capacity) throws InterruptedException, 149 ResourceAcquisitionFailedException { 150 AllocatingImageStream stream = createUnallocatedStream(capacity); 151 try { 152 stream.allocate(); 153 return stream; 154 } catch (Exception e) { 155 // If allocation failed, close the stream before returning. 156 stream.close(); 157 throw e; 158 } 159 } 160 } 161