Home | History | Annotate | Download | only in video
      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      */
    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 
    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
    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
    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
    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 
    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
    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
    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
    313     public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
    314         // Ignored, Camera does all the work for us
    315     }
    316 
    317     @Override
    318     public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
    319         return true;
    320     }
    321 
    322 
    323     @Override
    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 
    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 
    435         public VideoSizeNamePair(int id, String name) {
    436             sizeId = id;
    437             sizeName = name;
    438         }
    439 
    440         public int getSizeId() {
    441             return sizeId;
    442         }
    443 
    444         public String getSizeName() {
    445             return sizeName;
    446         }
    447     }
    448 
    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 
    491         public ResolutionQuality() {
    492             // intentionally left blank
    493         }
    494         public ResolutionQuality(int newSizeId, int newWidth, int newHeight) {
    495             videoSizeId = newSizeId;
    496             width = newWidth;
    497             height = newHeight;
    498         }
    499     }
    500 
    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
    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 
    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 
    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      */
    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 
    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