Merge pull request #800 from asmorkalov:android_cam_idx_semantic
[profile/ivi/opencv.git] / modules / java / generator / src / java / android+JavaCameraView.java
1 package org.opencv.android;
2
3 import java.util.List;
4
5 import android.content.Context;
6 import android.graphics.ImageFormat;
7 import android.graphics.SurfaceTexture;
8 import android.hardware.Camera;
9 import android.hardware.Camera.CameraInfo;
10 import android.hardware.Camera.PreviewCallback;
11 import android.os.Build;
12 import android.util.AttributeSet;
13 import android.util.Log;
14 import android.view.ViewGroup.LayoutParams;
15
16 import org.opencv.core.CvType;
17 import org.opencv.core.Mat;
18 import org.opencv.core.Size;
19 import org.opencv.imgproc.Imgproc;
20
21 /**
22  * This class is an implementation of the Bridge View between OpenCV and Java Camera.
23  * This class relays on the functionality available in base class and only implements
24  * required functions:
25  * connectCamera - opens Java camera and sets the PreviewCallback to be delivered.
26  * disconnectCamera - closes the camera and stops preview.
27  * When frame is delivered via callback from Camera - it processed via OpenCV to be
28  * converted to RGBA32 and then passed to the external callback for modifications if required.
29  */
30 public class JavaCameraView extends CameraBridgeViewBase implements PreviewCallback {
31
32     private static final int MAGIC_TEXTURE_ID = 10;
33     private static final String TAG = "JavaCameraView";
34
35     private byte mBuffer[];
36     private Mat[] mFrameChain;
37     private int mChainIdx = 0;
38     private Thread mThread;
39     private boolean mStopThread;
40
41     protected Camera mCamera;
42     protected JavaCameraFrame mCameraFrame;
43     private SurfaceTexture mSurfaceTexture;
44
45     public static class JavaCameraSizeAccessor implements ListItemAccessor {
46
47         public int getWidth(Object obj) {
48             Camera.Size size = (Camera.Size) obj;
49             return size.width;
50         }
51
52         public int getHeight(Object obj) {
53             Camera.Size size = (Camera.Size) obj;
54             return size.height;
55         }
56     }
57
58     public JavaCameraView(Context context, int cameraId) {
59         super(context, cameraId);
60     }
61
62     public JavaCameraView(Context context, AttributeSet attrs) {
63         super(context, attrs);
64     }
65
66     protected boolean initializeCamera(int width, int height) {
67         Log.d(TAG, "Initialize java camera");
68         boolean result = true;
69         synchronized (this) {
70             mCamera = null;
71
72             if (mCameraIndex == CAMERA_ID_ANY) {
73                 Log.d(TAG, "Trying to open camera with old open()");
74                 try {
75                     mCamera = Camera.open();
76                 }
77                 catch (Exception e){
78                     Log.e(TAG, "Camera is not available (in use or does not exist): " + e.getLocalizedMessage());
79                 }
80
81                 if(mCamera == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
82                     boolean connected = false;
83                     for (int camIdx = 0; camIdx < Camera.getNumberOfCameras(); ++camIdx) {
84                         Log.d(TAG, "Trying to open camera with new open(" + Integer.valueOf(camIdx) + ")");
85                         try {
86                             mCamera = Camera.open(camIdx);
87                             connected = true;
88                         } catch (RuntimeException e) {
89                             Log.e(TAG, "Camera #" + camIdx + "failed to open: " + e.getLocalizedMessage());
90                         }
91                         if (connected) break;
92                     }
93                 }
94             } else {
95                 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
96                     int localCameraIndex = mCameraIndex;
97                     if (mCameraIndex == CAMERA_ID_BACK) {
98                         Log.i(TAG, "Trying to open back camera");
99                         Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
100                         for (int camIdx = 0; camIdx < Camera.getNumberOfCameras(); ++camIdx) {
101                             Camera.getCameraInfo( camIdx, cameraInfo );
102                             if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
103                                 localCameraIndex = camIdx;
104                                 break;
105                             }
106                         }
107                     } else if (mCameraIndex == CAMERA_ID_FRONT) {
108                         Log.i(TAG, "Trying to open front camera");
109                         Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
110                         for (int camIdx = 0; camIdx < Camera.getNumberOfCameras(); ++camIdx) {
111                             Camera.getCameraInfo( camIdx, cameraInfo );
112                             if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
113                                 localCameraIndex = camIdx;
114                                 break;
115                             }
116                         }
117                     }
118                     if (localCameraIndex == CAMERA_ID_BACK) {
119                         Log.e(TAG, "Back camera not found!");
120                     } else if (localCameraIndex == CAMERA_ID_FRONT) {
121                         Log.e(TAG, "Front camera not found!");
122                     } else {
123                         Log.d(TAG, "Trying to open camera with new open(" + Integer.valueOf(localCameraIndex) + ")");
124                         try {
125                             mCamera = Camera.open(localCameraIndex);
126                         } catch (RuntimeException e) {
127                             Log.e(TAG, "Camera #" + localCameraIndex + "failed to open: " + e.getLocalizedMessage());
128                         }
129                     }
130                 }
131             }
132
133             if (mCamera == null)
134                 return false;
135
136             /* Now set camera parameters */
137             try {
138                 Camera.Parameters params = mCamera.getParameters();
139                 Log.d(TAG, "getSupportedPreviewSizes()");
140                 List<android.hardware.Camera.Size> sizes = params.getSupportedPreviewSizes();
141
142                 if (sizes != null) {
143                     /* Select the size that fits surface considering maximum size allowed */
144                     Size frameSize = calculateCameraFrameSize(sizes, new JavaCameraSizeAccessor(), width, height);
145
146                     params.setPreviewFormat(ImageFormat.NV21);
147                     Log.d(TAG, "Set preview size to " + Integer.valueOf((int)frameSize.width) + "x" + Integer.valueOf((int)frameSize.height));
148                     params.setPreviewSize((int)frameSize.width, (int)frameSize.height);
149
150                     List<String> FocusModes = params.getSupportedFocusModes();
151                     if (FocusModes != null && FocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO))
152                     {
153                         params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
154                     }
155
156                     mCamera.setParameters(params);
157                     params = mCamera.getParameters();
158
159                     mFrameWidth = params.getPreviewSize().width;
160                     mFrameHeight = params.getPreviewSize().height;
161
162                     if ((getLayoutParams().width == LayoutParams.MATCH_PARENT) && (getLayoutParams().height == LayoutParams.MATCH_PARENT))
163                         mScale = Math.min(((float)height)/mFrameHeight, ((float)width)/mFrameWidth);
164                     else
165                         mScale = 0;
166
167                     if (mFpsMeter != null) {
168                         mFpsMeter.setResolution(mFrameWidth, mFrameHeight);
169                     }
170
171                     int size = mFrameWidth * mFrameHeight;
172                     size  = size * ImageFormat.getBitsPerPixel(params.getPreviewFormat()) / 8;
173                     mBuffer = new byte[size];
174
175                     mCamera.addCallbackBuffer(mBuffer);
176                     mCamera.setPreviewCallbackWithBuffer(this);
177
178                     mFrameChain = new Mat[2];
179                     mFrameChain[0] = new Mat(mFrameHeight + (mFrameHeight/2), mFrameWidth, CvType.CV_8UC1);
180                     mFrameChain[1] = new Mat(mFrameHeight + (mFrameHeight/2), mFrameWidth, CvType.CV_8UC1);
181
182                     AllocateCache();
183
184                     mCameraFrame = new JavaCameraFrame(mFrameChain[mChainIdx], mFrameWidth, mFrameHeight);
185
186                     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
187                         mSurfaceTexture = new SurfaceTexture(MAGIC_TEXTURE_ID);
188                         mCamera.setPreviewTexture(mSurfaceTexture);
189                     } else
190                        mCamera.setPreviewDisplay(null);
191
192                     /* Finally we are ready to start the preview */
193                     Log.d(TAG, "startPreview");
194                     mCamera.startPreview();
195                 }
196                 else
197                     result = false;
198             } catch (Exception e) {
199                 result = false;
200                 e.printStackTrace();
201             }
202         }
203
204         return result;
205     }
206
207     protected void releaseCamera() {
208         synchronized (this) {
209             if (mCamera != null) {
210                 mCamera.stopPreview();
211                 mCamera.release();
212             }
213             mCamera = null;
214             if (mFrameChain != null) {
215                 mFrameChain[0].release();
216                 mFrameChain[1].release();
217             }
218             if (mCameraFrame != null)
219                 mCameraFrame.release();
220         }
221     }
222
223     @Override
224     protected boolean connectCamera(int width, int height) {
225
226         /* 1. We need to instantiate camera
227          * 2. We need to start thread which will be getting frames
228          */
229         /* First step - initialize camera connection */
230         Log.d(TAG, "Connecting to camera");
231         if (!initializeCamera(width, height))
232             return false;
233
234         /* now we can start update thread */
235         Log.d(TAG, "Starting processing thread");
236         mStopThread = false;
237         mThread = new Thread(new CameraWorker());
238         mThread.start();
239
240         return true;
241     }
242
243     protected void disconnectCamera() {
244         /* 1. We need to stop thread which updating the frames
245          * 2. Stop camera and release it
246          */
247         Log.d(TAG, "Disconnecting from camera");
248         try {
249             mStopThread = true;
250             Log.d(TAG, "Notify thread");
251             synchronized (this) {
252                 this.notify();
253             }
254             Log.d(TAG, "Wating for thread");
255             if (mThread != null)
256                 mThread.join();
257         } catch (InterruptedException e) {
258             e.printStackTrace();
259         } finally {
260             mThread =  null;
261         }
262
263         /* Now release camera */
264         releaseCamera();
265     }
266
267     public void onPreviewFrame(byte[] frame, Camera arg1) {
268         Log.d(TAG, "Preview Frame received. Frame size: " + frame.length);
269         synchronized (this) {
270             mFrameChain[1 - mChainIdx].put(0, 0, frame);
271             this.notify();
272         }
273         if (mCamera != null)
274             mCamera.addCallbackBuffer(mBuffer);
275     }
276
277     private class JavaCameraFrame implements CvCameraViewFrame {
278         public Mat gray() {
279             return mYuvFrameData.submat(0, mHeight, 0, mWidth);
280         }
281
282         public Mat rgba() {
283             Imgproc.cvtColor(mYuvFrameData, mRgba, Imgproc.COLOR_YUV2BGR_NV12, 4);
284             return mRgba;
285         }
286
287         public JavaCameraFrame(Mat Yuv420sp, int width, int height) {
288             super();
289             mWidth = width;
290             mHeight = height;
291             mYuvFrameData = Yuv420sp;
292             mRgba = new Mat();
293         }
294
295         public void release() {
296             mRgba.release();
297         }
298
299         private Mat mYuvFrameData;
300         private Mat mRgba;
301         private int mWidth;
302         private int mHeight;
303     };
304
305     private class CameraWorker implements Runnable {
306
307         public void run() {
308             do {
309                 synchronized (JavaCameraView.this) {
310                     try {
311                         JavaCameraView.this.wait();
312                     } catch (InterruptedException e) {
313                         // TODO Auto-generated catch block
314                         e.printStackTrace();
315                     }
316                 }
317
318                 if (!mStopThread) {
319                     if (!mFrameChain[mChainIdx].empty())
320                         deliverAndDrawFrame(mCameraFrame);
321                     mChainIdx = 1 - mChainIdx;
322                 }
323             } while (!mStopThread);
324             Log.d(TAG, "Finish processing thread");
325         }
326     }
327 }