CameraHolder.java revision 43b6525b4aedc5e177163cab7b6f26698a19097f
191acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang/*
291acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * Copyright (C) 2009 The Android Open Source Project
391acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang *
491acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * Licensed under the Apache License, Version 2.0 (the "License");
591acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * you may not use this file except in compliance with the License.
691acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * You may obtain a copy of the License at
791acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang *
891acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang *      http://www.apache.org/licenses/LICENSE-2.0
991acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang *
1091acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * Unless required by applicable law or agreed to in writing, software
1191acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * distributed under the License is distributed on an "AS IS" BASIS,
1291acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1391acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * See the License for the specific language governing permissions and
1491acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * limitations under the License.
1591acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang */
1691acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang
17cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changpackage com.android.camera;
18cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
193f3c857e3f34650c15d764810335024654b0fcc3Owen Linimport static com.android.camera.Util.Assert;
203f3c857e3f34650c15d764810335024654b0fcc3Owen Lin
217add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Liimport android.hardware.Camera.CameraInfo;
224c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Changimport android.hardware.Camera.Parameters;
23f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Linimport android.os.Build;
24cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport android.os.Handler;
25cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport android.os.HandlerThread;
26cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport android.os.Looper;
27cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport android.os.Message;
28cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport android.util.Log;
29cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
30cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport java.io.IOException;
31cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
32271b3095b9f763421c0547109da9de774795072dChih-Chung Chang/**
33271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * The class is used to hold an {@code android.hardware.Camera} instance.
34271b3095b9f763421c0547109da9de774795072dChih-Chung Chang *
35271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * <p>The {@code open()} and {@code release()} calls are similar to the ones
36271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * in {@code android.hardware.Camera}. The difference is if {@code keep()} is
37271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * called before {@code release()}, CameraHolder will try to hold the {@code
38271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * android.hardware.Camera} instance for a while, so if {@code open()} is
39271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * called soon after, we can avoid the cost of {@code open()} in {@code
40271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * android.hardware.Camera}.
41271b3095b9f763421c0547109da9de774795072dChih-Chung Chang *
42271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * <p>This is used in switching between {@code Camera} and {@code VideoCamera}
43271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * activities.
44271b3095b9f763421c0547109da9de774795072dChih-Chung Chang */
45cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changpublic class CameraHolder {
46cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private static final String TAG = "CameraHolder";
47cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private android.hardware.Camera mCameraDevice;
4891acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang    private long mKeepBeforeTime = 0;  // Keep the Camera before this time.
493f3c857e3f34650c15d764810335024654b0fcc3Owen Lin    private final Handler mHandler;
5091acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang    private int mUsers = 0;  // number of open() - number of release()
51ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang    private int mNumberOfCameras;
5243b6525b4aedc5e177163cab7b6f26698a19097fWu-cheng Li    private int mCameraId = -1;
537add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li    private CameraInfo[] mInfo;
54cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
554c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang    // We store the camera parameters when we actually open the device,
564c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang    // so we can restore them in the subsequent open() requests by the user.
574c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang    // This prevents the parameters set by the Camera activity used by
584c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang    // the VideoCamera activity inadvertently.
594c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang    private Parameters mParameters;
604c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang
61cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    // Use a singleton.
62cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private static CameraHolder sHolder;
63cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    public static synchronized CameraHolder instance() {
64cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        if (sHolder == null) {
65cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            sHolder = new CameraHolder();
66cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        }
67cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        return sHolder;
68cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
69cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
70cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private static final int RELEASE_CAMERA = 1;
71cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private class MyHandler extends Handler {
72cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        MyHandler(Looper looper) {
73cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            super(looper);
74cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        }
75cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
76cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        @Override
77cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        public void handleMessage(Message msg) {
78cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            switch(msg.what) {
79cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang                case RELEASE_CAMERA:
80dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                    synchronized (CameraHolder.this) {
81dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                        // In 'CameraHolder.open', the 'RELEASE_CAMERA' message
82dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                        // will be removed if it is found in the queue. However,
83dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                        // there is a chance that this message has been handled
84dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                        // before being removed. So, we need to add a check
85dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                        // here:
86dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                        if (CameraHolder.this.mUsers == 0) releaseCamera();
87dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                    }
88cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang                    break;
89cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            }
90cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        }
91cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
92cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
93cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private CameraHolder() {
94cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        HandlerThread ht = new HandlerThread("CameraHolder");
95cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        ht.start();
96cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        mHandler = new MyHandler(ht.getLooper());
97ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang        mNumberOfCameras = android.hardware.Camera.getNumberOfCameras();
987add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li        mInfo = new CameraInfo[mNumberOfCameras];
99ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang        for (int i = 0; i < mNumberOfCameras; i++) {
1007add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li            mInfo[i] = new CameraInfo();
1017add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li            android.hardware.Camera.getCameraInfo(i, mInfo[i]);
102ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang        }
103ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang    }
104ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang
105ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang    public int getNumberOfCameras() {
106ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang        return mNumberOfCameras;
107cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
108cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
1097add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li    public CameraInfo[] getCameraInfo() {
1107add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li        return mInfo;
1117add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li    }
1127add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li
113ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang    public synchronized android.hardware.Camera open(int cameraId)
1143f3c857e3f34650c15d764810335024654b0fcc3Owen Lin            throws CameraHardwareException {
11591acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        Assert(mUsers == 0);
11643b6525b4aedc5e177163cab7b6f26698a19097fWu-cheng Li        if (mCameraDevice != null && mCameraId != cameraId) {
11743b6525b4aedc5e177163cab7b6f26698a19097fWu-cheng Li            mCameraDevice.release();
11843b6525b4aedc5e177163cab7b6f26698a19097fWu-cheng Li            mCameraDevice = null;
11943b6525b4aedc5e177163cab7b6f26698a19097fWu-cheng Li            mCameraId = -1;
12043b6525b4aedc5e177163cab7b6f26698a19097fWu-cheng Li        }
121cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        if (mCameraDevice == null) {
1223f3c857e3f34650c15d764810335024654b0fcc3Owen Lin            try {
123ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang                Log.v(TAG, "open camera " + cameraId);
124ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang                mCameraDevice = android.hardware.Camera.open(cameraId);
12543b6525b4aedc5e177163cab7b6f26698a19097fWu-cheng Li                mCameraId = cameraId;
1263f3c857e3f34650c15d764810335024654b0fcc3Owen Lin            } catch (RuntimeException e) {
1273f3c857e3f34650c15d764810335024654b0fcc3Owen Lin                Log.e(TAG, "fail to connect Camera", e);
128f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin                throw new CameraHardwareException(e);
1293f3c857e3f34650c15d764810335024654b0fcc3Owen Lin            }
1304c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang            mParameters = mCameraDevice.getParameters();
131cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        } else {
132cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            try {
133cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang                mCameraDevice.reconnect();
134cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            } catch (IOException e) {
135cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang                Log.e(TAG, "reconnect failed.");
136f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin                throw new CameraHardwareException(e);
137cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            }
1384c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang            mCameraDevice.setParameters(mParameters);
139cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        }
14091acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        ++mUsers;
141cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        mHandler.removeMessages(RELEASE_CAMERA);
14291acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        mKeepBeforeTime = 0;
143cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        return mCameraDevice;
144cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
145cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
146d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin    /**
147d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin     * Tries to open the hardware camera. If the camera is being used or
148d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin     * unavailable then return {@code null}.
149d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin     */
150ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang    public synchronized android.hardware.Camera tryOpen(int cameraId) {
151d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin        try {
152ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang            return mUsers == 0 ? open(cameraId) : null;
153d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin        } catch (CameraHardwareException e) {
154f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin            // In eng build, we throw the exception so that test tool
155f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin            // can detect it and report it
156f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin            if ("eng".equals(Build.TYPE)) {
157f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin                throw new RuntimeException(e);
158f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin            }
159d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin            return null;
160d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin        }
161d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin    }
162d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin
163cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    public synchronized void release() {
16491acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        Assert(mUsers == 1);
16591acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        --mUsers;
166cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        mCameraDevice.stopPreview();
167cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        releaseCamera();
168cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
169cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
170cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private synchronized void releaseCamera() {
17191acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        Assert(mUsers == 0);
172cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        Assert(mCameraDevice != null);
173cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        long now = System.currentTimeMillis();
17491acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        if (now < mKeepBeforeTime) {
175cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            mHandler.sendEmptyMessageDelayed(RELEASE_CAMERA,
17691acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang                    mKeepBeforeTime - now);
177cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            return;
178cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        }
179cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        mCameraDevice.release();
180cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        mCameraDevice = null;
18143b6525b4aedc5e177163cab7b6f26698a19097fWu-cheng Li        mCameraId = -1;
182cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
183cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
184cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    public synchronized void keep() {
18591acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        // We allow (mUsers == 0) for the convenience of the calling activity.
1865075f53df86f3dcb0a46fd6353057260ad480f43Chih-Chung Chang        // The activity may not have a chance to call open() before the user
1875075f53df86f3dcb0a46fd6353057260ad480f43Chih-Chung Chang        // choose the menu item to switch to another activity.
18891acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        Assert(mUsers == 1 || mUsers == 0);
189cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        // Keep the camera instance for 3 seconds.
19091acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        mKeepBeforeTime = System.currentTimeMillis() + 3000;
191cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
192cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang}
193