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 */
16package android.hardware.camera2.impl;
17
18import android.hardware.camera2.CameraAccessException;
19import android.hardware.camera2.CameraCaptureSession;
20import android.hardware.camera2.CameraCharacteristics;
21import android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession;
22import android.hardware.camera2.CameraDevice;
23import android.hardware.camera2.CaptureRequest;
24import android.hardware.camera2.params.OutputConfiguration;
25import android.hardware.camera2.params.StreamConfigurationMap;
26import android.hardware.camera2.utils.SurfaceUtils;
27import android.os.Handler;
28import android.util.Range;
29import android.view.Surface;
30
31import java.util.ArrayList;
32import java.util.Collection;
33import java.util.Collections;
34import java.util.Iterator;
35import java.util.List;
36
37import static com.android.internal.util.Preconditions.*;
38
39/**
40 * Standard implementation of CameraConstrainedHighSpeedCaptureSession.
41 *
42 * <p>
43 * Mostly just forwards calls to an instance of CameraCaptureSessionImpl,
44 * but implements the few necessary behavior changes and additional methods required
45 * for the constrained high speed speed mode.
46 * </p>
47 */
48
49public class CameraConstrainedHighSpeedCaptureSessionImpl
50        extends CameraConstrainedHighSpeedCaptureSession implements CameraCaptureSessionCore {
51    private final CameraCharacteristics mCharacteristics;
52    private final CameraCaptureSessionImpl mSessionImpl;
53
54    /**
55     * Create a new CameraCaptureSession.
56     *
57     * <p>The camera device must already be in the {@code IDLE} state when this is invoked.
58     * There must be no pending actions
59     * (e.g. no pending captures, no repeating requests, no flush).</p>
60     */
61    CameraConstrainedHighSpeedCaptureSessionImpl(int id,
62            CameraCaptureSession.StateCallback callback, Handler stateHandler,
63            android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
64            Handler deviceStateHandler, boolean configureSuccess,
65            CameraCharacteristics characteristics) {
66        mCharacteristics = characteristics;
67        CameraCaptureSession.StateCallback wrapperCallback = new WrapperCallback(callback);
68        mSessionImpl = new CameraCaptureSessionImpl(id, /*input*/null, wrapperCallback,
69                stateHandler, deviceImpl, deviceStateHandler, configureSuccess);
70    }
71
72    @Override
73    public List<CaptureRequest> createHighSpeedRequestList(CaptureRequest request)
74            throws CameraAccessException {
75        if (request == null) {
76            throw new IllegalArgumentException("Input capture request must not be null");
77        }
78        Collection<Surface> outputSurfaces = request.getTargets();
79        Range<Integer> fpsRange = request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
80
81        StreamConfigurationMap config =
82                mCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
83        SurfaceUtils.checkConstrainedHighSpeedSurfaces(outputSurfaces, fpsRange, config);
84
85        // Request list size: to limit the preview to 30fps, need use maxFps/30; to maximize
86        // the preview frame rate, should use maxBatch size for that high speed stream
87        // configuration. We choose the former for now.
88        int requestListSize = fpsRange.getUpper() / 30;
89        List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
90
91        // Prepare the Request builders: need carry over the request controls.
92        // First, create a request builder that will only include preview or recording target.
93        CameraMetadataNative requestMetadata = new CameraMetadataNative(request.getNativeCopy());
94        // Note that after this step, the requestMetadata is mutated (swapped) and can not be used
95        // for next request builder creation.
96        CaptureRequest.Builder singleTargetRequestBuilder = new CaptureRequest.Builder(
97                requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
98
99        // Overwrite the capture intent to make sure a good value is set.
100        Iterator<Surface> iterator = outputSurfaces.iterator();
101        Surface firstSurface = iterator.next();
102        Surface secondSurface = null;
103        if (outputSurfaces.size() == 1 && SurfaceUtils.isSurfaceForHwVideoEncoder(firstSurface)) {
104            singleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT,
105                    CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW);
106        } else {
107            // Video only, or preview + video
108            singleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT,
109                    CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD);
110        }
111        singleTargetRequestBuilder.setPartOfCHSRequestList(/*partOfCHSList*/true);
112
113        // Second, Create a request builder that will include both preview and recording targets.
114        CaptureRequest.Builder doubleTargetRequestBuilder = null;
115        if (outputSurfaces.size() == 2) {
116            // Have to create a new copy, the original one was mutated after a new
117            // CaptureRequest.Builder creation.
118            requestMetadata = new CameraMetadataNative(request.getNativeCopy());
119            doubleTargetRequestBuilder = new CaptureRequest.Builder(
120                    requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
121            doubleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT,
122                    CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD);
123            doubleTargetRequestBuilder.addTarget(firstSurface);
124            secondSurface = iterator.next();
125            doubleTargetRequestBuilder.addTarget(secondSurface);
126            doubleTargetRequestBuilder.setPartOfCHSRequestList(/*partOfCHSList*/true);
127            // Make sure singleTargetRequestBuilder contains only recording surface for
128            // preview + recording case.
129            Surface recordingSurface = firstSurface;
130            if (!SurfaceUtils.isSurfaceForHwVideoEncoder(recordingSurface)) {
131                recordingSurface = secondSurface;
132            }
133            singleTargetRequestBuilder.addTarget(recordingSurface);
134        } else {
135            // Single output case: either recording or preview.
136            singleTargetRequestBuilder.addTarget(firstSurface);
137        }
138
139        // Generate the final request list.
140        for (int i = 0; i < requestListSize; i++) {
141            if (i == 0 && doubleTargetRequestBuilder != null) {
142                // First request should be recording + preview request
143                requestList.add(doubleTargetRequestBuilder.build());
144            } else {
145                requestList.add(singleTargetRequestBuilder.build());
146            }
147        }
148
149        return Collections.unmodifiableList(requestList);
150    }
151
152    private boolean isConstrainedHighSpeedRequestList(List<CaptureRequest> requestList) {
153        checkCollectionNotEmpty(requestList, "High speed request list");
154        for (CaptureRequest request : requestList) {
155            if (!request.isPartOfCRequestList()) {
156                return false;
157            }
158        }
159        return true;
160    }
161
162    @Override
163    public CameraDevice getDevice() {
164        return mSessionImpl.getDevice();
165    }
166
167    @Override
168    public void prepare(Surface surface) throws CameraAccessException {
169        mSessionImpl.prepare(surface);
170    }
171
172    @Override
173    public void prepare(int maxCount, Surface surface) throws CameraAccessException {
174        mSessionImpl.prepare(maxCount, surface);
175    }
176
177    @Override
178    public void tearDown(Surface surface) throws CameraAccessException {
179        mSessionImpl.tearDown(surface);
180    }
181
182    @Override
183    public int capture(CaptureRequest request, CaptureCallback listener, Handler handler)
184            throws CameraAccessException {
185        throw new UnsupportedOperationException("Constrained high speed session doesn't support"
186                + " this method");
187    }
188
189    @Override
190    public int captureBurst(List<CaptureRequest> requests, CaptureCallback listener,
191            Handler handler) throws CameraAccessException {
192        if (!isConstrainedHighSpeedRequestList(requests)) {
193            throw new IllegalArgumentException(
194                "Only request lists created by createHighSpeedRequestList() can be submitted to " +
195                "a constrained high speed capture session");
196        }
197        return mSessionImpl.captureBurst(requests, listener, handler);
198    }
199
200    @Override
201    public int setRepeatingRequest(CaptureRequest request, CaptureCallback listener,
202            Handler handler) throws CameraAccessException {
203        throw new UnsupportedOperationException("Constrained high speed session doesn't support"
204                + " this method");
205    }
206
207    @Override
208    public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback listener,
209            Handler handler) throws CameraAccessException {
210        if (!isConstrainedHighSpeedRequestList(requests)) {
211            throw new IllegalArgumentException(
212                "Only request lists created by createHighSpeedRequestList() can be submitted to " +
213                "a constrained high speed capture session");
214        }
215        return mSessionImpl.setRepeatingBurst(requests, listener, handler);
216    }
217
218    @Override
219    public void stopRepeating() throws CameraAccessException {
220        mSessionImpl.stopRepeating();
221    }
222
223    @Override
224    public void abortCaptures() throws CameraAccessException {
225        mSessionImpl.abortCaptures();
226    }
227
228    @Override
229    public Surface getInputSurface() {
230        return null;
231    }
232
233    @Override
234    public void close() {
235        mSessionImpl.close();
236    }
237
238    @Override
239    public boolean isReprocessable() {
240        return false;
241    }
242
243    // Implementation of CameraCaptureSessionCore methods
244
245    @Override
246    public void replaceSessionClose() {
247        mSessionImpl.replaceSessionClose();
248    }
249
250    @Override
251    public CameraDeviceImpl.StateCallbackKK getDeviceStateCallback() {
252        return mSessionImpl.getDeviceStateCallback();
253    }
254
255    @Override
256    public boolean isAborting() {
257        return mSessionImpl.isAborting();
258    }
259
260    @Override
261    public void finalizeOutputConfigurations(List<OutputConfiguration> deferredOutputConfigs)
262            throws CameraAccessException {
263        mSessionImpl.finalizeOutputConfigurations(deferredOutputConfigs);
264    }
265
266    private class WrapperCallback extends StateCallback {
267        private final StateCallback mCallback;
268
269        public WrapperCallback(StateCallback callback) {
270            mCallback = callback;
271        }
272
273        @Override
274        public void onConfigured(CameraCaptureSession session) {
275            mCallback.onConfigured(CameraConstrainedHighSpeedCaptureSessionImpl.this);
276        }
277
278        @Override
279        public void onConfigureFailed(CameraCaptureSession session) {
280            mCallback.onConfigureFailed(CameraConstrainedHighSpeedCaptureSessionImpl.this);
281        }
282
283        @Override
284        public void onReady(CameraCaptureSession session) {
285            mCallback.onReady(CameraConstrainedHighSpeedCaptureSessionImpl.this);
286        }
287
288        @Override
289        public void onActive(CameraCaptureSession session) {
290            mCallback.onActive(CameraConstrainedHighSpeedCaptureSessionImpl.this);
291        }
292
293        @Override
294        public void onCaptureQueueEmpty(CameraCaptureSession session) {
295            mCallback.onCaptureQueueEmpty(CameraConstrainedHighSpeedCaptureSessionImpl.this);
296        }
297
298        @Override
299        public void onClosed(CameraCaptureSession session) {
300            mCallback.onClosed(CameraConstrainedHighSpeedCaptureSessionImpl.this);
301        }
302
303        @Override
304        public void onSurfacePrepared(CameraCaptureSession session, Surface surface) {
305            mCallback.onSurfacePrepared(CameraConstrainedHighSpeedCaptureSessionImpl.this,
306                    surface);
307        }
308    }
309}
310