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