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