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