1/*
2 * Copyright (C) 2013 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 */
16package com.android.rs.livepreview;
17
18//import com.android.cts.verifier.PassFailButtons;
19//import com.android.cts.verifier.R;
20
21import android.app.Activity;
22import android.app.AlertDialog;
23import android.graphics.Bitmap;
24import android.graphics.Color;
25import android.graphics.ColorMatrix;
26import android.graphics.ColorMatrixColorFilter;
27import android.graphics.ImageFormat;
28import android.graphics.Matrix;
29import android.graphics.SurfaceTexture;
30import android.hardware.Camera;
31import android.os.AsyncTask;
32import android.os.Bundle;
33import android.os.Handler;
34import android.util.Log;
35import android.util.SparseArray;
36import android.view.View;
37import android.view.TextureView;
38import android.widget.AdapterView;
39import android.widget.ArrayAdapter;
40import android.widget.ImageView;
41import android.widget.Spinner;
42
43import java.io.IOException;
44import java.lang.InterruptedException;
45import java.lang.Math;
46import java.lang.Thread;
47import java.util.ArrayList;
48import java.util.Comparator;
49import java.util.List;
50import java.util.TreeSet;
51
52import android.renderscript.*;
53
54/**
55 * Tests for manual verification of the CDD-required camera output formats
56 * for preview callbacks
57 */
58public class CameraPreviewActivity extends Activity
59        implements TextureView.SurfaceTextureListener, Camera.PreviewCallback {
60
61    private static final String TAG = "CameraFormats";
62
63    private TextureView mPreviewView;
64    private SurfaceTexture mPreviewTexture;
65    private int mPreviewTexWidth;
66    private int mPreviewTexHeight;
67
68    //private TextureView mFormatView;
69
70    private Spinner mCameraSpinner;
71    private Spinner mResolutionSpinner;
72
73    private int mCurrentCameraId = -1;
74    private Camera mCamera;
75
76    private List<Camera.Size> mPreviewSizes;
77    private Camera.Size mNextPreviewSize;
78    private Camera.Size mPreviewSize;
79
80    private TextureView mOutputView;
81    //private Bitmap mCallbackBitmap;
82
83    private static final int STATE_OFF = 0;
84    private static final int STATE_PREVIEW = 1;
85    private static final int STATE_NO_CALLBACKS = 2;
86    private int mState = STATE_OFF;
87    private boolean mProcessInProgress = false;
88    private boolean mProcessingFirstFrame = false;
89
90
91    private RenderScript mRS;
92    private RsYuv mFilterYuv;
93
94    @Override
95    public void onCreate(Bundle savedInstanceState) {
96        super.onCreate(savedInstanceState);
97
98        setContentView(R.layout.cf_main);
99
100        mPreviewView = (TextureView) findViewById(R.id.preview_view);
101        mOutputView = (TextureView) findViewById(R.id.format_view);
102
103        mPreviewView.setSurfaceTextureListener(this);
104
105        int numCameras = Camera.getNumberOfCameras();
106        String[] cameraNames = new String[numCameras];
107        for (int i = 0; i < numCameras; i++) {
108            cameraNames[i] = "Camera " + i;
109        }
110        mCameraSpinner = (Spinner) findViewById(R.id.cameras_selection);
111        mCameraSpinner.setAdapter(
112            new ArrayAdapter<String>(
113                this, R.layout.cf_format_list_item, cameraNames));
114        mCameraSpinner.setOnItemSelectedListener(mCameraSpinnerListener);
115
116        mResolutionSpinner = (Spinner) findViewById(R.id.resolution_selection);
117        mResolutionSpinner.setOnItemSelectedListener(mResolutionSelectedListener);
118
119        mRS = RenderScript.create(this);
120        mFilterYuv = new RsYuv(mRS);
121        mOutputView.setSurfaceTextureListener(mFilterYuv);
122    }
123
124    @Override
125    public void onResume() {
126        super.onResume();
127
128        setUpCamera(mCameraSpinner.getSelectedItemPosition());
129    }
130
131    @Override
132    public void onPause() {
133        super.onPause();
134
135        shutdownCamera();
136    }
137
138    public void onSurfaceTextureAvailable(SurfaceTexture surface,
139            int width, int height) {
140        mPreviewTexture = surface;
141        mPreviewTexWidth = width;
142        mPreviewTexHeight = height;
143        if (mCamera != null) {
144            startPreview();
145        }
146    }
147
148    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
149        // Ignored, Camera does all the work for us
150    }
151
152    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
153        return true;
154    }
155
156    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
157        // Invoked every time there's a new Camera preview frame
158    }
159
160    private AdapterView.OnItemSelectedListener mCameraSpinnerListener =
161            new AdapterView.OnItemSelectedListener() {
162                public void onItemSelected(AdapterView<?> parent,
163                        View view, int pos, long id) {
164                    if (mCurrentCameraId != pos) {
165                        setUpCamera(pos);
166                    }
167                }
168
169                public void onNothingSelected(AdapterView parent) {
170
171                }
172
173            };
174
175    private AdapterView.OnItemSelectedListener mResolutionSelectedListener =
176            new AdapterView.OnItemSelectedListener() {
177                public void onItemSelected(AdapterView<?> parent,
178                        View view, int position, long id) {
179                    if (mPreviewSizes.get(position) != mPreviewSize) {
180                        mNextPreviewSize = mPreviewSizes.get(position);
181                        startPreview();
182                    }
183                }
184
185                public void onNothingSelected(AdapterView parent) {
186
187                }
188
189            };
190
191
192    private void setUpCamera(int id) {
193        shutdownCamera();
194
195        mCurrentCameraId = id;
196        mCamera = Camera.open(id);
197        Camera.Parameters p = mCamera.getParameters();
198
199        // Get preview resolutions
200
201        List<Camera.Size> unsortedSizes = p.getSupportedPreviewSizes();
202
203        class SizeCompare implements Comparator<Camera.Size> {
204            public int compare(Camera.Size lhs, Camera.Size rhs) {
205                if (lhs.width < rhs.width) return -1;
206                if (lhs.width > rhs.width) return 1;
207                if (lhs.height < rhs.height) return -1;
208                if (lhs.height > rhs.height) return 1;
209                return 0;
210            }
211        };
212
213        SizeCompare s = new SizeCompare();
214        TreeSet<Camera.Size> sortedResolutions = new TreeSet<Camera.Size>(s);
215        sortedResolutions.addAll(unsortedSizes);
216
217        mPreviewSizes = new ArrayList<Camera.Size>(sortedResolutions);
218
219        String[] availableSizeNames = new String[mPreviewSizes.size()];
220        for (int i = 0; i < mPreviewSizes.size(); i++) {
221            availableSizeNames[i] =
222                    Integer.toString(mPreviewSizes.get(i).width) + " x " +
223                    Integer.toString(mPreviewSizes.get(i).height);
224        }
225        mResolutionSpinner.setAdapter(
226            new ArrayAdapter<String>(
227                this, R.layout.cf_format_list_item, availableSizeNames));
228
229
230        // Set initial values
231	//
232        int initialSize = mPreviewSizes.size() - 1;
233
234	mNextPreviewSize = mPreviewSizes.get(initialSize);
235        mResolutionSpinner.setSelection(initialSize);
236
237	if(mPreviewTexture != null)
238	{
239            startPreview();
240        }
241    }
242
243    private void shutdownCamera() {
244        if (mCamera != null) {
245            mCamera.setPreviewCallbackWithBuffer(null);
246            mCamera.stopPreview();
247            mCamera.release();
248            mCamera = null;
249            mState = STATE_OFF;
250        }
251    }
252
253    private void startPreview() {
254        if (mState != STATE_OFF) {
255            // Stop for a while to drain callbacks
256            mCamera.setPreviewCallbackWithBuffer(null);
257            mCamera.stopPreview();
258            mState = STATE_OFF;
259            Handler h = new Handler();
260            Runnable mDelayedPreview = new Runnable() {
261                public void run() {
262                    startPreview();
263                }
264            };
265            h.postDelayed(mDelayedPreview, 300);
266            return;
267        }
268        mState = STATE_PREVIEW;
269
270        Matrix transform = new Matrix();
271        float widthRatio = mNextPreviewSize.width / (float)mPreviewTexWidth;
272        float heightRatio = mNextPreviewSize.height / (float)mPreviewTexHeight;
273
274        transform.setScale(1, heightRatio/widthRatio);
275        transform.postTranslate(0,
276                mPreviewTexHeight * (1 - heightRatio/widthRatio)/2);
277
278        mPreviewView.setTransform(transform);
279        mOutputView.setTransform(transform);
280
281        mPreviewSize   = mNextPreviewSize;
282
283        Camera.Parameters p = mCamera.getParameters();
284        p.setPreviewFormat(ImageFormat.NV21);
285        p.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
286        mCamera.setParameters(p);
287
288        mCamera.setPreviewCallbackWithBuffer(this);
289        int expectedBytes = mPreviewSize.width * mPreviewSize.height *
290                ImageFormat.getBitsPerPixel(ImageFormat.NV21) / 8;
291        for (int i=0; i < 4; i++) {
292            mCamera.addCallbackBuffer(new byte[expectedBytes]);
293        }
294        //mFormatView.setColorFilter(mYuv2RgbFilter);
295
296        mProcessingFirstFrame = true;
297        try {
298            mCamera.setPreviewTexture(mPreviewTexture);
299            mCamera.startPreview();
300        } catch (IOException ioe) {
301            // Something bad happened
302            Log.e(TAG, "Unable to start up preview");
303        }
304
305    }
306
307
308    private class ProcessPreviewDataTask extends AsyncTask<byte[], Void, Boolean> {
309        protected Boolean doInBackground(byte[]... datas) {
310            byte[] data = datas[0];
311
312            long t1 = java.lang.System.currentTimeMillis();
313
314            mFilterYuv.execute(data);
315
316            long t2 = java.lang.System.currentTimeMillis();
317            mTiming[mTimingSlot++] = t2 - t1;
318            if (mTimingSlot >= mTiming.length) {
319                float total = 0;
320                for (int i=0; i<mTiming.length; i++) {
321                    total += (float)mTiming[i];
322                }
323                total /= mTiming.length;
324                Log.e(TAG, "time + " + total);
325                mTimingSlot = 0;
326            }
327
328            mCamera.addCallbackBuffer(data);
329            mProcessInProgress = false;
330            return true;
331        }
332
333        protected void onPostExecute(Boolean result) {
334            mOutputView.invalidate();
335        }
336
337    }
338
339    private long mTiming[] = new long[50];
340    private int mTimingSlot = 0;
341
342    public void onPreviewFrame(byte[] data, Camera camera) {
343        if (mProcessInProgress || mState != STATE_PREVIEW) {
344            mCamera.addCallbackBuffer(data);
345            return;
346        }
347        if (data == null) {
348            return;
349        }
350
351        int expectedBytes = mPreviewSize.width * mPreviewSize.height *
352                ImageFormat.getBitsPerPixel(ImageFormat.NV21) / 8;
353
354        if (expectedBytes != data.length) {
355            Log.e(TAG, "Mismatched size of buffer! Expected ");
356
357            mState = STATE_NO_CALLBACKS;
358            mCamera.setPreviewCallbackWithBuffer(null);
359            return;
360        }
361
362        mProcessInProgress = true;
363
364        if ((mFilterYuv == null) ||
365            (mPreviewSize.width != mFilterYuv.getWidth()) ||
366            (mPreviewSize.height != mFilterYuv.getHeight()) ) {
367
368            mFilterYuv.reset(mPreviewSize.width, mPreviewSize.height);
369        }
370
371        mProcessInProgress = true;
372        new ProcessPreviewDataTask().execute(data);
373    }
374
375
376
377}
378