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 */
16
17package com.android.camera.one.v2.photo.zsl;
18
19import android.hardware.camera2.CaptureResult;
20import android.hardware.camera2.TotalCaptureResult;
21
22import com.android.camera.async.Updatable;
23import com.android.camera.debug.Log;
24import com.android.camera.debug.Logger;
25import com.android.camera.one.v2.camera2proxy.CaptureRequestProxy;
26import com.android.camera.one.v2.camera2proxy.CaptureResultProxy;
27import com.android.camera.one.v2.camera2proxy.TotalCaptureResultProxy;
28import com.google.common.base.Objects;
29import com.google.common.base.Preconditions;
30import com.google.common.base.Predicate;
31
32import java.util.List;
33import java.util.concurrent.atomic.AtomicBoolean;
34
35import javax.annotation.Nonnull;
36import javax.annotation.Nullable;
37import javax.annotation.ParametersAreNonnullByDefault;
38
39/**
40 * Like {@link AcceptableZslImageFilter}, but determines whether or not to
41 * filter ZSL images by AE convergence based on the most-recent converged AE
42 * result. This enables an optimization in which pictures can be taken with zero
43 * shutter lag when flash is AUTO even if AE is searching.
44 * <p>
45 * For example, when flash is AUTO, we can only capture images via the ZSL
46 * buffer if we know that flash is not required. We know that flash is not
47 * required when CONTROL_AE_STATE == CONTROL_AE_STATE_CONVERGED. However, when
48 * the AE system is scanning, we do not know if flash is required. So, instead
49 * of waiting until it converges, we can cache the most recent result and allow
50 * capturing an image instantly.
51 * <p>
52 * Note that this optimization presents a trade-off between speed and
53 * quality/accuracy. For example, if a user moves the camera from a bright scene
54 * to a dark scene and tries to immediately take a picture before AE has
55 * converged, then the flash may not fire. However, it enables faster capture if
56 * the user moves the camera from a bright scene to another bright scene with a
57 * different level of illumination, which would not otherwise require flash.
58 */
59@ParametersAreNonnullByDefault
60public final class AutoFlashZslImageFilter implements Predicate<TotalCaptureResultProxy>,
61        Updatable<CaptureResultProxy> {
62    private final Logger mLog;
63    private final AcceptableZslImageFilter mDefaultFilter;
64
65    private AtomicBoolean mRequireAEConvergence;
66    private long mLastFrameNumber;
67
68    private AutoFlashZslImageFilter(Logger.Factory logFactory,
69            AcceptableZslImageFilter defaultFilter) {
70        mDefaultFilter = defaultFilter;
71        mLog = logFactory.create(new Log.Tag("AutoFlashZslImgFltr"));
72        mRequireAEConvergence = new AtomicBoolean(true);
73        mLastFrameNumber = -1;
74    }
75
76    /**
77     * Wraps a TotalCaptureResult, converting
78     * CaptureResult.CONTROL_AE_STATE_SEARCHING into
79     * CaptureResult.CONTROL_AE_STATE_CONVERGED.
80     */
81    private static class AEConvergedTotalCaptureResult implements TotalCaptureResultProxy {
82        private final TotalCaptureResultProxy mDelegate;
83
84        public AEConvergedTotalCaptureResult(TotalCaptureResultProxy delegate) {
85            mDelegate = delegate;
86        }
87
88        @Nullable
89        @Override
90        public <T> T get(CaptureResult.Key<T> key) {
91            if (key == TotalCaptureResult.CONTROL_AE_STATE) {
92                Integer aeState = (Integer) mDelegate.get(key);
93                if (Objects.equal(aeState, CaptureResult.CONTROL_AE_STATE_SEARCHING)) {
94                    return (T) ((Integer) CaptureResult.CONTROL_AE_STATE_CONVERGED);
95                }
96            }
97            return mDelegate.get(key);
98        }
99
100        @Nonnull
101        @Override
102        public List<CaptureResult.Key<?>> getKeys() {
103            return mDelegate.getKeys();
104        }
105
106        @Nonnull
107        @Override
108        public CaptureRequestProxy getRequest() {
109            return mDelegate.getRequest();
110        }
111
112        @Override
113        public long getFrameNumber() {
114            return mDelegate.getFrameNumber();
115        }
116
117        @Override
118        public int getSequenceId() {
119            return mDelegate.getSequenceId();
120        }
121
122        @Nonnull
123        @Override
124        public List<CaptureResultProxy> getPartialResults() {
125            return mDelegate.getPartialResults();
126        }
127    }
128
129    public static AutoFlashZslImageFilter create(Logger.Factory logFactory,
130            boolean requireAFConvergence) {
131        return new AutoFlashZslImageFilter(
132                logFactory,
133                new AcceptableZslImageFilter(requireAFConvergence, /* aeConvergence */true));
134    }
135
136    @Override
137    public boolean apply(TotalCaptureResultProxy totalCaptureResultProxy) {
138        if (!mRequireAEConvergence.get()) {
139            // If AE was previously converged, wrap the metadata to appear as if AE is currently
140            // converged.
141            totalCaptureResultProxy = new AEConvergedTotalCaptureResult(totalCaptureResultProxy);
142        }
143        return mDefaultFilter.apply(totalCaptureResultProxy);
144    }
145
146    @Override
147    public void update(@Nonnull CaptureResultProxy captureResult) {
148        if (captureResult.getFrameNumber() > mLastFrameNumber) {
149            Integer aeState = captureResult.get(CaptureResult.CONTROL_AE_STATE);
150            if (aeState != null) {
151                if (aeState == CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED) {
152                    boolean previousValue = mRequireAEConvergence.getAndSet(true);
153                    if (previousValue != true) {
154                        // Only log changes
155                        mLog.i("Flash required");
156                    }
157                } else if (aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
158                    boolean previousValue = mRequireAEConvergence.getAndSet(false);
159                    if (previousValue != false) {
160                        // Only log changes
161                        mLog.i("Flash not required");
162                    }
163                }
164                mLastFrameNumber = captureResult.getFrameNumber();
165            }
166        }
167    }
168}
169