Home | History | Annotate | Download | only in rscamera
      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 
     17 package com.android.example.rscamera;
     18 
     19 import android.content.ContentResolver;
     20 import android.graphics.ImageFormat;
     21 import android.hardware.camera2.CameraAccessException;
     22 import android.hardware.camera2.CameraCaptureSession;
     23 import android.hardware.camera2.CameraCharacteristics;
     24 import android.hardware.camera2.CameraDevice;
     25 import android.hardware.camera2.CameraManager;
     26 import android.hardware.camera2.CaptureRequest;
     27 import android.hardware.camera2.TotalCaptureResult;
     28 import android.hardware.camera2.params.StreamConfigurationMap;
     29 import android.media.Image;
     30 import android.media.ImageReader;
     31 import android.os.ConditionVariable;
     32 import android.os.Handler;
     33 import android.os.HandlerThread;
     34 import android.os.Looper;
     35 import android.util.Log;
     36 import android.util.Range;
     37 import android.util.Size;
     38 import android.view.Surface;
     39 import android.view.SurfaceHolder;
     40 import android.widget.Toast;
     41 
     42 import com.android.example.rscamera.rscamera.R;
     43 
     44 import java.io.File;
     45 import java.io.FileNotFoundException;
     46 import java.io.FileOutputStream;
     47 import java.io.IOException;
     48 import java.io.OutputStream;
     49 import java.nio.ByteBuffer;
     50 import java.util.ArrayList;
     51 import java.util.Arrays;
     52 import java.util.Collections;
     53 import java.util.Comparator;
     54 import java.util.List;
     55 
     56 /**
     57  * Simple interface for operating the camera, with major camera operations
     58  * all performed on a background handler thread.
     59  */
     60 public class CameraOps {
     61 
     62     private static final String TAG = "CameraOps";
     63     private static final long ONE_SECOND = 1000000000;
     64     public static final long CAMERA_CLOSE_TIMEOUT = 2000; // ms
     65 
     66     private final CameraManager mCameraManager;
     67     private CameraDevice mCameraDevice;
     68     private CameraCaptureSession mCameraSession;
     69     private List<Surface> mSurfaces;
     70 
     71     private final ConditionVariable mCloseWaiter = new ConditionVariable();
     72 
     73     private HandlerThread mCameraThread;
     74     private Handler mCameraHandler;
     75 
     76     private final ErrorDisplayer mErrorDisplayer;
     77 
     78     private final CameraReadyListener mReadyListener;
     79     private final Handler mReadyHandler;
     80 
     81     private int mISOmax;
     82     private int mISOmin;
     83     private long mExpMax;
     84     private long mExpMin;
     85     private float mFocusMin;
     86     private float mFocusDist = 0;
     87     private int mIso;
     88     boolean mAutoExposure = true;
     89     boolean mAutoFocus = true;
     90     private long mExposure = ONE_SECOND / 33;
     91 
     92     private Object mAutoExposureTag = new Object();
     93 
     94     private ImageReader mImageReader;
     95     private Handler mBackgroundHandler;
     96     private CameraCharacteristics mCameraInfo;
     97     private HandlerThread mBackgroundThread;
     98     CaptureRequest.Builder mHdrBuilder;
     99     private Surface mProcessingNormalSurface;
    100     CaptureRequest mPreviewRequest;
    101     private String mSaveFileName;
    102     private ContentResolver mContentResolver;
    103 
    104     public String resume() {
    105         String errorMessage = "Unknown error";
    106         boolean foundCamera = false;
    107         try {
    108             // Find first back-facing camera that has necessary capability
    109             String[] cameraIds = mCameraManager.getCameraIdList();
    110             for (String id : cameraIds) {
    111                 CameraCharacteristics info = mCameraManager.getCameraCharacteristics(id);
    112                 int facing = info.get(CameraCharacteristics.LENS_FACING);
    113 
    114                 int level = info.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
    115                 boolean hasFullLevel
    116                         = (level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL);
    117 
    118                 int[] capabilities = info.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
    119                 int syncLatency = info.get(CameraCharacteristics.SYNC_MAX_LATENCY);
    120                 boolean hasManualControl = hasCapability(capabilities,
    121                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR);
    122                 boolean hasEnoughCapability = hasManualControl &&
    123                         syncLatency == CameraCharacteristics.SYNC_MAX_LATENCY_PER_FRAME_CONTROL;
    124                 Range<Integer> irange;
    125                 Range<Long> lrange;
    126 
    127                 irange = info.get(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE);
    128                 mISOmax = irange.getUpper();
    129                 mISOmin = irange.getLower();
    130                 lrange = info.get(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE);
    131                 mExpMax = lrange.getUpper();
    132                 mExpMin = lrange.getLower();
    133                 mFocusMin = info.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE);
    134 
    135                 mFocusDist = mFocusMin;
    136                 StreamConfigurationMap map = info.get(
    137                         CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    138                 Size[] sizes = map.getOutputSizes(ImageFormat.JPEG);
    139                 Size largest = Collections.max(Arrays.asList(sizes), new Comparator<Size>() {
    140                     @Override
    141                     public int compare(Size lhs, Size rhs) {
    142                         int leftArea = lhs.getHeight() * lhs.getWidth();
    143                         int rightArea = lhs.getHeight() * lhs.getWidth();
    144                         return Integer.compare(leftArea, rightArea);
    145                     }
    146                 });
    147                 mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(),
    148                         ImageFormat.JPEG, /*maxImages*/2);
    149                 mImageReader.setOnImageAvailableListener(
    150                         mOnImageAvailableListener, mBackgroundHandler);
    151 
    152                 if (facing == CameraCharacteristics.LENS_FACING_BACK &&
    153                         (hasFullLevel || hasEnoughCapability)) {
    154                     // Found suitable camera - get info, open, and set up outputs
    155                     mCameraInfo = info;
    156                     openCamera(id);
    157                     foundCamera = true;
    158                     break;
    159                 }
    160             }
    161             if (!foundCamera) {
    162                 errorMessage = "no back camera";
    163             }
    164         } catch (CameraAccessException e) {
    165             errorMessage = e.getMessage();
    166         }
    167         // startBackgroundThread
    168         mBackgroundThread = new HandlerThread("CameraBackground");
    169         mBackgroundThread.start();
    170         mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
    171         return (foundCamera) ? null : errorMessage;
    172     }
    173 
    174 
    175     private boolean hasCapability(int[] capabilities, int capability) {
    176         for (int c : capabilities) {
    177             if (c == capability) return true;
    178         }
    179         return false;
    180     }
    181 
    182     private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
    183             = new ImageReader.OnImageAvailableListener() {
    184 
    185         @Override
    186         public void onImageAvailable(ImageReader reader) {
    187             mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mSaveFileName, mContentResolver));
    188         }
    189 
    190     };
    191 
    192     /**
    193      * Saves a JPEG {@link android.media.Image} into the specified {@link java.io.File}.
    194      */
    195     private static class ImageSaver implements Runnable {
    196         private final Image mImage;
    197         private final String mName;
    198         ContentResolver mContentResolver;
    199 
    200         public ImageSaver(Image image, String fileName, ContentResolver contentResolver) {
    201             mImage = image;
    202             mName = fileName;
    203             mContentResolver = contentResolver;
    204         }
    205 
    206         @Override
    207         public void run() {
    208             Log.v(TAG, "SAVING...");
    209             MediaStoreSaver.insertImage(mContentResolver, new MediaStoreSaver.StreamWriter() {
    210                 @Override
    211                 public void write(OutputStream imageOut) throws IOException {
    212                     try {
    213                         ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
    214                         byte[] bytes = new byte[buffer.remaining()];
    215                         buffer.get(bytes);
    216                         imageOut.write(bytes);
    217                     } finally {
    218                         mImage.close();
    219                     }
    220                 }
    221             }, mName, "Saved from Simple Camera Demo");
    222         }
    223     }
    224 
    225     /**
    226      * Create a new camera ops thread.
    227      *
    228      * @param errorDisplayer listener for displaying error messages
    229      * @param readyListener  listener for notifying when camera is ready for requests
    230      */
    231     CameraOps(CameraManager manager, ErrorDisplayer errorDisplayer,
    232               CameraReadyListener readyListener) {
    233         mReadyHandler = new Handler(Looper.getMainLooper());
    234 
    235         mCameraThread = new HandlerThread("CameraOpsThread");
    236         mCameraThread.start();
    237 
    238         if (manager == null || errorDisplayer == null ||
    239                 readyListener == null || mReadyHandler == null) {
    240             throw new IllegalArgumentException("Need valid displayer, listener, handler");
    241         }
    242 
    243         mCameraManager = manager;
    244         mErrorDisplayer = errorDisplayer;
    245         mReadyListener = readyListener;
    246 
    247     }
    248 
    249     /**
    250      * Open the first backfacing camera listed by the camera manager.
    251      * Displays a dialog if it cannot open a camera.
    252      */
    253     public void openCamera(final String cameraId) {
    254         mCameraHandler = new Handler(mCameraThread.getLooper());
    255 
    256         mCameraHandler.post(new Runnable() {
    257             public void run() {
    258                 if (mCameraDevice != null) {
    259                     throw new IllegalStateException("Camera already open");
    260                 }
    261                 try {
    262 
    263                     mCameraManager.openCamera(cameraId, mCameraDeviceListener, mCameraHandler);
    264                 } catch (CameraAccessException e) {
    265                     String errorMessage = mErrorDisplayer.getErrorString(e);
    266                     mErrorDisplayer.showErrorDialog(errorMessage);
    267                 }
    268             }
    269         });
    270     }
    271 
    272     public void pause() {
    273         closeCameraAndWait();
    274         mBackgroundThread.quitSafely();
    275         try {
    276             mBackgroundThread.join();
    277             mBackgroundThread = null;
    278             mBackgroundHandler = null;
    279         } catch (InterruptedException e) {
    280             e.printStackTrace();
    281         }
    282     }
    283 
    284     public Size getBestSize() {
    285         // Find a good size for output - largest 16:9 aspect ratio that's less than 720p
    286         final int MAX_WIDTH = 1280;
    287         final float TARGET_ASPECT = 16.f / 9.f;
    288         final float ASPECT_TOLERANCE = 0.1f;
    289 
    290 
    291         StreamConfigurationMap configs =
    292                 mCameraInfo.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    293 
    294         Size[] outputSizes = configs.getOutputSizes(SurfaceHolder.class);
    295 
    296         Size outputSize = outputSizes[0];
    297         float outputAspect = (float) outputSize.getWidth() / outputSize.getHeight();
    298         for (Size candidateSize : outputSizes) {
    299             if (candidateSize.getWidth() > MAX_WIDTH) continue;
    300             float candidateAspect = (float) candidateSize.getWidth() / candidateSize.getHeight();
    301             boolean goodCandidateAspect =
    302                     Math.abs(candidateAspect - TARGET_ASPECT) < ASPECT_TOLERANCE;
    303             boolean goodOutputAspect =
    304                     Math.abs(outputAspect - TARGET_ASPECT) < ASPECT_TOLERANCE;
    305             if ((goodCandidateAspect && !goodOutputAspect) ||
    306                     candidateSize.getWidth() > outputSize.getWidth()) {
    307                 outputSize = candidateSize;
    308                 outputAspect = candidateAspect;
    309             }
    310         }
    311         return outputSize;
    312     }
    313 
    314     /**
    315      * Close the camera and wait for the close callback to be called in the camera thread.
    316      * Times out after @{value CAMERA_CLOSE_TIMEOUT} ms.
    317      */
    318     public void closeCameraAndWait() {
    319         mCloseWaiter.close();
    320         mCameraHandler.post(mCloseCameraRunnable);
    321         boolean closed = mCloseWaiter.block(CAMERA_CLOSE_TIMEOUT);
    322         if (!closed) {
    323             Log.e(TAG, "Timeout closing camera");
    324         }
    325     }
    326 
    327     private Runnable mCloseCameraRunnable = new Runnable() {
    328         public void run() {
    329             if (mCameraDevice != null) {
    330                 mCameraDevice.close();
    331             }
    332             mCameraDevice = null;
    333             mCameraSession = null;
    334             mSurfaces = null;
    335         }
    336     };
    337 
    338     /**
    339      * Set the output Surfaces, and finish configuration if otherwise ready.
    340      */
    341     public void setSurface(Surface surface) {
    342         final List<Surface> surfaceList = new ArrayList<Surface>();
    343         surfaceList.add(surface);
    344         surfaceList.add(mImageReader.getSurface());
    345 
    346         mCameraHandler.post(new Runnable() {
    347             public void run() {
    348                 mSurfaces = surfaceList;
    349                 startCameraSession();
    350             }
    351         });
    352     }
    353 
    354     /**
    355      * Get a request builder for the current camera.
    356      */
    357     public CaptureRequest.Builder createCaptureRequest(int template) throws CameraAccessException {
    358         CameraDevice device = mCameraDevice;
    359         if (device == null) {
    360             throw new IllegalStateException("Can't get requests when no camera is open");
    361         }
    362         return device.createCaptureRequest(template);
    363     }
    364 
    365     /**
    366      * Set a repeating request.
    367      */
    368     public void setRepeatingRequest(final CaptureRequest request,
    369                                     final CameraCaptureSession.CaptureCallback listener,
    370                                     final Handler handler) {
    371         mCameraHandler.post(new Runnable() {
    372             public void run() {
    373                 try {
    374                     mCameraSession.setRepeatingRequest(request, listener, handler);
    375                 } catch (CameraAccessException e) {
    376                     String errorMessage = mErrorDisplayer.getErrorString(e);
    377                     mErrorDisplayer.showErrorDialog(errorMessage);
    378                 }
    379             }
    380         });
    381     }
    382 
    383     /**
    384      * Set a repeating request.
    385      */
    386     public void setRepeatingBurst(final List<CaptureRequest> requests,
    387                                   final CameraCaptureSession.CaptureCallback listener,
    388                                   final Handler handler) {
    389         mCameraHandler.post(new Runnable() {
    390             public void run() {
    391                 try {
    392                     mCameraSession.setRepeatingBurst(requests, listener, handler);
    393 
    394                 } catch (CameraAccessException e) {
    395                     String errorMessage = mErrorDisplayer.getErrorString(e);
    396                     mErrorDisplayer.showErrorDialog(errorMessage);
    397                 }
    398             }
    399         });
    400     }
    401 
    402     /**
    403      * Configure the camera session.
    404      */
    405     private void startCameraSession() {
    406         // Wait until both the camera device is open and the SurfaceView is ready
    407         if (mCameraDevice == null || mSurfaces == null) return;
    408 
    409         try {
    410 
    411             mCameraDevice.createCaptureSession(
    412                     mSurfaces, mCameraSessionListener, mCameraHandler);
    413         } catch (CameraAccessException e) {
    414             String errorMessage = mErrorDisplayer.getErrorString(e);
    415             mErrorDisplayer.showErrorDialog(errorMessage);
    416             mCameraDevice.close();
    417             mCameraDevice = null;
    418         }
    419     }
    420 
    421     /**
    422      * Main listener for camera session events
    423      * Invoked on mCameraThread
    424      */
    425     private CameraCaptureSession.StateCallback mCameraSessionListener =
    426             new CameraCaptureSession.StateCallback() {
    427 
    428                 @Override
    429                 public void onConfigured(CameraCaptureSession session) {
    430                     mCameraSession = session;
    431                     mReadyHandler.post(new Runnable() {
    432                         public void run() {
    433                             // This can happen when the screen is turned off and turned back on.
    434                             if (null == mCameraDevice) {
    435                                 return;
    436                             }
    437 
    438                             mReadyListener.onCameraReady();
    439                         }
    440                     });
    441 
    442                 }
    443 
    444                 @Override
    445                 public void onConfigureFailed(CameraCaptureSession session) {
    446                     mErrorDisplayer.showErrorDialog("Unable to configure the capture session");
    447                     mCameraDevice.close();
    448                     mCameraDevice = null;
    449                 }
    450             };
    451 
    452     /**
    453      * Main listener for camera device events.
    454      * Invoked on mCameraThread
    455      */
    456     private CameraDevice.StateCallback mCameraDeviceListener = new CameraDevice.StateCallback() {
    457 
    458         @Override
    459         public void onOpened(CameraDevice camera) {
    460             mCameraDevice = camera;
    461             startCameraSession();
    462         }
    463 
    464         @Override
    465         public void onClosed(CameraDevice camera) {
    466             mCloseWaiter.open();
    467         }
    468 
    469         @Override
    470         public void onDisconnected(CameraDevice camera) {
    471             mErrorDisplayer.showErrorDialog("The camera device has been disconnected.");
    472             camera.close();
    473             mCameraDevice = null;
    474         }
    475 
    476         @Override
    477         public void onError(CameraDevice camera, int error) {
    478             mErrorDisplayer.showErrorDialog("The camera encountered an error:" + error);
    479             camera.close();
    480             mCameraDevice = null;
    481         }
    482 
    483     };
    484 
    485     public void captureStillPicture(int currentJpegRotation, String name, ContentResolver resolver) {
    486         mSaveFileName = name;
    487         mContentResolver = resolver;
    488         try {
    489             // TODO call lock focus if we are in "AF-S(One-Shot AF) mode"
    490             // TODO call precapture if we are using flash
    491             // This is the CaptureRequest.Builder that we use to take a picture.
    492             final CaptureRequest.Builder captureBuilder =
    493                     createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
    494             Log.v(TAG, " Target " + mImageReader.getWidth() + "," + mImageReader.getHeight());
    495 
    496             captureBuilder.addTarget(mImageReader.getSurface());
    497 
    498             captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, currentJpegRotation);
    499 
    500             CameraCaptureSession.CaptureCallback captureCallback
    501                     = new CameraCaptureSession.CaptureCallback() {
    502 
    503                 @Override
    504                 public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
    505                                                TotalCaptureResult result) {
    506                     Log.v(TAG, " onCaptureCompleted");
    507                     setParameters();
    508                 }
    509             };
    510 
    511 
    512             setRequest(captureBuilder.build(), captureCallback, null);
    513         } catch (CameraAccessException e) {
    514             e.printStackTrace();
    515         }
    516     }
    517 
    518     /**
    519      * Set a repeating request.
    520      */
    521     private void setRequest(final CaptureRequest request,
    522                             final CameraCaptureSession.CaptureCallback listener,
    523                             final Handler handler) {
    524         mCameraHandler.post(new Runnable() {
    525             public void run() {
    526                 try {
    527                     mCameraSession.stopRepeating();
    528                     mCameraSession.capture(request, listener, handler);
    529                 } catch (CameraAccessException e) {
    530                     String errorMessage = mErrorDisplayer.getErrorString(e);
    531                     mErrorDisplayer.showErrorDialog(errorMessage);
    532                 }
    533             }
    534         });
    535     }
    536 
    537     public void setUpCamera(Surface processingNormalSurface) {
    538         mProcessingNormalSurface = processingNormalSurface;
    539         // Ready to send requests in, so set them up
    540         try {
    541             CaptureRequest.Builder previewBuilder =
    542                     createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
    543             previewBuilder.addTarget(mProcessingNormalSurface);
    544             previewBuilder.setTag(mAutoExposureTag);
    545             mPreviewRequest = previewBuilder.build();
    546             mHdrBuilder = createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
    547             mHdrBuilder.set(CaptureRequest.CONTROL_AE_MODE,
    548                     CaptureRequest.CONTROL_AE_MODE_OFF);
    549             mHdrBuilder.addTarget(mProcessingNormalSurface);
    550             setParameters();
    551 
    552         } catch (CameraAccessException e) {
    553             String errorMessage = e.getMessage();
    554             // MessageDialogFragment.newInstance(errorMessage).show(getFragmentManager(), FRAGMENT_DIALOG);
    555         }
    556     }
    557 
    558     /**
    559      * Start running an HDR burst on a configured camera session
    560      */
    561     public void setParameters() {
    562         if (mHdrBuilder == null) {
    563             Log.v(TAG," Camera not set up");
    564             return;
    565         }
    566         if (mAutoExposure) {
    567             mHdrBuilder.set(CaptureRequest.SENSOR_FRAME_DURATION, ONE_SECOND / 30);
    568             mHdrBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, getExposure());
    569             mHdrBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
    570         } else {
    571             mHdrBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_OFF);
    572             mHdrBuilder.set(CaptureRequest.SENSOR_FRAME_DURATION, ONE_SECOND / 30);
    573             mHdrBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, getExposure());
    574             mHdrBuilder.set(CaptureRequest.SENSOR_SENSITIVITY, getIso());
    575         }
    576         if (mAutoFocus) {
    577             mHdrBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);
    578             mHdrBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
    579         } else {
    580             mHdrBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);
    581             mHdrBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, getFocusDistance());
    582             mHdrBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
    583         }
    584 
    585         setRepeatingRequest(mHdrBuilder.build(), mCaptureCallback, mReadyHandler);
    586     }
    587 
    588     private CameraCaptureSession.CaptureCallback mCaptureCallback
    589             = new CameraCaptureSession.CaptureCallback() {
    590 
    591         public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
    592                                        TotalCaptureResult result) {
    593         }
    594     };
    595 
    596     /**
    597      * Simple listener for main code to know the camera is ready for requests, or failed to
    598      * start.
    599      */
    600     public interface CameraReadyListener {
    601         public void onCameraReady();
    602     }
    603 
    604     /**
    605      * Simple listener for displaying error messages
    606      */
    607     public interface ErrorDisplayer {
    608         public void showErrorDialog(String errorMessage);
    609         public String getErrorString(CameraAccessException e);
    610     }
    611 
    612     public float getFocusDistance() {
    613         return mFocusDist;
    614     }
    615 
    616     public void setFocusDistance(float focusDistance) {
    617         mFocusDist = focusDistance;
    618     }
    619 
    620     public void setIso(int iso) {
    621         mIso = iso;
    622     }
    623 
    624     public boolean isAutoExposure() {
    625         return mAutoExposure;
    626     }
    627 
    628     public void setAutoExposure(boolean autoExposure) {
    629         mAutoExposure = autoExposure;
    630     }
    631 
    632     public boolean isAutoFocus() {
    633         return mAutoFocus;
    634     }
    635 
    636     public void setAutoFocus(boolean autoFocus) {
    637         mAutoFocus = autoFocus;
    638     }
    639 
    640     public int getIso() {
    641         return mIso;
    642     }
    643 
    644     public long getExposure() {
    645         return mExposure;
    646     }
    647 
    648     public void setExposure(long exposure) {
    649         mExposure = exposure;
    650     }
    651 
    652     public int getIsoMax() {
    653         return mISOmax;
    654     }
    655 
    656     public int getIsoMin() {
    657         return mISOmin;
    658     }
    659 
    660     public long getExpMax() {
    661         return mExpMax;
    662     }
    663 
    664     public long getExpMin() {
    665         return mExpMin;
    666     }
    667 
    668     public float getFocusMin() {
    669         return mFocusMin;
    670     }
    671 }