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
17package com.android.camera.one.v2.core;
18
19import java.util.ArrayList;
20import java.util.HashSet;
21import java.util.List;
22import java.util.Set;
23import java.util.concurrent.atomic.AtomicBoolean;
24
25import android.hardware.camera2.CameraAccessException;
26import android.hardware.camera2.CaptureRequest;
27import android.hardware.camera2.CaptureResult;
28import android.hardware.camera2.TotalCaptureResult;
29import android.view.Surface;
30
31import com.android.camera.async.BufferQueue;
32import com.android.camera.async.ConcurrentBufferQueue;
33import com.android.camera.async.Updatable;
34import com.android.camera.one.v2.camera2proxy.CaptureRequestBuilderProxy;
35import com.android.camera.one.v2.camera2proxy.CaptureResultProxy;
36import com.android.camera.one.v2.camera2proxy.TotalCaptureResultProxy;
37import com.android.camera.one.v2.common.TimestampResponseListener;
38
39/**
40 * Conveniently builds {@link Request}s.
41 */
42public class RequestBuilder {
43    public static interface Factory {
44        /**
45         * Creates a new RequestBuilder.
46         *
47         * @param templateType See
48         *            {@link android.hardware.camera2.CameraDevice#createCaptureRequest}
49         */
50        public RequestBuilder create(int templateType) throws CameraAccessException;
51    }
52
53    private static class UnregisteredStreamProvider implements RequestImpl
54            .Allocation {
55        private final CaptureStream mCaptureStream;
56        private final BufferQueue<Long> mTimestampQueue;
57        private final AtomicBoolean mAllocated;
58        private final CaptureRequestBuilderProxy mBuilderProxy;
59
60        private UnregisteredStreamProvider(CaptureStream captureStream,
61                BufferQueue<Long> timestampQueue,
62                CaptureRequestBuilderProxy builderProxy) {
63            mCaptureStream = captureStream;
64            mTimestampQueue = timestampQueue;
65            mAllocated = new AtomicBoolean(false);
66            mBuilderProxy = builderProxy;
67        }
68
69        public void allocate() throws InterruptedException, ResourceAcquisitionFailedException {
70            mBuilderProxy.addTarget(mCaptureStream.bind(mTimestampQueue));
71        }
72
73        @Override
74        public void abort() {
75            mTimestampQueue.close();
76        }
77    }
78
79    private static class RequestImpl implements Request {
80        private static interface Allocation {
81            public void allocate() throws InterruptedException,
82                    ResourceAcquisitionFailedException;
83
84            public void abort();
85        }
86
87        private final CaptureRequestBuilderProxy mCaptureRequestBuilder;
88        private final List<Allocation> mAllocations;
89        private final ResponseListener mResponseListener;
90
91        public RequestImpl(CaptureRequestBuilderProxy builder, List<Allocation> allocations,
92                ResponseListener responseListener) {
93            mCaptureRequestBuilder = builder;
94            mAllocations = allocations;
95            mResponseListener = responseListener;
96        }
97
98        @Override
99        public CaptureRequestBuilderProxy allocateCaptureRequest() throws InterruptedException,
100                ResourceAcquisitionFailedException {
101            for (Allocation allocation : mAllocations) {
102                allocation.allocate();
103            }
104            return mCaptureRequestBuilder;
105        }
106
107        @Override
108        public ResponseListener getResponseListener() {
109            return mResponseListener;
110        }
111
112        @Override
113        public void abort() {
114            for (Allocation allocation : mAllocations) {
115                allocation.abort();
116            }
117        }
118    }
119
120    private final CaptureRequestBuilderProxy mBuilder;
121
122    private final List<RequestImpl.Allocation> mAllocations;
123
124    /**
125     * The set of ResponseListeners to dispatch to for all updates.
126     */
127    private final Set<ResponseListener> mResponseListeners;
128
129    /**
130     * @param builder The capture request builder to use.
131     */
132    public RequestBuilder(CaptureRequestBuilderProxy builder) {
133        mBuilder = builder;
134        mAllocations = new ArrayList<>();
135        mResponseListeners = new HashSet<>();
136    }
137
138    /**
139     * Adds the given response listener. Duplicate listeners are only added
140     * once.
141     *
142     * @See {@link ResponseListeners}
143     *
144     * @param listener the listener to add.
145     */
146    public void addResponseListener(ResponseListener listener) {
147        mResponseListeners.add(listener);
148    }
149
150    /**
151     * Sets the given key-value pair.
152     *
153     * @see {@link CaptureRequest.Builder#set}.
154     */
155    public <T> void setParam(CaptureRequest.Key<T> key, T value) {
156        mBuilder.set(key, value);
157    }
158
159    /**
160     * Adds the given {@link CaptureStream} as an output target. Note that the
161     * {@link Surface} associated with the given {@link CaptureStream} should be
162     * one of of the surfaces added to the
163     * {@link android.hardware.camera2.CameraCaptureSession} which the built
164     * {@link Request} will be sent to.
165     *
166     * @param captureStream
167     */
168    public void addStream(CaptureStream captureStream) {
169        ConcurrentBufferQueue<Long> timestamps = new ConcurrentBufferQueue<>();
170
171        mAllocations.add(new UnregisteredStreamProvider(captureStream,
172                timestamps, mBuilder));
173
174        mResponseListeners.add(ResponseListeners.forTimestamps(timestamps));
175    }
176
177    /**
178     * Builds a new {@link Request} based on the current state of the builder.
179     * The builder should not be reused after this is called.
180     *
181     * @return A new {@link Request} based on the current state of the builder.
182     */
183    public Request build() {
184        return new RequestImpl(mBuilder, mAllocations,
185                new ResponseListenerBroadcaster(new ArrayList<>(mResponseListeners)));
186    }
187
188}
189