CameraHolder.java revision ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5
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
214c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Changimport android.hardware.Camera.Parameters;
22f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Linimport android.os.Build;
23cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport android.os.Handler;
24cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport android.os.HandlerThread;
25cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport android.os.Looper;
26cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport android.os.Message;
27cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport android.util.Log;
28cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
29cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport java.io.IOException;
30cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
31271b3095b9f763421c0547109da9de774795072dChih-Chung Chang/**
32271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * The class is used to hold an {@code android.hardware.Camera} instance.
33271b3095b9f763421c0547109da9de774795072dChih-Chung Chang *
34271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * <p>The {@code open()} and {@code release()} calls are similar to the ones
35271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * in {@code android.hardware.Camera}. The difference is if {@code keep()} is
36271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * called before {@code release()}, CameraHolder will try to hold the {@code
37271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * android.hardware.Camera} instance for a while, so if {@code open()} is
38271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * called soon after, we can avoid the cost of {@code open()} in {@code
39271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * android.hardware.Camera}.
40271b3095b9f763421c0547109da9de774795072dChih-Chung Chang *
41271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * <p>This is used in switching between {@code Camera} and {@code VideoCamera}
42271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * activities.
43271b3095b9f763421c0547109da9de774795072dChih-Chung Chang */
44cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changpublic class CameraHolder {
45cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private static final String TAG = "CameraHolder";
46cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private android.hardware.Camera mCameraDevice;
4791acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang    private long mKeepBeforeTime = 0;  // Keep the Camera before this time.
483f3c857e3f34650c15d764810335024654b0fcc3Owen Lin    private final Handler mHandler;
4991acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang    private int mUsers = 0;  // number of open() - number of release()
50ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang    private int mNumberOfCameras;
51cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
524c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang    // We store the camera parameters when we actually open the device,
534c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang    // so we can restore them in the subsequent open() requests by the user.
544c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang    // This prevents the parameters set by the Camera activity used by
554c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang    // the VideoCamera activity inadvertently.
564c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang    private Parameters mParameters;
574c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang
58cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    // Use a singleton.
59cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private static CameraHolder sHolder;
60cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    public static synchronized CameraHolder instance() {
61cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        if (sHolder == null) {
62cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            sHolder = new CameraHolder();
63cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        }
64cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        return sHolder;
65cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
66cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
67cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private static final int RELEASE_CAMERA = 1;
68cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private class MyHandler extends Handler {
69cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        MyHandler(Looper looper) {
70cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            super(looper);
71cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        }
72cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
73cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        @Override
74cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        public void handleMessage(Message msg) {
75cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            switch(msg.what) {
76cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang                case RELEASE_CAMERA:
77dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                    synchronized (CameraHolder.this) {
78dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                        // In 'CameraHolder.open', the 'RELEASE_CAMERA' message
79dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                        // will be removed if it is found in the queue. However,
80dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                        // there is a chance that this message has been handled
81dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                        // before being removed. So, we need to add a check
82dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                        // here:
83dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                        if (CameraHolder.this.mUsers == 0) releaseCamera();
84dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin                    }
85cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang                    break;
86cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            }
87cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        }
88cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
89cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
90cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private CameraHolder() {
91cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        HandlerThread ht = new HandlerThread("CameraHolder");
92cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        ht.start();
93cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        mHandler = new MyHandler(ht.getLooper());
94ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang        mNumberOfCameras = android.hardware.Camera.getNumberOfCameras();
95ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang
96ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang        android.hardware.Camera.CameraInfo info =
97ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang                new android.hardware.Camera.CameraInfo();
98ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang        Log.v(TAG, "# of cameras:" + mNumberOfCameras);
99ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang        for (int i = 0; i < mNumberOfCameras; i++) {
100ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang            Log.v(TAG, "camera info #" + i);
101ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang            android.hardware.Camera.getCameraInfo(i, info);
102ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang            Log.v(TAG, "facing: " + info.mFacing);
103ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang            Log.v(TAG, "orientation: " + info.mOrientation);
104ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang        }
105ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang    }
106ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang
107ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang    public int getNumberOfCameras() {
108ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang        return mNumberOfCameras;
109cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
110cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
111ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang    public synchronized android.hardware.Camera open(int cameraId)
1123f3c857e3f34650c15d764810335024654b0fcc3Owen Lin            throws CameraHardwareException {
11391acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        Assert(mUsers == 0);
114cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        if (mCameraDevice == null) {
1153f3c857e3f34650c15d764810335024654b0fcc3Owen Lin            try {
116ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang                Log.v(TAG, "open camera " + cameraId);
117ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang                mCameraDevice = android.hardware.Camera.open(cameraId);
1183f3c857e3f34650c15d764810335024654b0fcc3Owen Lin            } catch (RuntimeException e) {
1193f3c857e3f34650c15d764810335024654b0fcc3Owen Lin                Log.e(TAG, "fail to connect Camera", e);
120f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin                throw new CameraHardwareException(e);
1213f3c857e3f34650c15d764810335024654b0fcc3Owen Lin            }
1224c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang            mParameters = mCameraDevice.getParameters();
123cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        } else {
124cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            try {
125cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang                mCameraDevice.reconnect();
126cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            } catch (IOException e) {
127cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang                Log.e(TAG, "reconnect failed.");
128f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin                throw new CameraHardwareException(e);
129cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            }
1304c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang            mCameraDevice.setParameters(mParameters);
131cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        }
13291acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        ++mUsers;
133cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        mHandler.removeMessages(RELEASE_CAMERA);
13491acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        mKeepBeforeTime = 0;
135cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        return mCameraDevice;
136cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
137cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
138d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin    /**
139d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin     * Tries to open the hardware camera. If the camera is being used or
140d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin     * unavailable then return {@code null}.
141d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin     */
142ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang    public synchronized android.hardware.Camera tryOpen(int cameraId) {
143d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin        try {
144ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang            return mUsers == 0 ? open(cameraId) : null;
145d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin        } catch (CameraHardwareException e) {
146f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin            // In eng build, we throw the exception so that test tool
147f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin            // can detect it and report it
148f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin            if ("eng".equals(Build.TYPE)) {
149f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin                throw new RuntimeException(e);
150f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin            }
151d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin            return null;
152d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin        }
153d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin    }
154d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin
155cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    public synchronized void release() {
15691acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        Assert(mUsers == 1);
15791acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        --mUsers;
158cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        mCameraDevice.stopPreview();
159cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        releaseCamera();
160cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
161cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
162cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    private synchronized void releaseCamera() {
16391acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        Assert(mUsers == 0);
164cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        Assert(mCameraDevice != null);
165cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        long now = System.currentTimeMillis();
16691acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        if (now < mKeepBeforeTime) {
167cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            mHandler.sendEmptyMessageDelayed(RELEASE_CAMERA,
16891acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang                    mKeepBeforeTime - now);
169cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang            return;
170cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        }
171cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        mCameraDevice.release();
172cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        mCameraDevice = null;
173cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
174cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang
175cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    public synchronized void keep() {
17691acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        // We allow (mUsers == 0) for the convenience of the calling activity.
1775075f53df86f3dcb0a46fd6353057260ad480f43Chih-Chung Chang        // The activity may not have a chance to call open() before the user
1785075f53df86f3dcb0a46fd6353057260ad480f43Chih-Chung Chang        // choose the menu item to switch to another activity.
17991acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        Assert(mUsers == 1 || mUsers == 0);
180cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang        // Keep the camera instance for 3 seconds.
18191acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang        mKeepBeforeTime = System.currentTimeMillis() + 3000;
182cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang    }
183cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang}
184