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