1 /*
2  * Copyright 2014 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.cts.verifier.sensors.sixdof.Renderer.RenderUtils;
18 
19 import android.annotation.TargetApi;
20 import android.content.Context;
21 import android.content.pm.ActivityInfo;
22 import android.content.res.Configuration;
23 import android.content.res.Resources;
24 import android.graphics.SurfaceTexture;
25 import android.hardware.camera2.CameraAccessException;
26 import android.hardware.camera2.CameraCaptureSession;
27 import android.hardware.camera2.CameraCharacteristics;
28 import android.hardware.camera2.CameraDevice;
29 import android.hardware.camera2.CameraManager;
30 import android.hardware.camera2.CaptureRequest;
31 import android.hardware.camera2.CaptureResult;
32 import android.hardware.camera2.TotalCaptureResult;
33 import android.os.Handler;
34 import android.os.HandlerThread;
35 import android.util.Log;
36 import android.util.Size;
37 import android.view.Display;
38 import android.view.Surface;
39 import android.view.WindowManager;
40 
41 import java.util.Arrays;
42 import java.util.concurrent.Semaphore;
43 import java.util.concurrent.TimeUnit;
44 
45 @TargetApi(21)
46 public class CameraStreamManager {
47 
48     private Context mContext;
49     private SurfaceTexture mSurfaceTextureToStreamTo;
50     private int mWidth;
51     private int mHeight;
52 
53     /**
54      * Tag for the {@link Log}.
55      */
56     private static final String TAG = "Camera2BasicFragment";
57 
58     /**
59      * ID of the current {@link CameraDevice}.
60      */
61     private static final String CAMERA_ID = "0";
62 
63     /**
64      * A {@link CameraCaptureSession } for camera preview.
65      */
66     private CameraCaptureSession mCaptureSession;
67 
68     /**
69      * A reference to the opened {@link CameraDevice}.
70      */
71     private CameraDevice mCameraDevice;
72 
73     /**
74      * The {@link android.util.Size} of camera preview.
75      */
76     private Size mPreviewSize;
77 
78     /**
79      * {@link CameraDevice.StateCallback} is called when {@link CameraDevice} changes its state.
80      */
81     private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
82 
83         @Override
84         public void onOpened(CameraDevice cameraDevice) {
85             // This method is called when the camera is opened.  We start camera preview here.
86             mCameraOpenCloseLock.release();
87             mCameraDevice = cameraDevice;
88             createCameraPreviewSession();
89         }
90 
91         @Override
92         public void onDisconnected(CameraDevice cameraDevice) {
93             mCameraOpenCloseLock.release();
94             cameraDevice.close();
95             mCameraDevice = null;
96         }
97 
98         @Override
99         public void onError(CameraDevice cameraDevice, int error) {
100             mCameraOpenCloseLock.release();
101             cameraDevice.close();
102             mCameraDevice = null;
103             Log.e(TAG, "onError " + error);
104         }
105 
106     };
107 
108     /**
109      * An additional thread for running tasks that shouldn't block the UI.
110      */
111     private HandlerThread mBackgroundThread;
112 
113     /**
114      * A {@link Handler} for running tasks in the background.
115      */
116     private Handler mBackgroundHandler;
117 
118     /**
119      * {@link CaptureRequest.Builder} for the camera preview
120      */
121     private CaptureRequest.Builder mPreviewRequestBuilder;
122 
123     /**
124      * {@link CaptureRequest} generated by {@link #mPreviewRequestBuilder}
125      */
126     private CaptureRequest mPreviewRequest;
127 
128     /**
129      * A {@link Semaphore} to prevent the app from exiting before closing the camera.
130      */
131     private Semaphore mCameraOpenCloseLock = new Semaphore(1);
132 
133     /**
134      * A {@link CameraCaptureSession.CaptureCallback} that handles events related to JPEG capture.
135      */
136     /**
137      * A {@link CameraCaptureSession.CaptureCallback} that handles events related to JPEG capture.
138      */
139     private CameraCaptureSession.CaptureCallback mCaptureCallback
140             = new CameraCaptureSession.CaptureCallback() {
141 
142         @Override
143         public void onCaptureProgressed(CameraCaptureSession session,
144                                         CaptureRequest request,
145                                         CaptureResult partialResult) {
146             // Don't need to do anything here.
147         }
148 
149         @Override
150         public void onCaptureCompleted(CameraCaptureSession session,
151                                        CaptureRequest request,
152                                        TotalCaptureResult result) {
153             // Don't need to do anything here.
154         }
155 
156     };
157 
CameraStreamManager(Context context, SurfaceTexture textureToStreamTo, int width, int height)158     public CameraStreamManager(Context context, SurfaceTexture textureToStreamTo,
159                                int width, int height) {
160         mContext = context;
161         mSurfaceTextureToStreamTo = textureToStreamTo;
162         mWidth = width;
163         mHeight = height;
164     }
165 
onStartCameraStream()166     public void onStartCameraStream() {
167         startBackgroundThread();
168         openCamera(mWidth, mHeight);
169     }
170 
onStopCameraStream()171     public void onStopCameraStream() {
172         closeCamera();
173         stopBackgroundThread();
174     }
175 
176     /**
177      * Sets up member variables related to camera.
178      *
179      * @param width  The width of available size for camera preview
180      * @param height The height of available size for camera preview
181      */
setUpCameraOutputs(int width, int height)182     private void setUpCameraOutputs(int width, int height) {
183         // Danger, W.R.! Attempting to use too large a preview size could  exceed the camera
184         // bus' bandwidth limitation, resulting in gorgeous previews but the storage of
185         // garbage capture data.
186         mPreviewSize = new Size(width, height);
187     }
188 
189     /**
190      * Opens the camera specified by {@link CameraStreamManager#CAMERA_ID}.
191      */
openCamera(int width, int height)192     private void openCamera(int width, int height) {
193         setUpCameraOutputs(width, height);
194         CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
195         try {
196             if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
197                 throw new RuntimeException("Time out waiting to lock camera opening.");
198             }
199             manager.openCamera(CAMERA_ID, mStateCallback, mBackgroundHandler);
200         } catch (CameraAccessException e) {
201             e.printStackTrace();
202         } catch (InterruptedException e) {
203             throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
204         }
205     }
206 
207     /**
208      * Closes the current {@link CameraDevice}.
209      */
closeCamera()210     private void closeCamera() {
211         try {
212             mCameraOpenCloseLock.acquire();
213             if (null != mCaptureSession) {
214                 mCaptureSession.close();
215                 mCaptureSession = null;
216             }
217             if (null != mCameraDevice) {
218                 mCameraDevice.close();
219                 mCameraDevice = null;
220             }
221         } catch (InterruptedException e) {
222             throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
223         } finally {
224             mCameraOpenCloseLock.release();
225         }
226     }
227 
228     /**
229      * Starts a background thread and its {@link Handler}.
230      */
startBackgroundThread()231     private void startBackgroundThread() {
232         mBackgroundThread = new HandlerThread("CameraBackground");
233         mBackgroundThread.start();
234         mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
235     }
236 
237     /**
238      * Stops the background thread and its {@link Handler}.
239      */
stopBackgroundThread()240     private void stopBackgroundThread() {
241         if (mBackgroundThread != null) {
242             mBackgroundThread.quitSafely();
243             try {
244                 mBackgroundThread.join();
245                 mBackgroundThread = null;
246                 mBackgroundHandler = null;
247             } catch (InterruptedException e) {
248                 e.printStackTrace();
249             }
250         }
251     }
252 
253     /**
254      * Creates a new {@link CameraCaptureSession} for camera preview.
255      */
createCameraPreviewSession()256     private void createCameraPreviewSession() {
257         try {
258             assert mSurfaceTextureToStreamTo != null;
259 
260             // We configure the size of default buffer to be the size of camera preview we want.
261             mSurfaceTextureToStreamTo.setDefaultBufferSize(mPreviewSize.getWidth(),
262                     mPreviewSize.getHeight());
263 
264             // This is the output Surface we need to start preview.
265             Surface surface = new Surface(mSurfaceTextureToStreamTo);
266 
267             // We set up a CaptureRequest.Builder with the output Surface.
268             mPreviewRequestBuilder
269                     = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
270             mPreviewRequestBuilder.addTarget(surface);
271 
272             // Here, we create a CameraCaptureSession for camera preview.
273             mCameraDevice.createCaptureSession(Arrays.asList(surface),
274                     new CameraCaptureSession.StateCallback() {
275 
276                         @Override
277                         public void onConfigured(CameraCaptureSession cameraCaptureSession) {
278                             // The camera is already closed.
279                             if (null == mCameraDevice) {
280                                 return;
281                             }
282 
283                             // When the session is ready, we start displaying the preview.
284                             mCaptureSession = cameraCaptureSession;
285                             try {
286                                 // Auto focus should be continuous for camera preview.
287                                 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
288                                         CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
289 
290                                 // Finally, we start displaying the camera preview.
291                                 mPreviewRequest = mPreviewRequestBuilder.build();
292                                 mCaptureSession.setRepeatingRequest(mPreviewRequest,
293                                         mCaptureCallback, mBackgroundHandler);
294                             } catch (CameraAccessException e) {
295                                 e.printStackTrace();
296                             }
297                         }
298 
299                         @Override
300                         public void onConfigureFailed(
301                                 CameraCaptureSession cameraCaptureSession) {
302                             Log.e(TAG, "Camera configuration failed.");
303                         }
304                     }, null
305             );
306         } catch (CameraAccessException e) {
307             e.printStackTrace();
308         }
309     }
310 
getRotation(Context context, int deviceRotation)311     public static int getRotation(Context context, int deviceRotation) {
312         // Get offset from the RGB camera.
313         CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
314         CameraCharacteristics characteristics = null;
315         try {
316             characteristics = manager.getCameraCharacteristics(CAMERA_ID + "");
317         } catch (CameraAccessException e) {
318             e.printStackTrace();
319         }
320         int toOrientate = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
321 
322         // Add RGB offset to current device rotation.
323         return toOrientate + deviceRotation;
324     }
325 }
326