Home | History | Annotate | Download | only in livepreview
      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  */
     16 package com.android.rs.livepreview;
     17 
     18 //import com.android.cts.verifier.PassFailButtons;
     19 //import com.android.cts.verifier.R;
     20 
     21 import android.app.Activity;
     22 import android.app.AlertDialog;
     23 import android.graphics.Bitmap;
     24 import android.graphics.Color;
     25 import android.graphics.ColorMatrix;
     26 import android.graphics.ColorMatrixColorFilter;
     27 import android.graphics.ImageFormat;
     28 import android.graphics.Matrix;
     29 import android.graphics.SurfaceTexture;
     30 import android.hardware.Camera;
     31 import android.os.AsyncTask;
     32 import android.os.Bundle;
     33 import android.os.Handler;
     34 import android.util.Log;
     35 import android.util.SparseArray;
     36 import android.view.View;
     37 import android.view.TextureView;
     38 import android.widget.AdapterView;
     39 import android.widget.ArrayAdapter;
     40 import android.widget.ImageView;
     41 import android.widget.Spinner;
     42 
     43 import java.io.IOException;
     44 import java.lang.InterruptedException;
     45 import java.lang.Math;
     46 import java.lang.Thread;
     47 import java.util.ArrayList;
     48 import java.util.Comparator;
     49 import java.util.List;
     50 import java.util.TreeSet;
     51 
     52 import android.renderscript.*;
     53 
     54 /**
     55  * Tests for manual verification of the CDD-required camera output formats
     56  * for preview callbacks
     57  */
     58 public 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 = findViewById(R.id.preview_view);
    101         mOutputView = 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 = 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 = 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         mPreviewTexture = null;
    154         return true;
    155     }
    156 
    157     public void onSurfaceTextureUpdated(SurfaceTexture surface) {
    158         // Invoked every time there's a new Camera preview frame
    159     }
    160 
    161     private AdapterView.OnItemSelectedListener mCameraSpinnerListener =
    162             new AdapterView.OnItemSelectedListener() {
    163                 public void onItemSelected(AdapterView<?> parent,
    164                         View view, int pos, long id) {
    165                     if (mCurrentCameraId != pos) {
    166                         setUpCamera(pos);
    167                     }
    168                 }
    169 
    170                 public void onNothingSelected(AdapterView parent) {
    171 
    172                 }
    173 
    174             };
    175 
    176     private AdapterView.OnItemSelectedListener mResolutionSelectedListener =
    177             new AdapterView.OnItemSelectedListener() {
    178                 public void onItemSelected(AdapterView<?> parent,
    179                         View view, int position, long id) {
    180                     if (mPreviewSizes.get(position) != mPreviewSize) {
    181                         mNextPreviewSize = mPreviewSizes.get(position);
    182                         startPreview();
    183                     }
    184                 }
    185 
    186                 public void onNothingSelected(AdapterView parent) {
    187 
    188                 }
    189 
    190             };
    191 
    192 
    193     private void setUpCamera(int id) {
    194         shutdownCamera();
    195 
    196         mCurrentCameraId = id;
    197         mCamera = Camera.open(id);
    198         Camera.Parameters p = mCamera.getParameters();
    199 
    200         // Get preview resolutions
    201 
    202         List<Camera.Size> unsortedSizes = p.getSupportedPreviewSizes();
    203 
    204         class SizeCompare implements Comparator<Camera.Size> {
    205             public int compare(Camera.Size lhs, Camera.Size rhs) {
    206                 if (lhs.width < rhs.width) return -1;
    207                 if (lhs.width > rhs.width) return 1;
    208                 if (lhs.height < rhs.height) return -1;
    209                 if (lhs.height > rhs.height) return 1;
    210                 return 0;
    211             }
    212         };
    213 
    214         SizeCompare s = new SizeCompare();
    215         TreeSet<Camera.Size> sortedResolutions = new TreeSet<Camera.Size>(s);
    216         sortedResolutions.addAll(unsortedSizes);
    217 
    218         mPreviewSizes = new ArrayList<Camera.Size>(sortedResolutions);
    219 
    220         String[] availableSizeNames = new String[mPreviewSizes.size()];
    221         for (int i = 0; i < mPreviewSizes.size(); i++) {
    222             availableSizeNames[i] =
    223                     Integer.toString(mPreviewSizes.get(i).width) + " x " +
    224                     Integer.toString(mPreviewSizes.get(i).height);
    225         }
    226         mResolutionSpinner.setAdapter(
    227             new ArrayAdapter<String>(
    228                 this, R.layout.cf_format_list_item, availableSizeNames));
    229 
    230 
    231         // Set initial values
    232 	//
    233         int initialSize = mPreviewSizes.size() - 1;
    234 
    235 	mNextPreviewSize = mPreviewSizes.get(initialSize);
    236         mResolutionSpinner.setSelection(initialSize);
    237 
    238 	if(mPreviewTexture != null)
    239 	{
    240             startPreview();
    241         }
    242     }
    243 
    244     private void shutdownCamera() {
    245         if (mCamera != null) {
    246             mCamera.setPreviewCallbackWithBuffer(null);
    247             mCamera.stopPreview();
    248             mCamera.release();
    249             mCamera = null;
    250             mState = STATE_OFF;
    251         }
    252     }
    253 
    254     private void startPreview() {
    255         if (mState != STATE_OFF) {
    256             // Stop for a while to drain callbacks
    257             mCamera.setPreviewCallbackWithBuffer(null);
    258             mCamera.stopPreview();
    259             mState = STATE_OFF;
    260             Handler h = new Handler();
    261             Runnable mDelayedPreview = new Runnable() {
    262                 public void run() {
    263                     startPreview();
    264                 }
    265             };
    266             h.postDelayed(mDelayedPreview, 300);
    267             return;
    268         }
    269         mState = STATE_PREVIEW;
    270 
    271         Matrix transform = new Matrix();
    272         float widthRatio = mNextPreviewSize.width / (float)mPreviewTexWidth;
    273         float heightRatio = mNextPreviewSize.height / (float)mPreviewTexHeight;
    274 
    275         transform.setScale(1, heightRatio/widthRatio);
    276         transform.postTranslate(0,
    277                 mPreviewTexHeight * (1 - heightRatio/widthRatio)/2);
    278 
    279         mPreviewView.setTransform(transform);
    280         mOutputView.setTransform(transform);
    281 
    282         mPreviewSize   = mNextPreviewSize;
    283 
    284         Camera.Parameters p = mCamera.getParameters();
    285         p.setPreviewFormat(ImageFormat.NV21);
    286         p.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
    287         mCamera.setParameters(p);
    288 
    289         mCamera.setPreviewCallbackWithBuffer(this);
    290         int expectedBytes = mPreviewSize.width * mPreviewSize.height *
    291                 ImageFormat.getBitsPerPixel(ImageFormat.NV21) / 8;
    292         for (int i=0; i < 4; i++) {
    293             mCamera.addCallbackBuffer(new byte[expectedBytes]);
    294         }
    295         //mFormatView.setColorFilter(mYuv2RgbFilter);
    296 
    297         mProcessingFirstFrame = true;
    298         try {
    299             mCamera.setPreviewTexture(mPreviewTexture);
    300             mCamera.startPreview();
    301         } catch (IOException ioe) {
    302             // Something bad happened
    303             Log.e(TAG, "Unable to start up preview");
    304         }
    305 
    306     }
    307 
    308 
    309     private class ProcessPreviewDataTask extends AsyncTask<byte[], Void, Boolean> {
    310         protected Boolean doInBackground(byte[]... datas) {
    311             byte[] data = datas[0];
    312 
    313             long t1 = java.lang.System.currentTimeMillis();
    314 
    315             mFilterYuv.execute(data);
    316 
    317             long t2 = java.lang.System.currentTimeMillis();
    318             mTiming[mTimingSlot++] = t2 - t1;
    319             if (mTimingSlot >= mTiming.length) {
    320                 float total = 0;
    321                 for (int i=0; i<mTiming.length; i++) {
    322                     total += (float)mTiming[i];
    323                 }
    324                 total /= mTiming.length;
    325                 Log.e(TAG, "time + " + total);
    326                 mTimingSlot = 0;
    327             }
    328 
    329             mCamera.addCallbackBuffer(data);
    330             mProcessInProgress = false;
    331             return true;
    332         }
    333 
    334         protected void onPostExecute(Boolean result) {
    335             mOutputView.invalidate();
    336         }
    337 
    338     }
    339 
    340     private long mTiming[] = new long[50];
    341     private int mTimingSlot = 0;
    342 
    343     public void onPreviewFrame(byte[] data, Camera camera) {
    344         if (mProcessInProgress || mState != STATE_PREVIEW) {
    345             mCamera.addCallbackBuffer(data);
    346             return;
    347         }
    348         if (data == null) {
    349             return;
    350         }
    351 
    352         int expectedBytes = mPreviewSize.width * mPreviewSize.height *
    353                 ImageFormat.getBitsPerPixel(ImageFormat.NV21) / 8;
    354 
    355         if (expectedBytes != data.length) {
    356             Log.e(TAG, "Mismatched size of buffer! Expected ");
    357 
    358             return;
    359         }
    360 
    361         mProcessInProgress = true;
    362 
    363         if ((mFilterYuv == null) ||
    364             (mPreviewSize.width != mFilterYuv.getWidth()) ||
    365             (mPreviewSize.height != mFilterYuv.getHeight()) ) {
    366 
    367             mFilterYuv.reset(mPreviewSize.width, mPreviewSize.height);
    368         }
    369 
    370         mProcessInProgress = true;
    371         new ProcessPreviewDataTask().execute(data);
    372     }
    373 
    374 
    375 
    376 }
    377