diff options
author | Sol Boucher <solb@google.com> | 2014-07-24 10:28:14 -0700 |
---|---|---|
committer | Sol Boucher <solb@google.com> | 2014-07-30 14:13:52 -0700 |
commit | 8ba391e3f88936557ad6d44bbef32cb08f4ca310 (patch) | |
tree | 2e2af725f365bef500146fbd1af93f814a1802cc /camera2/utils/src/com/android | |
parent | 90aa7e817d1969b4fbbfc36dbeca6c500b6aaa0b (diff) | |
download | android_frameworks_ex-8ba391e3f88936557ad6d44bbef32cb08f4ca310.tar.gz android_frameworks_ex-8ba391e3f88936557ad6d44bbef32cb08f4ca310.tar.bz2 android_frameworks_ex-8ba391e3f88936557ad6d44bbef32cb08f4ca310.zip |
Create new com.android.ex.camera2.utils package
It contains:
- Camera2RequestSettingsSet class for storing/unioning CaptureRequest.Builders
- A Camera2CaptureListenerSplitter class for connecting multiple such listeners
- A Camera2CpatureListenerForwarder for relaying callbacks to another Handler
Change-Id: I47381e4a45b4232b86e5498a1cc39cc600b5c5c5
Diffstat (limited to 'camera2/utils/src/com/android')
3 files changed, 432 insertions, 0 deletions
diff --git a/camera2/utils/src/com/android/ex/camera2/utils/Camera2CaptureListenerForwarder.java b/camera2/utils/src/com/android/ex/camera2/utils/Camera2CaptureListenerForwarder.java new file mode 100644 index 0000000..35b1c6d --- /dev/null +++ b/camera2/utils/src/com/android/ex/camera2/utils/Camera2CaptureListenerForwarder.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ex.camera2.utils; + +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraCaptureSession.CaptureListener; +import android.hardware.camera2.CaptureFailure; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; +import android.os.Handler; + +/** + * Proxy that forwards all updates to another {@link CaptureListener}, invoking + * its callbacks on a separate {@link Handler}. + */ +public class Camera2CaptureListenerForwarder extends CaptureListener { + private CaptureListener mListener; + private Handler mHandler; + + public Camera2CaptureListenerForwarder(CaptureListener listener, Handler handler) { + mListener = listener; + mHandler = handler; + } + + @Override + public void onCaptureCompleted(final CameraCaptureSession session, final CaptureRequest request, + final TotalCaptureResult result) { + mHandler.post(new Runnable() { + @Override + public void run() { + mListener.onCaptureCompleted(session, request, result); + }}); + } + + @Override + public void onCaptureFailed(final CameraCaptureSession session, final CaptureRequest request, + final CaptureFailure failure) { + mHandler.post(new Runnable() { + @Override + public void run() { + mListener.onCaptureFailed(session, request, failure); + }}); + } + + @Override + public void onCaptureProgressed(final CameraCaptureSession session, + final CaptureRequest request, + final CaptureResult partialResult) { + mHandler.post(new Runnable() { + @Override + public void run() { + mListener.onCaptureProgressed(session, request, partialResult); + }}); + } + + @Override + public void onCaptureSequenceAborted(final CameraCaptureSession session, final int sequenceId) { + mHandler.post(new Runnable() { + @Override + public void run() { + mListener.onCaptureSequenceAborted(session, sequenceId); + }}); + } + + @Override + public void onCaptureSequenceCompleted(final CameraCaptureSession session, final int sequenceId, + final long frameNumber) { + mHandler.post(new Runnable() { + @Override + public void run() { + mListener.onCaptureSequenceCompleted(session, sequenceId, frameNumber); + }}); + } + + @Override + public void onCaptureStarted(final CameraCaptureSession session, final CaptureRequest request, + final long timestamp) { + mHandler.post(new Runnable() { + @Override + public void run() { + mListener.onCaptureStarted(session, request, timestamp); + }}); + } +} diff --git a/camera2/utils/src/com/android/ex/camera2/utils/Camera2CaptureListenerSplitter.java b/camera2/utils/src/com/android/ex/camera2/utils/Camera2CaptureListenerSplitter.java new file mode 100644 index 0000000..a13dc04 --- /dev/null +++ b/camera2/utils/src/com/android/ex/camera2/utils/Camera2CaptureListenerSplitter.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ex.camera2.utils; + +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraCaptureSession.CaptureListener; +import android.hardware.camera2.CaptureFailure; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +/** + * Junction that allows notifying multiple {@link CaptureListener}s whenever + * the {@link CameraCaptureSession} posts a capture-related update. + */ +public class Camera2CaptureListenerSplitter extends CaptureListener { + private final List<CaptureListener> mRecipients = new LinkedList<>(); + + /** + * @param recipients The listeners to notify. Any {@code null} passed here + * will be completely ignored. + */ + public Camera2CaptureListenerSplitter(CaptureListener... recipients) { + for (CaptureListener listener : recipients) { + if (listener != null) { + mRecipients.add(listener); + } + } + } + + @Override + public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, + TotalCaptureResult result) { + for (CaptureListener target : mRecipients) { + target.onCaptureCompleted(session, request, result); + } + } + + @Override + public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request, + CaptureFailure failure) { + for (CaptureListener target : mRecipients) { + target.onCaptureFailed(session, request, failure); + } + } + + @Override + public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, + CaptureResult partialResult) { + for (CaptureListener target : mRecipients) { + target.onCaptureProgressed(session, request, partialResult); + } + } + + @Override + public void onCaptureSequenceAborted(CameraCaptureSession session, int sequenceId) { + for (CaptureListener target : mRecipients) { + target.onCaptureSequenceAborted(session, sequenceId); + } + } + + @Override + public void onCaptureSequenceCompleted(CameraCaptureSession session, int sequenceId, + long frameNumber) { + for (CaptureListener target : mRecipients) { + target.onCaptureSequenceCompleted(session, sequenceId, frameNumber); + } + } + + @Override + public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request, + long timestamp) { + for (CaptureListener target : mRecipients) { + target.onCaptureStarted(session, request, timestamp); + } + } +} diff --git a/camera2/utils/src/com/android/ex/camera2/utils/Camera2RequestSettingsSet.java b/camera2/utils/src/com/android/ex/camera2/utils/Camera2RequestSettingsSet.java new file mode 100644 index 0000000..fac8f1a --- /dev/null +++ b/camera2/utils/src/com/android/ex/camera2/utils/Camera2RequestSettingsSet.java @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ex.camera2.utils; + +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureRequest.Builder; +import android.hardware.camera2.CaptureRequest.Key; +import android.view.Surface; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * A set of settings to be used when filing a {@link CaptureRequest}. + */ +public class Camera2RequestSettingsSet { + private final Map<Key<?>, Object> mDictionary; + private long mRevision; + + /** + * Create a new instance with no settings defined. + * + * <p>Creating a request from this object without first specifying any + * properties on it is equivalent to just creating a request directly + * from the template of choice. Its revision identifier is initially + * {@code 0}, and will remain thus until its first modification.</p> + */ + public Camera2RequestSettingsSet() { + mDictionary = new HashMap<>(); + mRevision = 0; + } + + /** + * Perform a deep copy of the defined settings and revision number. + * + * @param other The reference instance. + * + * @throws NullPointerException If {@code other} is {@code null}. + */ + public Camera2RequestSettingsSet(Camera2RequestSettingsSet other) { + if (other == null) { + throw new NullPointerException("Tried to copy null Camera2RequestSettingsSet"); + } + + mDictionary = new HashMap<>(other.mDictionary); + mRevision = other.mRevision; + } + + /** + * Specify a setting, potentially overriding the template's default choice. + * + * <p>Providing a {@code null} {@code value} will indicate a forced use of + * the template's selection for that {@code key}; the difference here is + * that this information will be propagated with unions as documented in + * {@link #union}. This method increments the revision identifier if the new + * choice is different than the existing selection.</p> + * + * @param key Which setting to alter. + * @param value The new selection for that setting, or {@code null} to force + * the use of the template's default selection for this field. + * @return Whether the settings were updated, which only occurs if the + * {@code value} is different from any already stored. + * + * @throws NullPointerException If {@code key} is {@code null}. + */ + public <T> boolean set(Key<T> key, T value) { + if (key == null) { + throw new NullPointerException("Received a null key"); + } + + Object currentValue = get(key); + // Only save the value if it's different from the one we already have + if (!mDictionary.containsKey(key) || !Objects.equals(value, currentValue)) { + mDictionary.put(key, value); + ++mRevision; + return true; + } + return false; + } + + /** + * Unsets a setting, preventing it from being propagated with unions or from + * overriding the default when creating a capture request. + * + * <p>This method increments the revision identifier if a selection had + * previously been made for that parameter.</p> + * + * @param key Which setting to reset. + * @return Whether the settings were updated, which only occurs if the + * specified setting already had a value or was forced to default. + * + * @throws NullPointerException If {@code key} is {@code null}. + */ + public boolean unset(Key<?> key) { + if (key == null) { + throw new NullPointerException("Received a null key"); + } + + if (mDictionary.containsKey(key)) { + mDictionary.remove(key); + ++mRevision; + return true; + } + return false; + } + + /** + * Interrogate the current specialization of a setting. + * + * @param key Which setting to check. + * @return The current selection for that setting, or {@code null} if the + * setting is unset or forced to the template-defined default. + * + * @throws NullPointerException If {@code key} is {@code null}. + */ + @SuppressWarnings("unchecked") + public <T> T get(Key<T> key) { + if (key == null) { + throw new NullPointerException("Received a null key"); + } + return (T) mDictionary.get(key); + } + + /** + * Query this instance for whether it prefers a particular choice for the + * given request parameter. + * + * <p>This method can be used to detect whether a particular field is forced + * to its default value or simply unset. While {@link #get} will return + * {@code null} in both these cases, this method will return {@code true} + * and {@code false}, respectively.</p> + + * @param key Which setting to look for. + * @return Whether that setting has a value that will propagate with unions. + * + * @throws NullPointerException If {@code key} is {@code null}. + */ + public boolean contains(Key<?> key) { + if (key == null) { + throw new NullPointerException("Received a null key"); + } + return mDictionary.containsKey(key); + } + + /** + * Get this set of settings's revision identifier, which can be compared + * against cached past values to determine whether it has been modified. + * <p>Distinct revisions across the same object do not necessarily indicate + * that the object's key/value pairs have changed at all, but the same + * revision on the same object does imply that they've stayed the same.</p> + * + * @return The number of modifications made since the beginning of this + * object's heritage. + */ + public long getRevision() { + return mRevision; + } + + /** + * Add all settings choices defined by {@code moreSettings} to this object. + * + * <p>For any settings defined in both, the choice stored in the argument + * to this method take precedence. Unset settings are not propagated, but + * those forced to default as described in {@link set} are also forced to + * default in {@code this} set. Invoking this method increments {@code this} + * object's revision counter, but leaves the argument's unchanged.</p> + * + * @param moreSettings The source of the additional settings ({@code null} + * is allowed here). + * @return Whether these settings were updated, which can only fail if the + * target itself is also given as the argument. + */ + public boolean union(Camera2RequestSettingsSet moreSettings) { + if (moreSettings == null || moreSettings == this) { + return false; + } + + mDictionary.putAll(moreSettings.mDictionary); + ++mRevision; + return true; + } + + /** + * Create a {@link CaptureRequest} specialized for the specified + * {@link CameraDevice} and targeting the given {@link Surface}s. + * + * @param camera The camera from which to capture. + * @param template A {@link CaptureRequest} template defined in + * {@link CameraDevice}. + * @param targets The location(s) to draw the resulting image onto. + * @return The request, ready to be passed to the camera framework. + * + * @throws CameraAccessException Upon an underlying framework API failure. + * @throws NullPointerException If any argument is {@code null}. + */ + public CaptureRequest createRequest(CameraDevice camera, int template, Surface... targets) + throws CameraAccessException { + if (camera == null) { + throw new NullPointerException("Tried to create request using null CameraDevice"); + } + + Builder reqBuilder = camera.createCaptureRequest(template); + for (Key<?> key : mDictionary.keySet()) { + setRequestFieldIfNonNull(reqBuilder, key); + } + for (Surface target : targets) { + if (target == null) { + throw new NullPointerException("Tried to add null Surface as request target"); + } + reqBuilder.addTarget(target); + } + return reqBuilder.build(); + } + + private <T> void setRequestFieldIfNonNull(Builder requestBuilder, Key<T> key) { + T value = get(key); + if (value != null) { + requestBuilder.set(key, value); + } + } +} |