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.cts.verifier.camera.video; 17 18 import android.app.AlertDialog; 19 import android.content.DialogInterface; 20 import android.graphics.Matrix; 21 import android.graphics.SurfaceTexture; 22 import android.hardware.Camera; 23 import android.hardware.Camera.CameraInfo; 24 import android.hardware.Camera.Size; 25 import android.media.CamcorderProfile; 26 import android.media.MediaPlayer; 27 import android.media.MediaRecorder; 28 import android.os.Bundle; 29 import android.os.Environment; 30 import android.os.Handler; 31 import android.util.Log; 32 import android.view.Surface; 33 import android.view.TextureView; 34 import android.view.View; 35 import android.widget.AdapterView; 36 import android.widget.ArrayAdapter; 37 import android.widget.Button; 38 import android.widget.ImageButton; 39 import android.widget.Spinner; 40 import android.widget.TextView; 41 import android.widget.VideoView; 42 43 import com.android.cts.verifier.PassFailButtons; 44 import com.android.cts.verifier.R; 45 46 import java.io.File; 47 import java.io.IOException; 48 import java.text.SimpleDateFormat; 49 import java.util.ArrayList; 50 import java.util.Comparator; 51 import java.util.Date; 52 import java.util.List; 53 import java.util.TreeSet; 54 55 56 /** 57 * Tests for manual verification of camera video capture 58 */ 59 public class CameraVideoActivity extends PassFailButtons.Activity 60 implements TextureView.SurfaceTextureListener { 61 62 private static final String TAG = "CtsCameraVideo"; 63 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 64 private static final int MEDIA_TYPE_IMAGE = 1; 65 private static final int MEDIA_TYPE_VIDEO = 2; 66 private static final int VIDEO_LENGTH = 3000; // in ms 67 68 private TextureView mPreviewView; 69 private SurfaceTexture mPreviewTexture; 70 private int mPreviewTexWidth; 71 private int mPreviewTexHeight; 72 private int mPreviewRotation; 73 private int mVideoRotation; 74 75 private VideoView mPlaybackView; 76 77 private Spinner mCameraSpinner; 78 private Spinner mResolutionSpinner; 79 80 private int mCurrentCameraId = -1; 81 private Camera mCamera; 82 83 private MediaRecorder mMediaRecorder; 84 85 private List<Size> mPreviewSizes; 86 private Size mNextPreviewSize; 87 private Size mPreviewSize; 88 private List<Integer> mVideoSizeIds; 89 private int mCurrentVideoSizeId; 90 91 private boolean isRecording = false; 92 private boolean isPlayingBack = false; 93 private Button captureButton; 94 private ImageButton mPassButton; 95 private ImageButton mFailButton; 96 97 private TextView mStatusLabel; 98 99 private TreeSet<String> mTestedCombinations = new TreeSet<String>(); 100 private TreeSet<String> mUntestedCombinations = new TreeSet<String>(); 101 102 private File outputVideoFile; 103 104 /** 105 * @see #MEDIA_TYPE_IMAGE 106 * @see #MEDIA_TYPE_VIDEO 107 */ getOutputMediaFile(int type)108 private static File getOutputMediaFile(int type) { 109 // Question: why do I need to comment this to get it working? 110 // Logcat says "external storage not ready" 111 // if (Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED) { 112 // Log.e(TAG, "external storage not ready"); 113 // return null; 114 // } 115 116 File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory( 117 Environment.DIRECTORY_MOVIES), TAG); 118 119 if (!mediaStorageDir.exists()) { 120 if (!mediaStorageDir.mkdirs()) { 121 Log.d(TAG, "failed to create directory"); 122 return null; 123 } 124 } 125 126 String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); 127 File mediaFile; 128 if (type == MEDIA_TYPE_IMAGE) { 129 mediaFile = new File(mediaStorageDir.getPath() + File.separator + 130 "IMG_" + timeStamp + ".jpg"); 131 } else if (type == MEDIA_TYPE_VIDEO) { 132 mediaFile = new File(mediaStorageDir.getPath() + File.separator + 133 "VID_" + timeStamp + ".mp4"); 134 if (VERBOSE) { 135 Log.v(TAG, "getOutputMediaFile: output file " + mediaFile.getPath()); 136 } 137 } else { 138 return null; 139 } 140 141 return mediaFile; 142 } 143 prepareVideoRecorder()144 private boolean prepareVideoRecorder() { 145 146 mMediaRecorder = new MediaRecorder(); 147 148 // Step 1: unlock and set camera to MediaRecorder 149 mCamera.unlock(); 150 mMediaRecorder.setCamera(mCamera); 151 152 // Step 2: set sources 153 mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER); 154 mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); 155 156 // Step 3: set a CamcorderProfile 157 mMediaRecorder.setProfile(CamcorderProfile.get(mCurrentCameraId, mCurrentVideoSizeId)); 158 159 // Step 4: set output file 160 outputVideoFile = getOutputMediaFile(MEDIA_TYPE_VIDEO); 161 mMediaRecorder.setOutputFile(outputVideoFile.toString()); 162 163 // Step 5: set preview output 164 // This is not necessary since preview has been taken care of 165 166 // Step 6: set orientation hint 167 mMediaRecorder.setOrientationHint(mVideoRotation); 168 169 // Step 7: prepare configured MediaRecorder 170 try { 171 mMediaRecorder.prepare(); 172 } catch (IOException e) { 173 Log.e(TAG, "IOException preparing MediaRecorder: ", e); 174 releaseMediaRecorder(); 175 throw new AssertionError(e); 176 } 177 178 mMediaRecorder.setOnErrorListener( 179 new MediaRecorder.OnErrorListener() { 180 @Override 181 public void onError(MediaRecorder mr, int what, int extra) { 182 if (what == MediaRecorder.MEDIA_RECORDER_ERROR_UNKNOWN) { 183 Log.e(TAG, "unknown error in media recorder, error: " + extra); 184 } else { 185 Log.e(TAG, "media recorder server died, error: " + extra); 186 } 187 188 failTest("Media recorder error."); 189 } 190 }); 191 192 if (VERBOSE) { 193 Log.v(TAG, "prepareVideoRecorder: prepared configured MediaRecorder"); 194 } 195 196 return true; 197 } 198 199 @Override onCreate(Bundle savedInstanceState)200 public void onCreate(Bundle savedInstanceState) { 201 super.onCreate(savedInstanceState); 202 203 setContentView(R.layout.camera_video); 204 setPassFailButtonClickListeners(); 205 setInfoResources(R.string.camera_video, R.string.video_info, /*viewId*/-1); 206 207 mPreviewView = (TextureView) findViewById(R.id.video_capture); 208 mPlaybackView = (VideoView) findViewById(R.id.video_playback); 209 mPlaybackView.setOnCompletionListener(mPlaybackViewListener); 210 211 captureButton = (Button) findViewById(R.id.record_button); 212 mPassButton = (ImageButton) findViewById(R.id.pass_button); 213 mFailButton = (ImageButton) findViewById(R.id.fail_button); 214 mPassButton.setEnabled(false); 215 mFailButton.setEnabled(true); 216 217 mPreviewView.setSurfaceTextureListener(this); 218 219 int numCameras = Camera.getNumberOfCameras(); 220 String[] cameraNames = new String[numCameras]; 221 for (int i = 0; i < numCameras; i++) { 222 cameraNames[i] = "Camera " + i; 223 mUntestedCombinations.add("All combinations for Camera " + i + "\n"); 224 } 225 if (VERBOSE) { 226 Log.v(TAG, "onCreate: number of cameras=" + numCameras); 227 } 228 mCameraSpinner = (Spinner) findViewById(R.id.cameras_selection); 229 mCameraSpinner.setAdapter( 230 new ArrayAdapter<String>( 231 this, R.layout.cf_format_list_item, cameraNames)); 232 mCameraSpinner.setOnItemSelectedListener(mCameraSpinnerListener); 233 234 mResolutionSpinner = (Spinner) findViewById(R.id.resolution_selection); 235 mResolutionSpinner.setOnItemSelectedListener(mResolutionSelectedListener); 236 237 mStatusLabel = (TextView) findViewById(R.id.status_label); 238 } 239 240 @Override onResume()241 public void onResume() { 242 super.onResume(); 243 244 setUpCamera(mCameraSpinner.getSelectedItemPosition()); 245 if (VERBOSE) { 246 Log.v(TAG, "onResume: camera has been setup"); 247 } 248 249 setUpCaptureButton(); 250 if (VERBOSE) { 251 Log.v(TAG, "onResume: captureButton has been setup"); 252 } 253 254 } 255 256 @Override onPause()257 public void onPause() { 258 super.onPause(); 259 260 releaseMediaRecorder(); 261 shutdownCamera(); 262 mPreviewTexture = null; 263 } 264 265 private MediaPlayer.OnCompletionListener mPlaybackViewListener = 266 new MediaPlayer.OnCompletionListener() { 267 268 @Override 269 public void onCompletion(MediaPlayer mp) { 270 isPlayingBack = false; 271 mPlaybackView.stopPlayback(); 272 captureButton.setEnabled(true); 273 mStatusLabel.setText(getResources().getString(R.string.status_ready)); 274 } 275 276 }; 277 releaseMediaRecorder()278 private void releaseMediaRecorder() { 279 if (mMediaRecorder != null) { 280 mMediaRecorder.reset(); 281 mMediaRecorder.release(); 282 mMediaRecorder = null; 283 mCamera.lock(); // check here, lock camera for later use 284 } 285 } 286 287 @Override getTestDetails()288 public String getTestDetails() { 289 StringBuilder reportBuilder = new StringBuilder(); 290 reportBuilder.append("Tested combinations:\n"); 291 for (String combination : mTestedCombinations) { 292 reportBuilder.append(combination); 293 } 294 reportBuilder.append("Untested combinations:\n"); 295 for (String combination : mUntestedCombinations) { 296 reportBuilder.append(combination); 297 } 298 return reportBuilder.toString(); 299 } 300 301 @Override onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height)302 public void onSurfaceTextureAvailable(SurfaceTexture surface, 303 int width, int height) { 304 mPreviewTexture = surface; 305 mPreviewTexWidth = width; 306 mPreviewTexHeight = height; 307 if (mCamera != null) { 308 startPreview(); 309 } 310 } 311 312 @Override onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height)313 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 314 // Ignored, Camera does all the work for us 315 } 316 317 @Override onSurfaceTextureDestroyed(SurfaceTexture surface)318 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 319 return true; 320 } 321 322 323 @Override onSurfaceTextureUpdated(SurfaceTexture surface)324 public void onSurfaceTextureUpdated(SurfaceTexture surface) { 325 // Invoked every time there's a new Camera preview frame 326 } 327 328 private AdapterView.OnItemSelectedListener mCameraSpinnerListener = 329 new AdapterView.OnItemSelectedListener() { 330 @Override 331 public void onItemSelected(AdapterView<?> parent, 332 View view, int pos, long id) { 333 if (mCurrentCameraId != pos) { 334 setUpCamera(pos); 335 } 336 } 337 338 @Override 339 public void onNothingSelected(AdapterView<?> parent) { 340 // Intentionally left blank 341 } 342 343 }; 344 345 private AdapterView.OnItemSelectedListener mResolutionSelectedListener = 346 new AdapterView.OnItemSelectedListener() { 347 @Override 348 public void onItemSelected(AdapterView<?> parent, 349 View view, int position, long id) { 350 if (mVideoSizeIds.get(position) != mCurrentVideoSizeId) { 351 mCurrentVideoSizeId = mVideoSizeIds.get(position); 352 if (VERBOSE) { 353 Log.v(TAG, "onItemSelected: mCurrentVideoSizeId = " + 354 mCurrentVideoSizeId); 355 } 356 mNextPreviewSize = matchPreviewRecordSize(); 357 if (VERBOSE) { 358 Log.v(TAG, "onItemSelected: setting preview size " 359 + mNextPreviewSize.width + "x" + mNextPreviewSize.height); 360 } 361 362 startPreview(); 363 if (VERBOSE) { 364 Log.v(TAG, "onItemSelected: started new preview"); 365 } 366 } 367 } 368 369 @Override 370 public void onNothingSelected(AdapterView<?> parent) { 371 // Intentionally left blank 372 } 373 374 }; 375 376 setUpCaptureButton()377 private void setUpCaptureButton() { 378 captureButton.setOnClickListener ( 379 new View.OnClickListener() { 380 @Override 381 public void onClick(View V) { 382 if ((!isRecording) && (!isPlayingBack)) { 383 if (prepareVideoRecorder()) { 384 mMediaRecorder.start(); 385 if (VERBOSE) { 386 Log.v(TAG, "onClick: started mMediaRecorder"); 387 } 388 isRecording = true; 389 captureButton.setEnabled(false); 390 mStatusLabel.setText(getResources() 391 .getString(R.string.status_recording)); 392 } else { 393 releaseMediaRecorder(); 394 Log.e(TAG, "media recorder cannot be set up"); 395 failTest("Unable to set up media recorder."); 396 } 397 Handler h = new Handler(); 398 Runnable mDelayedPreview = new Runnable() { 399 @Override 400 public void run() { 401 mMediaRecorder.stop(); 402 releaseMediaRecorder(); 403 404 mPlaybackView.setVideoPath(outputVideoFile.getPath()); 405 mPlaybackView.start(); 406 isRecording = false; 407 isPlayingBack = true; 408 mStatusLabel.setText(getResources() 409 .getString(R.string.status_playback)); 410 String combination = "Camera " + mCurrentCameraId + ", " + 411 mCurrentVideoSizeId + "\n"; 412 mUntestedCombinations.remove(combination); 413 mTestedCombinations.add(combination); 414 415 if (mUntestedCombinations.isEmpty()) { 416 mPassButton.setEnabled(true); 417 if (VERBOSE) { 418 Log.v(TAG, "run: test success"); 419 } 420 } 421 } 422 }; 423 h.postDelayed(mDelayedPreview, VIDEO_LENGTH); 424 } 425 426 } 427 } 428 ); 429 } 430 431 private class VideoSizeNamePair { 432 private int sizeId; 433 private String sizeName; 434 VideoSizeNamePair(int id, String name)435 public VideoSizeNamePair(int id, String name) { 436 sizeId = id; 437 sizeName = name; 438 } 439 getSizeId()440 public int getSizeId() { 441 return sizeId; 442 } 443 getSizeName()444 public String getSizeName() { 445 return sizeName; 446 } 447 } 448 getVideoSizeNamePairs(int cameraId)449 private ArrayList<VideoSizeNamePair> getVideoSizeNamePairs(int cameraId) { 450 int[] qualityArray = { 451 CamcorderProfile.QUALITY_LOW, 452 CamcorderProfile.QUALITY_HIGH, 453 CamcorderProfile.QUALITY_QCIF, 454 CamcorderProfile.QUALITY_QVGA, 455 CamcorderProfile.QUALITY_CIF, 456 CamcorderProfile.QUALITY_480P, 457 CamcorderProfile.QUALITY_720P, 458 CamcorderProfile.QUALITY_1080P, 459 CamcorderProfile.QUALITY_2160P 460 }; 461 462 String[] nameArray = { 463 "LOW", 464 "HIGH", 465 "QCIF", 466 "QVGA", 467 "CIF", 468 "480P", 469 "720P", 470 "1080P", 471 "2160P" 472 }; 473 474 ArrayList<VideoSizeNamePair> availableSizes = 475 new ArrayList<VideoSizeNamePair> (); 476 477 for (int i = 0; i < qualityArray.length; i++) { 478 if (CamcorderProfile.hasProfile(cameraId, qualityArray[i])) { 479 VideoSizeNamePair pair = new VideoSizeNamePair(qualityArray[i], nameArray[i]); 480 availableSizes.add(pair); 481 } 482 } 483 return availableSizes; 484 } 485 486 static class ResolutionQuality { 487 private int videoSizeId; 488 private int width; 489 private int height; 490 ResolutionQuality()491 public ResolutionQuality() { 492 // intentionally left blank 493 } ResolutionQuality(int newSizeId, int newWidth, int newHeight)494 public ResolutionQuality(int newSizeId, int newWidth, int newHeight) { 495 videoSizeId = newSizeId; 496 width = newWidth; 497 height = newHeight; 498 } 499 } 500 findRecordSize(int cameraId)501 private Size findRecordSize(int cameraId) { 502 int[] possibleQuality = { 503 CamcorderProfile.QUALITY_LOW, 504 CamcorderProfile.QUALITY_HIGH, 505 CamcorderProfile.QUALITY_QCIF, 506 CamcorderProfile.QUALITY_QVGA, 507 CamcorderProfile.QUALITY_CIF, 508 CamcorderProfile.QUALITY_480P, 509 CamcorderProfile.QUALITY_720P, 510 CamcorderProfile.QUALITY_1080P, 511 CamcorderProfile.QUALITY_2160P 512 }; 513 514 ArrayList<ResolutionQuality> qualityList = new ArrayList<ResolutionQuality>(); 515 for (int i = 0; i < possibleQuality.length; i++) { 516 if (CamcorderProfile.hasProfile(cameraId, possibleQuality[i])) { 517 CamcorderProfile profile = CamcorderProfile.get(cameraId, possibleQuality[i]); 518 qualityList.add(new ResolutionQuality(possibleQuality[i], 519 profile.videoFrameWidth, profile.videoFrameHeight)); 520 } 521 } 522 523 Size recordSize = null; 524 for (int i = 0; i < qualityList.size(); i++) { 525 if (mCurrentVideoSizeId == qualityList.get(i).videoSizeId) { 526 recordSize = mCamera.new Size(qualityList.get(i).width, 527 qualityList.get(i).height); 528 break; 529 } 530 } 531 532 if (recordSize == null) { 533 Log.e(TAG, "findRecordSize: did not find a match"); 534 failTest("Cannot find video size"); 535 } 536 return recordSize; 537 } 538 539 // Match preview size with current recording size mCurrentVideoSizeId matchPreviewRecordSize()540 private Size matchPreviewRecordSize() { 541 Size recordSize = findRecordSize(mCurrentCameraId); 542 543 Size matchedSize = null; 544 // First try to find exact match in size 545 for (int i = 0; i < mPreviewSizes.size(); i++) { 546 if (mPreviewSizes.get(i).equals(recordSize)) { 547 matchedSize = mCamera.new Size(recordSize.width, recordSize.height); 548 break; 549 } 550 } 551 // Second try to find same ratio in size 552 if (matchedSize == null) { 553 for (int i = mPreviewSizes.size() - 1; i >= 0; i--) { 554 if (mPreviewSizes.get(i).width * recordSize.height == 555 mPreviewSizes.get(i).height * recordSize.width) { 556 matchedSize = mCamera.new Size(mPreviewSizes.get(i).width, 557 mPreviewSizes.get(i).height); 558 break; 559 } 560 } 561 } 562 //Third try to find one with similar if not the same apect ratio 563 if (matchedSize == null) { 564 for (int i = mPreviewSizes.size() - 1; i >= 0; i--) { 565 if (Math.abs((float)mPreviewSizes.get(i).width * recordSize.height / 566 mPreviewSizes.get(i).height / recordSize.width - 1) < 0.12) { 567 matchedSize = mCamera.new Size(mPreviewSizes.get(i).width, 568 mPreviewSizes.get(i).height); 569 break; 570 } 571 } 572 } 573 // Last resort, just use the first preview size 574 if (matchedSize == null) { 575 matchedSize = mCamera.new Size(mPreviewSizes.get(0).width, 576 mPreviewSizes.get(0).height); 577 } 578 579 if (VERBOSE) { 580 Log.v(TAG, "matchPreviewRecordSize " + matchedSize.width + "x" + matchedSize.height); 581 } 582 583 return matchedSize; 584 } 585 setUpCamera(int id)586 private void setUpCamera(int id) { 587 shutdownCamera(); 588 589 mCurrentCameraId = id; 590 try { 591 mCamera = Camera.open(id); 592 } 593 catch (Exception e) { 594 Log.e(TAG, "camera is not available", e); 595 failTest("camera not available" + e.getMessage()); 596 return; 597 } 598 599 Camera.Parameters p = mCamera.getParameters(); 600 if (VERBOSE) { 601 Log.v(TAG, "setUpCamera: setUpCamera got camera parameters"); 602 } 603 604 // Get preview resolutions 605 List<Size> unsortedSizes = p.getSupportedPreviewSizes(); 606 607 class SizeCompare implements Comparator<Size> { 608 @Override 609 public int compare(Size lhs, Size rhs) { 610 if (lhs.width < rhs.width) return -1; 611 if (lhs.width > rhs.width) return 1; 612 if (lhs.height < rhs.height) return -1; 613 if (lhs.height > rhs.height) return 1; 614 return 0; 615 } 616 }; 617 618 SizeCompare s = new SizeCompare(); 619 TreeSet<Size> sortedResolutions = new TreeSet<Size>(s); 620 sortedResolutions.addAll(unsortedSizes); 621 622 mPreviewSizes = new ArrayList<Size>(sortedResolutions); 623 624 ArrayList<VideoSizeNamePair> availableVideoSizes = getVideoSizeNamePairs(id); 625 String[] availableVideoSizeNames = new String[availableVideoSizes.size()]; 626 mVideoSizeIds = new ArrayList<Integer>(); 627 for (int i = 0; i < availableVideoSizes.size(); i++) { 628 availableVideoSizeNames[i] = availableVideoSizes.get(i).getSizeName(); 629 mVideoSizeIds.add(availableVideoSizes.get(i).getSizeId()); 630 } 631 632 mResolutionSpinner.setAdapter( 633 new ArrayAdapter<String>( 634 this, R.layout.cf_format_list_item, availableVideoSizeNames)); 635 636 // Update untested 637 mUntestedCombinations.remove("All combinations for Camera " + id + "\n"); 638 for (int videoSizeId: mVideoSizeIds) { 639 String combination = "Camera " + id + ", " + videoSizeId + "\n"; 640 if (!mTestedCombinations.contains(combination)) { 641 mUntestedCombinations.add(combination); 642 } 643 } 644 645 // Set initial values 646 mCurrentVideoSizeId = mVideoSizeIds.get(0); 647 mNextPreviewSize = matchPreviewRecordSize(); 648 mResolutionSpinner.setSelection(0); 649 650 // Set up correct display orientation 651 CameraInfo info = new CameraInfo(); 652 Camera.getCameraInfo(id, info); 653 int rotation = getWindowManager().getDefaultDisplay().getRotation(); 654 int degrees = 0; 655 switch (rotation) { 656 case Surface.ROTATION_0: degrees = 0; break; 657 case Surface.ROTATION_90: degrees = 90; break; 658 case Surface.ROTATION_180: degrees = 180; break; 659 case Surface.ROTATION_270: degrees = 270; break; 660 } 661 662 if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { 663 mVideoRotation = (info.orientation + degrees) % 360; 664 mPreviewRotation = (360 - mVideoRotation) % 360; // compensate the mirror 665 } else { // back-facing 666 mVideoRotation = (info.orientation - degrees + 360) % 360; 667 mPreviewRotation = mVideoRotation; 668 } 669 if (mPreviewRotation != 0 && mPreviewRotation != 180) { 670 Log.w(TAG, 671 "Display orientation correction is not 0 or 180, as expected!"); 672 } 673 674 mCamera.setDisplayOrientation(mPreviewRotation); 675 676 // Start up preview if display is ready 677 if (mPreviewTexture != null) { 678 startPreview(); 679 } 680 } 681 shutdownCamera()682 private void shutdownCamera() { 683 if (mCamera != null) { 684 mCamera.setPreviewCallback(null); 685 mCamera.stopPreview(); 686 mCamera.release(); 687 mCamera = null; 688 } 689 } 690 691 /** 692 * starts capturing and drawing frames on screen 693 */ startPreview()694 private void startPreview() { 695 696 mCamera.stopPreview(); 697 698 Matrix transform = new Matrix(); 699 float widthRatio = mNextPreviewSize.width / (float)mPreviewTexWidth; 700 float heightRatio = mNextPreviewSize.height / (float)mPreviewTexHeight; 701 if (VERBOSE) { 702 Log.v(TAG, "startPreview: widthRatio=" + widthRatio + " " + "heightRatio=" + 703 heightRatio); 704 } 705 706 if (heightRatio < widthRatio) { 707 transform.setScale(1, heightRatio / widthRatio); 708 transform.postTranslate(0, 709 mPreviewTexHeight * (1 - heightRatio / widthRatio) / 2); 710 if (VERBOSE) { 711 Log.v(TAG, "startPreview: shrink vertical by " + heightRatio / widthRatio); 712 } 713 } else { 714 transform.setScale(widthRatio / heightRatio, 1); 715 transform.postTranslate(mPreviewTexWidth * (1 - widthRatio / heightRatio) / 2, 0); 716 if (VERBOSE) { 717 Log.v(TAG, "startPreview: shrink horizontal by " + widthRatio / heightRatio); 718 } 719 } 720 721 mPreviewView.setTransform(transform); 722 723 mPreviewSize = mNextPreviewSize; 724 725 Camera.Parameters p = mCamera.getParameters(); 726 p.setPreviewSize(mPreviewSize.width, mPreviewSize.height); 727 mCamera.setParameters(p); 728 729 try { 730 mCamera.setPreviewTexture(mPreviewTexture); 731 if (mPreviewTexture == null) { 732 Log.e(TAG, "preview texture is null."); 733 } 734 if (VERBOSE) { 735 Log.v(TAG, "startPreview: set preview texture in startPreview"); 736 } 737 mCamera.startPreview(); 738 if (VERBOSE) { 739 Log.v(TAG, "startPreview: started preview in startPreview"); 740 } 741 } catch (IOException ioe) { 742 Log.e(TAG, "Unable to start up preview", ioe); 743 // Show a dialog box to tell user test failed 744 failTest("Unable to start preview."); 745 } 746 } 747 failTest(String failMessage)748 private void failTest(String failMessage) { 749 DialogInterface.OnClickListener dialogClickListener = 750 new DialogInterface.OnClickListener() { 751 @Override 752 public void onClick(DialogInterface dialog, int which) { 753 switch (which) { 754 case DialogInterface.BUTTON_POSITIVE: 755 setTestResultAndFinish(/* passed */false); 756 break; 757 case DialogInterface.BUTTON_NEGATIVE: 758 break; 759 } 760 } 761 }; 762 763 AlertDialog.Builder builder = new AlertDialog.Builder(CameraVideoActivity.this); 764 builder.setMessage(getString(R.string.dialog_fail_test) + ". " + failMessage) 765 .setPositiveButton(R.string.fail_quit, dialogClickListener) 766 .setNegativeButton(R.string.cancel, dialogClickListener) 767 .show(); 768 } 769 770 } 771