1/*
2 * Copyright (C) 2008 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
17package com.android.mediaframeworktest.functional;
18
19import com.android.mediaframeworktest.MediaFrameworkTest;
20import com.android.mediaframeworktest.MediaNames;
21
22import android.content.Context;
23import android.hardware.Camera;
24import android.hardware.Camera.PictureCallback;
25import android.hardware.Camera.PreviewCallback;
26import android.hardware.Camera.ShutterCallback;
27import android.os.ConditionVariable;
28import android.os.Environment;
29import android.os.Looper;
30import android.test.ActivityInstrumentationTestCase;
31import android.test.suitebuilder.annotation.LargeTest;
32import android.util.Log;
33import android.view.SurfaceHolder;
34
35import java.io.*;
36
37/**
38 * Junit / Instrumentation test case for the camera api
39 *
40 * To run only tests in this class:
41 *
42 * adb shell am instrument \
43 *   -e class com.android.mediaframeworktest.functional.CameraTest \
44 *   -w  com.android.mediaframeworktest/.MediaFrameworkTestRunner
45 */
46public class CameraTest extends ActivityInstrumentationTestCase<MediaFrameworkTest> {
47    private String TAG = "CameraTest";
48
49    private boolean rawPreviewCallbackResult = false;
50    private boolean shutterCallbackResult = false;
51    private boolean rawPictureCallbackResult = false;
52    private boolean jpegPictureCallbackResult = false;
53
54    private static int WAIT_FOR_COMMAND_TO_COMPLETE = 10000;  // Milliseconds.
55    private static final int CAMERA_ID = 0;
56
57    private RawPreviewCallback mRawPreviewCallback = new RawPreviewCallback();
58    private TestShutterCallback mShutterCallback = new TestShutterCallback();
59    private RawPictureCallback mRawPictureCallback = new RawPictureCallback();
60    private JpegPictureCallback mJpegPictureCallback = new JpegPictureCallback();
61
62    private boolean mInitialized = false;
63    private Looper mLooper = null;
64    private final ConditionVariable mPreviewDone = new ConditionVariable();
65    private final ConditionVariable mSnapshotDone = new ConditionVariable();
66
67    Camera mCamera;
68    Context mContext;
69
70    public CameraTest() {
71        super("com.android.mediaframeworktest", MediaFrameworkTest.class);
72    }
73
74    protected void setUp() throws Exception {
75        super.setUp();
76    }
77
78    /*
79     * Initializes the message looper so that the Camera object can
80     * receive the callback messages.
81     */
82    private void initializeMessageLooper() {
83        final ConditionVariable startDone = new ConditionVariable();
84        Log.v(TAG, "start looper");
85        new Thread() {
86            @Override
87            public void run() {
88                // Set up a looper to be used by camera.
89                Looper.prepare();
90                Log.v(TAG, "start loopRun");
91                // Save the looper so that we can terminate this thread
92                // after we are done with it.
93                mLooper = Looper.myLooper();
94                mCamera = Camera.open(CAMERA_ID);
95                startDone.open();
96                Looper.loop();  // Blocks forever until Looper.quit() is called.
97                Log.v(TAG, "initializeMessageLooper: quit.");
98            }
99        }.start();
100
101        if (!startDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) {
102            fail("initializeMessageLooper: start timeout");
103        }
104    }
105
106    /*
107     * Terminates the message looper thread.
108     */
109    private void terminateMessageLooper() throws Exception {
110        mLooper.quit();
111        // Looper.quit() is asynchronous. The looper may still has some
112        // preview callbacks in the queue after quit is called. The preview
113        // callback still uses the camera object (setHasPreviewCallback).
114        // After camera is released, RuntimeException will be thrown from
115        // the method. So we need to join the looper thread here.
116        mLooper.getThread().join();
117        mCamera.release();
118    }
119
120    //Implement the previewCallback
121    private final class RawPreviewCallback implements PreviewCallback {
122        public void onPreviewFrame(byte [] rawData, Camera camera) {
123            Log.v(TAG, "Preview callback start");
124            int rawDataLength = 0;
125            if (rawData != null) {
126                rawDataLength = rawData.length;
127            }
128            if (rawDataLength > 0) {
129                rawPreviewCallbackResult = true;
130            } else {
131                rawPreviewCallbackResult = false;
132            }
133            mPreviewDone.open();
134            Log.v(TAG, "Preview callback stop");
135        }
136    };
137
138    //Implement the shutterCallback
139    private final class TestShutterCallback implements ShutterCallback {
140        public void onShutter() {
141            shutterCallbackResult = true;
142            Log.v(TAG, "onShutter called");
143        }
144    };
145
146    //Implement the RawPictureCallback
147    private final class RawPictureCallback implements PictureCallback {
148        public void onPictureTaken(byte [] rawData, Camera camera) {
149            // no support for raw data - success if we get the callback
150            rawPictureCallbackResult = true;
151            Log.v(TAG, "RawPictureCallback callback");
152        }
153    };
154
155    //Implement the JpegPictureCallback
156    private final class JpegPictureCallback implements PictureCallback {
157        public void onPictureTaken(byte [] rawData, Camera camera) {
158            try {
159                if (rawData != null) {
160                    int rawDataLength = rawData.length;
161                    File rawoutput = new File(
162                            Environment.getExternalStorageDirectory().toString(), "/test.bmp");
163                    FileOutputStream outstream = new FileOutputStream(rawoutput);
164                    outstream.write(rawData);
165                    Log.v(TAG, "JpegPictureCallback rawDataLength = " + rawDataLength);
166                    jpegPictureCallbackResult = true;
167                } else {
168                    jpegPictureCallbackResult = false;
169                }
170                mSnapshotDone.open();
171                Log.v(TAG, "Jpeg Picture callback");
172            } catch (Exception e) {
173                Log.v(TAG, e.toString());
174            }
175        }
176    };
177
178    private void waitForPreviewDone() {
179        if (!mPreviewDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) {
180            Log.v(TAG, "waitForPreviewDone: timeout");
181        }
182        mPreviewDone.close();
183    }
184
185    private void waitForSnapshotDone() {
186        if (!mSnapshotDone.block(MediaNames.WAIT_SNAPSHOT_TIME)) {
187            // timeout could be expected or unexpected. The caller will decide.
188            Log.v(TAG, "waitForSnapshotDone: timeout");
189        }
190        mSnapshotDone.close();
191    }
192
193
194    private void checkTakePicture() {
195        SurfaceHolder mSurfaceHolder;
196        try {
197            mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
198            mCamera.setPreviewDisplay(mSurfaceHolder);
199            Log.v(TAG, "Start preview");
200            mCamera.startPreview();
201            waitForPreviewDone();
202            mCamera.setPreviewCallback(null);
203            mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
204            waitForSnapshotDone();
205        } catch (Exception e) {
206            Log.v(TAG, e.toString());
207        }
208    }
209
210    private void checkPreviewCallback() {
211        SurfaceHolder mSurfaceHolder;
212        try {
213            mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
214            mCamera.setPreviewDisplay(mSurfaceHolder);
215            Log.v(TAG, "start preview");
216            mCamera.startPreview();
217            waitForPreviewDone();
218            mCamera.setPreviewCallback(null);
219        } catch (Exception e) {
220            Log.v(TAG, e.toString());
221        }
222    }
223
224    /*
225     * TODO(yslau): Need to setup the golden rawData and compare the
226     * the new captured rawData with the golden one.
227     *
228     * Test case 1: Take a picture and verify all the callback
229     * functions are called properly.
230     */
231    @LargeTest
232    public void testTakePicture() throws Exception {
233        initializeMessageLooper();
234        mCamera.setPreviewCallback(mRawPreviewCallback);
235        checkTakePicture();
236        terminateMessageLooper();
237        assertTrue("shutterCallbackResult", shutterCallbackResult);
238        assertTrue("rawPictureCallbackResult", rawPictureCallbackResult);
239        assertTrue("jpegPictureCallbackResult", jpegPictureCallbackResult);
240    }
241
242    /*
243     * Test case 2: Set the preview and
244     * verify the RawPreviewCallback is called
245     */
246    @LargeTest
247    public void testCheckPreview() throws Exception {
248        initializeMessageLooper();
249        mCamera.setPreviewCallback(mRawPreviewCallback);
250        checkPreviewCallback();
251        terminateMessageLooper();
252        assertTrue("RawPreviewCallbackResult", rawPreviewCallbackResult);
253    }
254
255}
256
257