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