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