Home | History | Annotate | Download | only in utils
      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.ex.camera2.utils;
     18 
     19 import android.hardware.camera2.CameraAccessException;
     20 import android.hardware.camera2.CameraDevice;
     21 import android.hardware.camera2.CaptureRequest;
     22 import android.hardware.camera2.CaptureRequest.Builder;
     23 import android.hardware.camera2.CaptureRequest.Key;
     24 import android.view.Surface;
     25 
     26 import java.util.HashMap;
     27 import java.util.Map;
     28 import java.util.Objects;
     29 
     30 /**
     31  * A set of settings to be used when filing a {@link CaptureRequest}.
     32  */
     33 public class Camera2RequestSettingsSet {
     34     private final Map<Key<?>, Object> mDictionary;
     35     private long mRevision;
     36 
     37     /**
     38      * Create a new instance with no settings defined.
     39      *
     40      * <p>Creating a request from this object without first specifying any
     41      * properties on it is equivalent to just creating a request directly
     42      * from the template of choice. Its revision identifier is initially
     43      * {@code 0}, and will remain thus until its first modification.</p>
     44      */
     45     public Camera2RequestSettingsSet() {
     46         mDictionary = new HashMap<>();
     47         mRevision = 0;
     48     }
     49 
     50     /**
     51      * Perform a deep copy of the defined settings and revision number.
     52      *
     53      * @param other The reference instance.
     54      *
     55      * @throws NullPointerException If {@code other} is {@code null}.
     56      */
     57     public Camera2RequestSettingsSet(Camera2RequestSettingsSet other) {
     58         if (other == null) {
     59             throw new NullPointerException("Tried to copy null Camera2RequestSettingsSet");
     60         }
     61 
     62         mDictionary = new HashMap<>(other.mDictionary);
     63         mRevision = other.mRevision;
     64     }
     65 
     66     /**
     67      * Specify a setting, potentially overriding the template's default choice.
     68      *
     69      * <p>Providing a {@code null} {@code value} will indicate a forced use of
     70      * the template's selection for that {@code key}; the difference here is
     71      * that this information will be propagated with unions as documented in
     72      * {@link #union}. This method increments the revision identifier if the new
     73      * choice is different than the existing selection.</p>
     74      *
     75      * @param key Which setting to alter.
     76      * @param value The new selection for that setting, or {@code null} to force
     77      *              the use of the template's default selection for this field.
     78      * @return Whether the settings were updated, which only occurs if the
     79      *         {@code value} is different from any already stored.
     80      *
     81      * @throws NullPointerException If {@code key} is {@code null}.
     82      */
     83     public <T> boolean set(Key<T> key, T value) {
     84         if (key == null) {
     85             throw new NullPointerException("Received a null key");
     86         }
     87 
     88         Object currentValue = get(key);
     89         // Only save the value if it's different from the one we already have
     90         if (!mDictionary.containsKey(key) || !Objects.equals(value, currentValue)) {
     91             mDictionary.put(key, value);
     92             ++mRevision;
     93             return true;
     94         }
     95         return false;
     96     }
     97 
     98     /**
     99      * Unsets a setting, preventing it from being propagated with unions or from
    100      * overriding the default when creating a capture request.
    101      *
    102      * <p>This method increments the revision identifier if a selection had
    103      * previously been made for that parameter.</p>
    104      *
    105      * @param key Which setting to reset.
    106      * @return Whether the settings were updated, which only occurs if the
    107      *         specified setting already had a value or was forced to default.
    108      *
    109      * @throws NullPointerException If {@code key} is {@code null}.
    110      */
    111     public boolean unset(Key<?> key) {
    112         if (key == null) {
    113             throw new NullPointerException("Received a null key");
    114         }
    115 
    116         if (mDictionary.containsKey(key)) {
    117             mDictionary.remove(key);
    118             ++mRevision;
    119             return true;
    120         }
    121         return false;
    122     }
    123 
    124     /**
    125      * Interrogate the current specialization of a setting.
    126      *
    127      * @param key Which setting to check.
    128      * @return The current selection for that setting, or {@code null} if the
    129      *         setting is unset or forced to the template-defined default.
    130      *
    131      * @throws NullPointerException If {@code key} is {@code null}.
    132      */
    133     @SuppressWarnings("unchecked")
    134     public <T> T get(Key<T> key) {
    135         if (key == null) {
    136             throw new NullPointerException("Received a null key");
    137         }
    138         return (T) mDictionary.get(key);
    139     }
    140 
    141     /**
    142      * Query this instance for whether it prefers a particular choice for the
    143      * given request parameter.
    144      *
    145      * <p>This method can be used to detect whether a particular field is forced
    146      * to its default value or simply unset. While {@link #get} will return
    147      * {@code null} in both these cases, this method will return {@code true}
    148      * and {@code false}, respectively.</p>
    149      *
    150      * @param key Which setting to look for.
    151      * @return Whether that setting has a value that will propagate with unions.
    152      *
    153      * @throws NullPointerException If {@code key} is {@code null}.
    154      */
    155     public boolean contains(Key<?> key) {
    156         if (key == null) {
    157             throw new NullPointerException("Received a null key");
    158         }
    159         return mDictionary.containsKey(key);
    160     }
    161 
    162     /**
    163      * Check whether the value of the specified setting matches the given one.
    164      *
    165      * <p>This method uses the {@code T} type's {@code equals} method, but is
    166      * {@code null}-tolerant.</p>
    167      *
    168      * @param key Which of this class's settings to check.
    169      * @param value Value to test for equality against.
    170      * @return Whether they are the same.
    171      */
    172     public <T> boolean matches(Key<T> key, T value) {
    173         return Objects.equals(get(key), value);
    174     }
    175 
    176     /**
    177      * Get this set of settings's revision identifier, which can be compared
    178      * against cached past values to determine whether it has been modified.
    179      *
    180      * <p>Distinct revisions across the same object do not necessarily indicate
    181      * that the object's key/value pairs have changed at all, but the same
    182      * revision on the same object does imply that they've stayed the same.</p>
    183      *
    184      * @return The number of modifications made since the beginning of this
    185      *         object's heritage.
    186      */
    187     public long getRevision() {
    188         return mRevision;
    189     }
    190 
    191     /**
    192      * Add all settings choices defined by {@code moreSettings} to this object.
    193      *
    194      * <p>For any settings defined in both, the choice stored in the argument
    195      * to this method take precedence. Unset settings are not propagated, but
    196      * those forced to default as described in {@link set} are also forced to
    197      * default in {@code this} set. Invoking this method increments {@code this}
    198      * object's revision counter, but leaves the argument's unchanged.</p>
    199      *
    200      * @param moreSettings The source of the additional settings ({@code null}
    201      *                     is allowed here).
    202      * @return Whether these settings were updated, which can only fail if the
    203      *         target itself is also given as the argument.
    204      */
    205     public boolean union(Camera2RequestSettingsSet moreSettings) {
    206         if (moreSettings == null || moreSettings == this) {
    207             return false;
    208         }
    209 
    210         mDictionary.putAll(moreSettings.mDictionary);
    211         ++mRevision;
    212         return true;
    213     }
    214 
    215     /**
    216      * Create a {@link CaptureRequest} specialized for the specified
    217      * {@link CameraDevice} and targeting the given {@link Surface}s.
    218      *
    219      * @param camera The camera from which to capture.
    220      * @param template A {@link CaptureRequest} template defined in
    221      *                 {@link CameraDevice}.
    222      * @param targets The location(s) to draw the resulting image onto.
    223      * @return The request, ready to be passed to the camera framework.
    224      *
    225      * @throws CameraAccessException Upon an underlying framework API failure.
    226      * @throws NullPointerException If any argument is {@code null}.
    227      */
    228     public CaptureRequest createRequest(CameraDevice camera, int template, Surface... targets)
    229             throws CameraAccessException {
    230         if (camera == null) {
    231             throw new NullPointerException("Tried to create request using null CameraDevice");
    232         }
    233 
    234         Builder reqBuilder = camera.createCaptureRequest(template);
    235         for (Key<?> key : mDictionary.keySet()) {
    236             setRequestFieldIfNonNull(reqBuilder, key);
    237         }
    238         for (Surface target : targets) {
    239             if (target == null) {
    240                 throw new NullPointerException("Tried to add null Surface as request target");
    241             }
    242             reqBuilder.addTarget(target);
    243         }
    244         return reqBuilder.build();
    245     }
    246 
    247     private <T> void setRequestFieldIfNonNull(Builder requestBuilder, Key<T> key) {
    248         T value = get(key);
    249         if (value != null) {
    250             requestBuilder.set(key, value);
    251         }
    252     }
    253 }
    254