TestingCamera.java revision 753ac7952bbb16004e5dd149dfdbfb70042827ff
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.testingcamera; 18 19import android.annotation.SuppressLint; 20import android.app.Activity; 21import android.app.FragmentManager; 22import android.content.res.Resources; 23import android.graphics.ImageFormat; 24import android.hardware.Camera; 25import android.hardware.Camera.Parameters; 26import android.hardware.Camera.ErrorCallback; 27import android.media.CamcorderProfile; 28import android.media.MediaRecorder; 29import android.media.MediaScannerConnection; 30import android.net.Uri; 31import android.os.Bundle; 32import android.os.Environment; 33import android.os.Handler; 34import android.os.SystemClock; 35import android.view.View; 36import android.view.SurfaceHolder; 37import android.view.SurfaceView; 38import android.view.View.OnClickListener; 39import android.widget.AdapterView; 40import android.widget.AdapterView.OnItemSelectedListener; 41import android.widget.ArrayAdapter; 42import android.widget.Button; 43import android.widget.CheckBox; 44import android.widget.LinearLayout; 45import android.widget.LinearLayout.LayoutParams; 46import android.widget.Spinner; 47import android.widget.TextView; 48import android.widget.ToggleButton; 49import android.renderscript.RenderScript; 50import android.text.Layout; 51import android.text.method.ScrollingMovementMethod; 52import android.util.Log; 53import android.util.SparseArray; 54 55import java.io.File; 56import java.io.IOException; 57import java.io.PrintWriter; 58import java.io.StringWriter; 59import java.text.SimpleDateFormat; 60import java.util.ArrayList; 61import java.util.Date; 62import java.util.HashSet; 63import java.util.List; 64import java.util.Set; 65 66/** 67 * A simple test application for the camera API. 68 * 69 * The goal of this application is to allow all camera API features to be 70 * exercised, and all information provided by the API to be shown. 71 */ 72public class TestingCamera extends Activity 73 implements SurfaceHolder.Callback, Camera.PreviewCallback, 74 Camera.ErrorCallback { 75 76 /** UI elements */ 77 private SurfaceView mPreviewView; 78 private SurfaceHolder mPreviewHolder; 79 private LinearLayout mPreviewColumn; 80 81 private SurfaceView mCallbackView; 82 private SurfaceHolder mCallbackHolder; 83 84 private Spinner mCameraSpinner; 85 private CheckBox mKeepOpenCheckBox; 86 private Button mInfoButton; 87 private Spinner mPreviewSizeSpinner; 88 private Spinner mPreviewFrameRateSpinner; 89 private ToggleButton mPreviewToggle; 90 private Spinner mAutofocusModeSpinner; 91 private Button mAutofocusButton; 92 private Button mCancelAutofocusButton; 93 private TextView mFlashModeSpinnerLabel; 94 private Spinner mFlashModeSpinner; 95 private ToggleButton mExposureLockToggle; 96 private Spinner mSnapshotSizeSpinner; 97 private Button mTakePictureButton; 98 private Spinner mCamcorderProfileSpinner; 99 private Spinner mVideoRecordSizeSpinner; 100 private Spinner mVideoFrameRateSpinner; 101 private ToggleButton mRecordToggle; 102 private CheckBox mRecordHandoffCheckBox; 103 private ToggleButton mRecordStabilizationToggle; 104 private Spinner mCallbackFormatSpinner; 105 private ToggleButton mCallbackToggle; 106 107 private TextView mLogView; 108 109 private Set<View> mOpenOnlyControls = new HashSet<View>(); 110 private Set<View> mPreviewOnlyControls = new HashSet<View>(); 111 112 private SparseArray<String> mFormatNames; 113 114 /** Camera state */ 115 private int mCameraId; 116 private Camera mCamera; 117 private Camera.Parameters mParams; 118 private List<Camera.Size> mPreviewSizes; 119 private int mPreviewSize = 0; 120 private List<Integer> mPreviewFrameRates; 121 private int mPreviewFrameRate = 0; 122 private List<Integer> mPreviewFormats; 123 private int mPreviewFormat = 0; 124 private List<String> mAfModes; 125 private int mAfMode = 0; 126 private List<String> mFlashModes; 127 private int mFlashMode = 0; 128 private List<Camera.Size> mSnapshotSizes; 129 private int mSnapshotSize = 0; 130 private List<CamcorderProfile> mCamcorderProfiles; 131 private int mCamcorderProfile = 0; 132 private List<Camera.Size> mVideoRecordSizes; 133 private int mVideoRecordSize = 0; 134 private List<Integer> mVideoFrameRates; 135 private int mVideoFrameRate = 0; 136 137 private MediaRecorder mRecorder; 138 private File mRecordingFile; 139 140 private RenderScript mRS; 141 142 private boolean mCallbacksEnabled = false; 143 private CallbackProcessor mCallbackProcessor = null; 144 long mLastCallbackTimestamp = -1; 145 float mCallbackAvgFrameDuration = 30; 146 int mCallbackFrameCount = 0; 147 private static final float MEAN_FPS_HISTORY_COEFF = 0.9f; 148 private static final float MEAN_FPS_MEASUREMENT_COEFF = 0.1f; 149 private static final int FPS_REPORTING_PERIOD = 200; // frames 150 private static final int CALLBACK_BUFFER_COUNT = 3; 151 152 private static final int CAMERA_UNINITIALIZED = 0; 153 private static final int CAMERA_OPEN = 1; 154 private static final int CAMERA_PREVIEW = 2; 155 private static final int CAMERA_TAKE_PICTURE = 3; 156 private static final int CAMERA_RECORD = 4; 157 private int mState = CAMERA_UNINITIALIZED; 158 159 private static final int NO_CAMERA_ID = -1; 160 161 /** Misc variables */ 162 163 private static final String TAG = "TestingCamera"; 164 165 166 /** Activity lifecycle */ 167 168 @Override 169 public void onCreate(Bundle savedInstanceState) { 170 super.onCreate(savedInstanceState); 171 172 setContentView(R.layout.main); 173 174 mPreviewColumn = (LinearLayout) findViewById(R.id.preview_column); 175 176 mPreviewView = (SurfaceView) findViewById(R.id.preview); 177 mPreviewView.getHolder().addCallback(this); 178 179 mCallbackView = (SurfaceView)findViewById(R.id.callback_view); 180 181 mCameraSpinner = (Spinner) findViewById(R.id.camera_spinner); 182 mCameraSpinner.setOnItemSelectedListener(mCameraSpinnerListener); 183 184 mKeepOpenCheckBox = (CheckBox) findViewById(R.id.keep_open_checkbox); 185 186 mInfoButton = (Button) findViewById(R.id.info_button); 187 mInfoButton.setOnClickListener(mInfoButtonListener); 188 mOpenOnlyControls.add(mInfoButton); 189 190 mPreviewSizeSpinner = (Spinner) findViewById(R.id.preview_size_spinner); 191 mPreviewSizeSpinner.setOnItemSelectedListener(mPreviewSizeListener); 192 mOpenOnlyControls.add(mPreviewSizeSpinner); 193 194 mPreviewFrameRateSpinner = (Spinner) findViewById(R.id.preview_frame_rate_spinner); 195 mPreviewFrameRateSpinner.setOnItemSelectedListener(mPreviewFrameRateListener); 196 mOpenOnlyControls.add(mPreviewFrameRateSpinner); 197 198 mPreviewToggle = (ToggleButton) findViewById(R.id.start_preview); 199 mPreviewToggle.setOnClickListener(mPreviewToggleListener); 200 mOpenOnlyControls.add(mPreviewToggle); 201 202 mAutofocusModeSpinner = (Spinner) findViewById(R.id.af_mode_spinner); 203 mAutofocusModeSpinner.setOnItemSelectedListener(mAutofocusModeListener); 204 mOpenOnlyControls.add(mAutofocusModeSpinner); 205 206 mAutofocusButton = (Button) findViewById(R.id.af_button); 207 mAutofocusButton.setOnClickListener(mAutofocusButtonListener); 208 mPreviewOnlyControls.add(mAutofocusButton); 209 210 mCancelAutofocusButton = (Button) findViewById(R.id.af_cancel_button); 211 mCancelAutofocusButton.setOnClickListener(mCancelAutofocusButtonListener); 212 mPreviewOnlyControls.add(mCancelAutofocusButton); 213 214 mFlashModeSpinnerLabel = (TextView) findViewById(R.id.flash_mode_spinner_label); 215 216 mFlashModeSpinner = (Spinner) findViewById(R.id.flash_mode_spinner); 217 mFlashModeSpinner.setOnItemSelectedListener(mFlashModeListener); 218 mOpenOnlyControls.add(mFlashModeSpinner); 219 220 mExposureLockToggle = (ToggleButton) findViewById(R.id.exposure_lock); 221 mExposureLockToggle.setOnClickListener(mExposureLockToggleListener); 222 mOpenOnlyControls.add(mExposureLockToggle); 223 224 mSnapshotSizeSpinner = (Spinner) findViewById(R.id.snapshot_size_spinner); 225 mSnapshotSizeSpinner.setOnItemSelectedListener(mSnapshotSizeListener); 226 mOpenOnlyControls.add(mSnapshotSizeSpinner); 227 228 mTakePictureButton = (Button) findViewById(R.id.take_picture); 229 mTakePictureButton.setOnClickListener(mTakePictureListener); 230 mPreviewOnlyControls.add(mTakePictureButton); 231 232 mCamcorderProfileSpinner = (Spinner) findViewById(R.id.camcorder_profile_spinner); 233 mCamcorderProfileSpinner.setOnItemSelectedListener(mCamcorderProfileListener); 234 mOpenOnlyControls.add(mCamcorderProfileSpinner); 235 236 mVideoRecordSizeSpinner = (Spinner) findViewById(R.id.video_record_size_spinner); 237 mVideoRecordSizeSpinner.setOnItemSelectedListener(mVideoRecordSizeListener); 238 mOpenOnlyControls.add(mVideoRecordSizeSpinner); 239 240 mVideoFrameRateSpinner = (Spinner) findViewById(R.id.video_frame_rate_spinner); 241 mVideoFrameRateSpinner.setOnItemSelectedListener(mVideoFrameRateListener); 242 mOpenOnlyControls.add(mVideoFrameRateSpinner); 243 244 mRecordToggle = (ToggleButton) findViewById(R.id.start_record); 245 mRecordToggle.setOnClickListener(mRecordToggleListener); 246 mPreviewOnlyControls.add(mRecordToggle); 247 248 mRecordHandoffCheckBox = (CheckBox) findViewById(R.id.record_handoff_checkbox); 249 250 mRecordStabilizationToggle = (ToggleButton) findViewById(R.id.record_stabilization); 251 mRecordStabilizationToggle.setOnClickListener(mRecordStabilizationToggleListener); 252 mOpenOnlyControls.add(mRecordStabilizationToggle); 253 254 mCallbackFormatSpinner = (Spinner) findViewById(R.id.callback_format_spinner); 255 mCallbackFormatSpinner.setOnItemSelectedListener(mCallbackFormatListener); 256 mOpenOnlyControls.add(mCallbackFormatSpinner); 257 258 mCallbackToggle = (ToggleButton) findViewById(R.id.enable_callbacks); 259 mCallbackToggle.setOnClickListener(mCallbackToggleListener); 260 mOpenOnlyControls.add(mCallbackToggle); 261 262 mLogView = (TextView) findViewById(R.id.log); 263 mLogView.setMovementMethod(new ScrollingMovementMethod()); 264 265 mOpenOnlyControls.addAll(mPreviewOnlyControls); 266 267 mFormatNames = new SparseArray<String>(7); 268 mFormatNames.append(ImageFormat.JPEG, "JPEG"); 269 mFormatNames.append(ImageFormat.NV16, "NV16"); 270 mFormatNames.append(ImageFormat.NV21, "NV21"); 271 mFormatNames.append(ImageFormat.RGB_565, "RGB_565"); 272 mFormatNames.append(ImageFormat.UNKNOWN, "UNKNOWN"); 273 mFormatNames.append(ImageFormat.YUY2, "YUY2"); 274 mFormatNames.append(ImageFormat.YV12, "YV12"); 275 276 int numCameras = Camera.getNumberOfCameras(); 277 String[] cameraNames = new String[numCameras + 1]; 278 cameraNames[0] = "None"; 279 for (int i = 0; i < numCameras; i++) { 280 cameraNames[i + 1] = "Camera " + i; 281 } 282 283 mCameraSpinner.setAdapter( 284 new ArrayAdapter<String>(this, 285 R.layout.spinner_item, cameraNames)); 286 if (numCameras > 0) { 287 mCameraId = 0; 288 mCameraSpinner.setSelection(mCameraId + 1); 289 } else { 290 resetCamera(); 291 mCameraSpinner.setSelection(0); 292 } 293 294 mRS = RenderScript.create(this); 295 } 296 297 @Override 298 public void onResume() { 299 super.onResume(); 300 log("onResume: Setting up"); 301 mPreviewHolder = null; 302 setUpCamera(); 303 } 304 305 @Override 306 public void onPause() { 307 super.onPause(); 308 if (mState == CAMERA_RECORD) { 309 stopRecording(false); 310 } 311 if (mKeepOpenCheckBox.isChecked()) { 312 log("onPause: Not releasing camera"); 313 314 if (mState == CAMERA_PREVIEW) { 315 mCamera.stopPreview(); 316 mState = CAMERA_OPEN; 317 } 318 } else { 319 log("onPause: Releasing camera"); 320 321 if (mCamera != null) { 322 mCamera.release(); 323 } 324 mState = CAMERA_UNINITIALIZED; 325 } 326 } 327 328 /** SurfaceHolder.Callback methods */ 329 @Override 330 public void surfaceChanged(SurfaceHolder holder, 331 int format, 332 int width, 333 int height) { 334 if (holder == mPreviewView.getHolder()) { 335 if (mState >= CAMERA_OPEN) { 336 final int previewWidth = 337 mPreviewSizes.get(mPreviewSize).width; 338 final int previewHeight = 339 mPreviewSizes.get(mPreviewSize).height; 340 341 if ( Math.abs((float)previewWidth / previewHeight - 342 (float)width/height) > 0.01f) { 343 Handler h = new Handler(); 344 h.post(new Runnable() { 345 @Override 346 public void run() { 347 layoutPreview(); 348 } 349 }); 350 } 351 } 352 353 if (mPreviewHolder != null) { 354 return; 355 } 356 log("Surface holder available: " + width + " x " + height); 357 mPreviewHolder = holder; 358 try { 359 if (mCamera != null) { 360 mCamera.setPreviewDisplay(holder); 361 } 362 } catch (IOException e) { 363 logE("Unable to set up preview!"); 364 } 365 } else if (holder == mCallbackView.getHolder()) { 366 mCallbackHolder = holder; 367 } 368 } 369 370 @Override 371 public void surfaceCreated(SurfaceHolder holder) { 372 373 } 374 375 @Override 376 public void surfaceDestroyed(SurfaceHolder holder) { 377 mPreviewHolder = null; 378 } 379 380 /** UI controls enable/disable for all open-only controls */ 381 private void enableOpenOnlyControls(boolean enabled) { 382 for (View v : mOpenOnlyControls) { 383 v.setEnabled(enabled); 384 } 385 } 386 387 /** UI controls enable/disable for all preview-only controls */ 388 private void enablePreviewOnlyControls(boolean enabled) { 389 for (View v : mPreviewOnlyControls) { 390 v.setEnabled(enabled); 391 } 392 } 393 394 /** UI listeners */ 395 396 private AdapterView.OnItemSelectedListener mCameraSpinnerListener = 397 new AdapterView.OnItemSelectedListener() { 398 @Override 399 public void onItemSelected(AdapterView<?> parent, 400 View view, int pos, long id) { 401 int cameraId = pos - 1; 402 if (mCameraId != cameraId) { 403 resetCamera(); 404 mCameraId = cameraId; 405 mPreviewToggle.setChecked(false); 406 setUpCamera(); 407 } 408 } 409 410 @Override 411 public void onNothingSelected(AdapterView<?> parent) { 412 413 } 414 }; 415 416 private OnClickListener mInfoButtonListener = new OnClickListener() { 417 @Override 418 public void onClick(View v) { 419 if (mCameraId != NO_CAMERA_ID) { 420 FragmentManager fm = getFragmentManager(); 421 InfoDialogFragment infoDialog = new InfoDialogFragment(); 422 infoDialog.updateInfo(mCameraId, mCamera); 423 infoDialog.show(fm, "info_dialog_fragment"); 424 } 425 } 426 }; 427 428 private AdapterView.OnItemSelectedListener mPreviewSizeListener = 429 new AdapterView.OnItemSelectedListener() { 430 @Override 431 public void onItemSelected(AdapterView<?> parent, 432 View view, int pos, long id) { 433 if (pos == mPreviewSize) return; 434 if (mState == CAMERA_PREVIEW) { 435 log("Stopping preview and callbacks to switch resolutions"); 436 stopCallbacks(); 437 mCamera.stopPreview(); 438 } 439 440 mPreviewSize = pos; 441 int width = mPreviewSizes.get(mPreviewSize).width; 442 int height = mPreviewSizes.get(mPreviewSize).height; 443 mParams.setPreviewSize(width, height); 444 445 log("Setting preview size to " + width + "x" + height); 446 447 mCamera.setParameters(mParams); 448 resizePreview(); 449 450 if (mState == CAMERA_PREVIEW) { 451 log("Restarting preview"); 452 mCamera.startPreview(); 453 } 454 } 455 456 @Override 457 public void onNothingSelected(AdapterView<?> parent) { 458 459 } 460 }; 461 462 private AdapterView.OnItemSelectedListener mPreviewFrameRateListener = 463 new AdapterView.OnItemSelectedListener() { 464 @Override 465 public void onItemSelected(AdapterView<?> parent, 466 View view, int pos, long id) { 467 if (pos == mPreviewFrameRate) return; 468 mPreviewFrameRate = pos; 469 mParams.setPreviewFrameRate(mPreviewFrameRates.get(mPreviewFrameRate)); 470 471 log("Setting preview frame rate to " + ((TextView)view).getText()); 472 473 mCamera.setParameters(mParams); 474 } 475 476 @Override 477 public void onNothingSelected(AdapterView<?> parent) { 478 479 } 480 }; 481 482 private View.OnClickListener mPreviewToggleListener = 483 new View.OnClickListener() { 484 @Override 485 public void onClick(View v) { 486 if (mState == CAMERA_TAKE_PICTURE) { 487 logE("Can't change preview state while taking picture!"); 488 return; 489 } 490 if (mPreviewToggle.isChecked()) { 491 log("Starting preview"); 492 mCamera.startPreview(); 493 mState = CAMERA_PREVIEW; 494 enablePreviewOnlyControls(true); 495 } else { 496 log("Stopping preview"); 497 mCamera.stopPreview(); 498 mState = CAMERA_OPEN; 499 500 enablePreviewOnlyControls(false); 501 } 502 } 503 }; 504 505 private OnItemSelectedListener mAutofocusModeListener = 506 new OnItemSelectedListener() { 507 @Override 508 public void onItemSelected(AdapterView<?> parent, 509 View view, int pos, long id) { 510 if (pos == mAfMode) return; 511 512 mAfMode = pos; 513 String focusMode = mAfModes.get(mAfMode); 514 log("Setting focus mode to " + focusMode); 515 if (focusMode == Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE || 516 focusMode == Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO) { 517 mCamera.setAutoFocusMoveCallback(mAutofocusMoveCallback); 518 } 519 mParams.setFocusMode(focusMode); 520 521 mCamera.setParameters(mParams); 522 } 523 524 @Override 525 public void onNothingSelected(AdapterView<?> arg0) { 526 527 } 528 }; 529 530 private OnClickListener mAutofocusButtonListener = 531 new View.OnClickListener() { 532 @Override 533 public void onClick(View v) { 534 log("Triggering autofocus"); 535 mCamera.autoFocus(mAutofocusCallback); 536 } 537 }; 538 539 private OnClickListener mCancelAutofocusButtonListener = 540 new View.OnClickListener() { 541 @Override 542 public void onClick(View v) { 543 log("Cancelling autofocus"); 544 mCamera.cancelAutoFocus(); 545 } 546 }; 547 548 private Camera.AutoFocusCallback mAutofocusCallback = 549 new Camera.AutoFocusCallback() { 550 @Override 551 public void onAutoFocus(boolean success, Camera camera) { 552 log("Autofocus completed: " + (success ? "success" : "failure") ); 553 } 554 }; 555 556 private Camera.AutoFocusMoveCallback mAutofocusMoveCallback = 557 new Camera.AutoFocusMoveCallback() { 558 @Override 559 public void onAutoFocusMoving(boolean start, Camera camera) { 560 log("Autofocus movement: " + (start ? "starting" : "stopped") ); 561 } 562 }; 563 564 private OnItemSelectedListener mFlashModeListener = 565 new OnItemSelectedListener() { 566 @Override 567 public void onItemSelected(AdapterView<?> parent, 568 View view, int pos, long id) { 569 if (pos == mFlashMode) return; 570 571 mFlashMode = pos; 572 String flashMode = mFlashModes.get(mFlashMode); 573 log("Setting flash mode to " + flashMode); 574 mParams.setFlashMode(flashMode); 575 mCamera.setParameters(mParams); 576 } 577 578 @Override 579 public void onNothingSelected(AdapterView<?> arg0) { 580 581 } 582 }; 583 584 585 private AdapterView.OnItemSelectedListener mSnapshotSizeListener = 586 new AdapterView.OnItemSelectedListener() { 587 @Override 588 public void onItemSelected(AdapterView<?> parent, 589 View view, int pos, long id) { 590 if (pos == mSnapshotSize) return; 591 592 mSnapshotSize = pos; 593 int width = mSnapshotSizes.get(mSnapshotSize).width; 594 int height = mSnapshotSizes.get(mSnapshotSize).height; 595 log("Setting snapshot size to " + width + " x " + height); 596 597 mParams.setPictureSize(width, height); 598 599 mCamera.setParameters(mParams); 600 } 601 602 @Override 603 public void onNothingSelected(AdapterView<?> parent) { 604 605 } 606 }; 607 608 private View.OnClickListener mTakePictureListener = 609 new View.OnClickListener() { 610 @Override 611 public void onClick(View v) { 612 log("Taking picture"); 613 if (mState == CAMERA_PREVIEW) { 614 mState = CAMERA_TAKE_PICTURE; 615 enablePreviewOnlyControls(false); 616 mPreviewToggle.setChecked(false); 617 618 mCamera.takePicture(mShutterCb, mRawCb, mPostviewCb, mJpegCb); 619 } else { 620 logE("Can't take picture while not running preview!"); 621 } 622 } 623 }; 624 625 private AdapterView.OnItemSelectedListener mCamcorderProfileListener = 626 new AdapterView.OnItemSelectedListener() { 627 @Override 628 public void onItemSelected(AdapterView<?> parent, 629 View view, int pos, long id) { 630 if (pos != mCamcorderProfile) { 631 log("Setting camcorder profile to " + ((TextView)view).getText()); 632 mCamcorderProfile = pos; 633 } 634 635 // Additionally change video recording size to match 636 mVideoRecordSize = 0; // "default", in case it's not found 637 int width = mCamcorderProfiles.get(pos).videoFrameWidth; 638 int height = mCamcorderProfiles.get(pos).videoFrameHeight; 639 for (int i = 0; i < mVideoRecordSizes.size(); i++) { 640 Camera.Size s = mVideoRecordSizes.get(i); 641 if (width == s.width && height == s.height) { 642 mVideoRecordSize = i; 643 break; 644 } 645 } 646 log("Setting video record size to " + mVideoRecordSize); 647 mVideoRecordSizeSpinner.setSelection(mVideoRecordSize); 648 } 649 650 @Override 651 public void onNothingSelected(AdapterView<?> parent) { 652 653 } 654 }; 655 656 private AdapterView.OnItemSelectedListener mVideoRecordSizeListener = 657 new AdapterView.OnItemSelectedListener() { 658 @Override 659 public void onItemSelected(AdapterView<?> parent, 660 View view, int pos, long id) { 661 if (pos == mVideoRecordSize) return; 662 663 log("Setting video record size to " + ((TextView)view).getText()); 664 mVideoRecordSize = pos; 665 } 666 667 @Override 668 public void onNothingSelected(AdapterView<?> parent) { 669 670 } 671 }; 672 673 private AdapterView.OnItemSelectedListener mVideoFrameRateListener = 674 new AdapterView.OnItemSelectedListener() { 675 @Override 676 public void onItemSelected(AdapterView<?> parent, 677 View view, int pos, long id) { 678 if (pos == mVideoFrameRate) return; 679 680 log("Setting video frame rate to " + ((TextView)view).getText()); 681 mVideoFrameRate = pos; 682 } 683 684 @Override 685 public void onNothingSelected(AdapterView<?> parent) { 686 687 } 688 }; 689 690 private View.OnClickListener mRecordToggleListener = 691 new View.OnClickListener() { 692 @Override 693 public void onClick(View v) { 694 mPreviewToggle.setEnabled(false); 695 if (mState == CAMERA_PREVIEW) { 696 startRecording(); 697 } else if (mState == CAMERA_RECORD) { 698 stopRecording(false); 699 } else { 700 logE("Can't toggle recording in current state!"); 701 } 702 mPreviewToggle.setEnabled(true); 703 } 704 }; 705 706 private View.OnClickListener mRecordStabilizationToggleListener = 707 new View.OnClickListener() { 708 @Override 709 public void onClick(View v) { 710 boolean on = ((ToggleButton) v).isChecked(); 711 mParams.setVideoStabilization(on); 712 713 mCamera.setParameters(mParams); 714 } 715 }; 716 717 private Camera.ShutterCallback mShutterCb = new Camera.ShutterCallback() { 718 @Override 719 public void onShutter() { 720 log("Shutter callback received"); 721 } 722 }; 723 724 private Camera.PictureCallback mRawCb = new Camera.PictureCallback() { 725 @Override 726 public void onPictureTaken(byte[] data, Camera camera) { 727 log("Raw callback received"); 728 } 729 }; 730 731 private Camera.PictureCallback mPostviewCb = new Camera.PictureCallback() { 732 @Override 733 public void onPictureTaken(byte[] data, Camera camera) { 734 log("Postview callback received"); 735 } 736 }; 737 738 private Camera.PictureCallback mJpegCb = new Camera.PictureCallback() { 739 @Override 740 public void onPictureTaken(byte[] data, Camera camera) { 741 log("JPEG picture callback received"); 742 FragmentManager fm = getFragmentManager(); 743 SnapshotDialogFragment snapshotDialog = new SnapshotDialogFragment(); 744 745 snapshotDialog.updateImage(data); 746 snapshotDialog.show(fm, "snapshot_dialog_fragment"); 747 748 mPreviewToggle.setEnabled(true); 749 750 mState = CAMERA_OPEN; 751 } 752 }; 753 754 private AdapterView.OnItemSelectedListener mCallbackFormatListener = 755 new AdapterView.OnItemSelectedListener() { 756 public void onItemSelected(AdapterView<?> parent, 757 View view, int pos, long id) { 758 mPreviewFormat = pos; 759 760 log("Setting preview format to " + 761 mFormatNames.get(mPreviewFormats.get(mPreviewFormat))); 762 763 switch (mState) { 764 case CAMERA_UNINITIALIZED: 765 return; 766 case CAMERA_OPEN: 767 break; 768 case CAMERA_PREVIEW: 769 if (mCallbacksEnabled) { 770 log("Stopping preview and callbacks to switch formats"); 771 stopCallbacks(); 772 mCamera.stopPreview(); 773 } 774 break; 775 case CAMERA_RECORD: 776 logE("Can't update format while recording active"); 777 return; 778 } 779 780 mParams.setPreviewFormat(mPreviewFormats.get(mPreviewFormat)); 781 782 if (mCallbacksEnabled) { 783 mCamera.setParameters(mParams); 784 785 if (mState == CAMERA_PREVIEW) { 786 mCamera.startPreview(); 787 } 788 } 789 790 configureCallbacks(mCallbackView.getWidth(), mCallbackView.getHeight()); 791 } 792 793 public void onNothingSelected(AdapterView<?> parent) { 794 795 } 796 }; 797 798 private View.OnClickListener mCallbackToggleListener = 799 new View.OnClickListener() { 800 public void onClick(View v) { 801 if (mCallbacksEnabled) { 802 log("Disabling preview callbacks"); 803 stopCallbacks(); 804 mCallbacksEnabled = false; 805 resizePreview(); 806 mCallbackView.setVisibility(View.GONE); 807 808 } else { 809 log("Enabling preview callbacks"); 810 mCallbacksEnabled = true; 811 resizePreview(); 812 mCallbackView.setVisibility(View.VISIBLE); 813 } 814 } 815 }; 816 817 818 // Internal methods 819 820 void setUpCamera() { 821 if (mCameraId == NO_CAMERA_ID) return; 822 823 log("Setting up camera " + mCameraId); 824 logIndent(1); 825 826 if (mState < CAMERA_OPEN) { 827 log("Opening camera " + mCameraId); 828 829 try { 830 mCamera = Camera.open(mCameraId); 831 } catch (RuntimeException e) { 832 logE("Exception opening camera: " + e.getMessage()); 833 resetCamera(); 834 mCameraSpinner.setSelection(0); 835 logIndent(-1); 836 return; 837 } 838 mState = CAMERA_OPEN; 839 } 840 841 mCamera.setErrorCallback(this); 842 mParams = mCamera.getParameters(); 843 844 // Set up preview size selection 845 846 log("Configuring camera"); 847 logIndent(1); 848 849 updatePreviewSizes(mParams); 850 updatePreviewFrameRate(mCameraId); 851 updatePreviewFormats(mParams); 852 updateAfModes(mParams); 853 updateFlashModes(mParams); 854 updateSnapshotSizes(mParams); 855 updateCamcorderProfile(mCameraId); 856 updateVideoRecordSize(mCameraId); 857 updateVideoFrameRate(mCameraId); 858 859 // Trigger updating video record size to match camcorder profile 860 mCamcorderProfileSpinner.setSelection(mCamcorderProfile); 861 862 if (mParams.isVideoStabilizationSupported()) { 863 log("Video stabilization is supported"); 864 mRecordStabilizationToggle.setEnabled(true); 865 } else { 866 log("Video stabilization not supported"); 867 mRecordStabilizationToggle.setEnabled(false); 868 } 869 870 if (mParams.isAutoExposureLockSupported()) { 871 log("Auto-Exposure locking is supported"); 872 mExposureLockToggle.setEnabled(true); 873 } else { 874 log("Auto-Exposure locking is not supported"); 875 mExposureLockToggle.setEnabled(false); 876 } 877 878 // Update parameters based on above updates 879 mCamera.setParameters(mParams); 880 881 if (mPreviewHolder != null) { 882 log("Setting preview display"); 883 try { 884 mCamera.setPreviewDisplay(mPreviewHolder); 885 } catch(IOException e) { 886 Log.e(TAG, "Unable to set up preview!"); 887 } 888 } 889 890 logIndent(-1); 891 892 enableOpenOnlyControls(true); 893 894 resizePreview(); 895 if (mPreviewToggle.isChecked()) { 896 log("Starting preview" ); 897 mCamera.startPreview(); 898 mState = CAMERA_PREVIEW; 899 } else { 900 mState = CAMERA_OPEN; 901 enablePreviewOnlyControls(false); 902 } 903 logIndent(-1); 904 } 905 906 private void resetCamera() { 907 if (mState >= CAMERA_OPEN) { 908 log("Closing old camera"); 909 mCamera.release(); 910 } 911 mCamera = null; 912 mCameraId = NO_CAMERA_ID; 913 mState = CAMERA_UNINITIALIZED; 914 915 enableOpenOnlyControls(false); 916 } 917 918 private void updateAfModes(Parameters params) { 919 mAfModes = params.getSupportedFocusModes(); 920 921 mAutofocusModeSpinner.setAdapter( 922 new ArrayAdapter<String>(this, R.layout.spinner_item, 923 mAfModes.toArray(new String[0]))); 924 925 mAfMode = 0; 926 927 params.setFocusMode(mAfModes.get(mAfMode)); 928 929 log("Setting AF mode to " + mAfModes.get(mAfMode)); 930 } 931 932 private void updateFlashModes(Parameters params) { 933 mFlashModes = params.getSupportedFlashModes(); 934 935 if (mFlashModes != null) { 936 mFlashModeSpinnerLabel.setVisibility(View.VISIBLE); 937 mFlashModeSpinner.setVisibility(View.VISIBLE); 938 mFlashModeSpinner.setAdapter( 939 new ArrayAdapter<String>(this, R.layout.spinner_item, 940 mFlashModes.toArray(new String[0]))); 941 942 mFlashMode = 0; 943 944 params.setFlashMode(mFlashModes.get(mFlashMode)); 945 946 log("Setting Flash mode to " + mFlashModes.get(mFlashMode)); 947 } else { 948 // this camera has no flash 949 mFlashModeSpinnerLabel.setVisibility(View.GONE); 950 mFlashModeSpinner.setVisibility(View.GONE); 951 } 952 } 953 954 private View.OnClickListener mExposureLockToggleListener = 955 new View.OnClickListener() { 956 public void onClick(View v) { 957 boolean on = ((ToggleButton) v).isChecked(); 958 log("Auto-Exposure was " + mParams.getAutoExposureLock()); 959 mParams.setAutoExposureLock(on); 960 log("Auto-Exposure is now " + mParams.getAutoExposureLock()); 961 } 962 }; 963 964 private void updatePreviewSizes(Camera.Parameters params) { 965 mPreviewSizes = params.getSupportedPreviewSizes(); 966 967 String[] availableSizeNames = new String[mPreviewSizes.size()]; 968 int i = 0; 969 for (Camera.Size previewSize: mPreviewSizes) { 970 availableSizeNames[i++] = 971 Integer.toString(previewSize.width) + " x " + 972 Integer.toString(previewSize.height); 973 } 974 mPreviewSizeSpinner.setAdapter( 975 new ArrayAdapter<String>( 976 this, R.layout.spinner_item, availableSizeNames)); 977 978 mPreviewSize = 0; 979 980 int width = mPreviewSizes.get(mPreviewSize).width; 981 int height = mPreviewSizes.get(mPreviewSize).height; 982 params.setPreviewSize(width, height); 983 log("Setting preview size to " + width + " x " + height); 984 } 985 986 private void updatePreviewFrameRate(int cameraId) { 987 List<Integer> frameRates = mParams.getSupportedPreviewFrameRates(); 988 int defaultPreviewFrameRate = mParams.getPreviewFrameRate(); 989 990 List<String> frameRateStrings = new ArrayList<String>(); 991 mPreviewFrameRates = new ArrayList<Integer>(); 992 993 int currentIndex = 0; 994 for (Integer frameRate : frameRates) { 995 mPreviewFrameRates.add(frameRate); 996 if(frameRate == defaultPreviewFrameRate) { 997 frameRateStrings.add(frameRate.toString() + " (Default)"); 998 mPreviewFrameRate = currentIndex; 999 } else { 1000 frameRateStrings.add(frameRate.toString()); 1001 } 1002 currentIndex++; 1003 } 1004 1005 String[] nameArray = (String[])frameRateStrings.toArray(new String[0]); 1006 mPreviewFrameRateSpinner.setAdapter( 1007 new ArrayAdapter<String>( 1008 this, R.layout.spinner_item, nameArray)); 1009 1010 mPreviewFrameRateSpinner.setSelection(mPreviewFrameRate); 1011 log("Setting preview frame rate to " + nameArray[mPreviewFrameRate]); 1012 } 1013 1014 private void updatePreviewFormats(Camera.Parameters params) { 1015 mPreviewFormats = params.getSupportedPreviewFormats(); 1016 1017 String[] availableFormatNames = new String[mPreviewFormats.size()]; 1018 int i = 0; 1019 for (Integer previewFormat: mPreviewFormats) { 1020 availableFormatNames[i++] = mFormatNames.get(previewFormat); 1021 } 1022 mCallbackFormatSpinner.setAdapter( 1023 new ArrayAdapter<String>( 1024 this, R.layout.spinner_item, availableFormatNames)); 1025 1026 mPreviewFormat = 0; 1027 mCallbacksEnabled = false; 1028 mCallbackToggle.setChecked(false); 1029 mCallbackView.setVisibility(View.GONE); 1030 1031 params.setPreviewFormat(mPreviewFormats.get(mPreviewFormat)); 1032 log("Setting preview format to " + 1033 mFormatNames.get(mPreviewFormats.get(mPreviewFormat))); 1034 } 1035 1036 private void updateSnapshotSizes(Camera.Parameters params) { 1037 String[] availableSizeNames; 1038 mSnapshotSizes = params.getSupportedPictureSizes(); 1039 1040 availableSizeNames = new String[mSnapshotSizes.size()]; 1041 int i = 0; 1042 for (Camera.Size snapshotSize : mSnapshotSizes) { 1043 availableSizeNames[i++] = 1044 Integer.toString(snapshotSize.width) + " x " + 1045 Integer.toString(snapshotSize.height); 1046 } 1047 mSnapshotSizeSpinner.setAdapter( 1048 new ArrayAdapter<String>( 1049 this, R.layout.spinner_item, availableSizeNames)); 1050 1051 mSnapshotSize = 0; 1052 1053 int snapshotWidth = mSnapshotSizes.get(mSnapshotSize).width; 1054 int snapshotHeight = mSnapshotSizes.get(mSnapshotSize).height; 1055 params.setPictureSize(snapshotWidth, snapshotHeight); 1056 log("Setting snapshot size to " + snapshotWidth + " x " + snapshotHeight); 1057 } 1058 1059 private void updateCamcorderProfile(int cameraId) { 1060 // Have to query all of these individually, 1061 final int PROFILES[] = new int[] { 1062 CamcorderProfile.QUALITY_1080P, 1063 CamcorderProfile.QUALITY_480P, 1064 CamcorderProfile.QUALITY_720P, 1065 CamcorderProfile.QUALITY_CIF, 1066 CamcorderProfile.QUALITY_HIGH, 1067 CamcorderProfile.QUALITY_LOW, 1068 CamcorderProfile.QUALITY_QCIF, 1069 CamcorderProfile.QUALITY_QVGA, 1070 CamcorderProfile.QUALITY_TIME_LAPSE_1080P, 1071 CamcorderProfile.QUALITY_TIME_LAPSE_480P, 1072 CamcorderProfile.QUALITY_TIME_LAPSE_720P, 1073 CamcorderProfile.QUALITY_TIME_LAPSE_CIF, 1074 CamcorderProfile.QUALITY_TIME_LAPSE_HIGH, 1075 CamcorderProfile.QUALITY_TIME_LAPSE_LOW, 1076 CamcorderProfile.QUALITY_TIME_LAPSE_QCIF, 1077 CamcorderProfile.QUALITY_TIME_LAPSE_QVGA 1078 }; 1079 1080 final String PROFILE_NAMES[] = new String[] { 1081 "1080P", 1082 "480P", 1083 "720P", 1084 "CIF", 1085 "HIGH", 1086 "LOW", 1087 "QCIF", 1088 "QVGA", 1089 "TIME_LAPSE_1080P", 1090 "TIME_LAPSE_480P", 1091 "TIME_LAPSE_720P", 1092 "TIME_LAPSE_CIF", 1093 "TIME_LAPSE_HIGH", 1094 "TIME_LAPSE_LOW", 1095 "TIME_LAPSE_QCIF", 1096 "TIME_LAPSE_QVGA" 1097 }; 1098 1099 List<String> availableCamcorderProfileNames = new ArrayList<String>(); 1100 mCamcorderProfiles = new ArrayList<CamcorderProfile>(); 1101 1102 for (int i = 0; i < PROFILES.length; i++) { 1103 if (CamcorderProfile.hasProfile(cameraId, PROFILES[i])) { 1104 availableCamcorderProfileNames.add(PROFILE_NAMES[i]); 1105 mCamcorderProfiles.add(CamcorderProfile.get(cameraId, PROFILES[i])); 1106 } 1107 } 1108 String[] nameArray = (String[])availableCamcorderProfileNames.toArray(new String[0]); 1109 mCamcorderProfileSpinner.setAdapter( 1110 new ArrayAdapter<String>( 1111 this, R.layout.spinner_item, nameArray)); 1112 1113 mCamcorderProfile = 0; 1114 log("Setting camcorder profile to " + nameArray[mCamcorderProfile]); 1115 1116 } 1117 1118 private void updateVideoRecordSize(int cameraId) { 1119 List<Camera.Size> videoSizes = mParams.getSupportedVideoSizes(); 1120 if (videoSizes == null) { // TODO: surface this to the user 1121 log("Failed to get video size list, using preview sizes instead"); 1122 videoSizes = mParams.getSupportedPreviewSizes(); 1123 } 1124 1125 List<String> availableVideoRecordSizes = new ArrayList<String>(); 1126 mVideoRecordSizes = new ArrayList<Camera.Size>(); 1127 1128 availableVideoRecordSizes.add("Default"); 1129 mVideoRecordSizes.add(mCamera.new Size(0,0)); 1130 1131 for (Camera.Size s : videoSizes) { 1132 availableVideoRecordSizes.add(s.width + "x" + s.height); 1133 mVideoRecordSizes.add(s); 1134 } 1135 String[] nameArray = (String[])availableVideoRecordSizes.toArray(new String[0]); 1136 mVideoRecordSizeSpinner.setAdapter( 1137 new ArrayAdapter<String>( 1138 this, R.layout.spinner_item, nameArray)); 1139 1140 mVideoRecordSize = 0; 1141 log("Setting video record profile to " + nameArray[mVideoRecordSize]); 1142 } 1143 1144 private void updateVideoFrameRate(int cameraId) { 1145 // Use preview framerates as video framerates 1146 List<Integer> frameRates = mParams.getSupportedPreviewFrameRates(); 1147 1148 List<String> frameRateStrings = new ArrayList<String>(); 1149 mVideoFrameRates = new ArrayList<Integer>(); 1150 1151 frameRateStrings.add("Default"); 1152 mVideoFrameRates.add(0); 1153 1154 for (Integer frameRate : frameRates) { 1155 frameRateStrings.add(frameRate.toString()); 1156 mVideoFrameRates.add(frameRate); 1157 } 1158 String[] nameArray = (String[])frameRateStrings.toArray(new String[0]); 1159 mVideoFrameRateSpinner.setAdapter( 1160 new ArrayAdapter<String>( 1161 this, R.layout.spinner_item, nameArray)); 1162 1163 mVideoFrameRate = 0; 1164 log("Setting recording frame rate to " + nameArray[mVideoFrameRate]); 1165 } 1166 1167 void resizePreview() { 1168 // Reset preview layout parameters, to trigger layout pass 1169 // This will eventually call layoutPreview below 1170 Resources res = getResources(); 1171 mPreviewView.setLayoutParams( 1172 new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, 0, 1173 mCallbacksEnabled ? 1174 res.getInteger(R.integer.preview_with_callback_weight): 1175 res.getInteger(R.integer.preview_only_weight) )); 1176 } 1177 1178 void layoutPreview() { 1179 int width = mPreviewSizes.get(mPreviewSize).width; 1180 int height = mPreviewSizes.get(mPreviewSize).height; 1181 float previewAspect = ((float) width) / height; 1182 1183 int viewHeight = mPreviewView.getHeight(); 1184 int viewWidth = mPreviewView.getWidth(); 1185 float viewAspect = ((float) viewWidth) / viewHeight; 1186 if ( previewAspect > viewAspect) { 1187 viewHeight = (int) (viewWidth / previewAspect); 1188 } else { 1189 viewWidth = (int) (viewHeight * previewAspect); 1190 } 1191 mPreviewView.setLayoutParams( 1192 new LayoutParams(viewWidth, viewHeight)); 1193 1194 if (mCallbacksEnabled) { 1195 int callbackHeight = mCallbackView.getHeight(); 1196 int callbackWidth = mCallbackView.getWidth(); 1197 float callbackAspect = ((float) callbackWidth) / callbackHeight; 1198 if ( previewAspect > callbackAspect) { 1199 callbackHeight = (int) (callbackWidth / previewAspect); 1200 } else { 1201 callbackWidth = (int) (callbackHeight * previewAspect); 1202 } 1203 mCallbackView.setLayoutParams( 1204 new LayoutParams(callbackWidth, callbackHeight)); 1205 configureCallbacks(callbackWidth, callbackHeight); 1206 } 1207 } 1208 1209 1210 private void configureCallbacks(int callbackWidth, int callbackHeight) { 1211 if (mState >= CAMERA_OPEN && mCallbacksEnabled) { 1212 mCamera.setPreviewCallbackWithBuffer(null); 1213 int width = mPreviewSizes.get(mPreviewSize).width; 1214 int height = mPreviewSizes.get(mPreviewSize).height; 1215 int format = mPreviewFormats.get(mPreviewFormat); 1216 1217 mCallbackProcessor = new CallbackProcessor(width, height, format, 1218 getResources(), mCallbackView, 1219 callbackWidth, callbackHeight, mRS); 1220 1221 int size = getCallbackBufferSize(width, height, format); 1222 log("Configuring callbacks:" + width + " x " + height + 1223 " , format " + format); 1224 for (int i = 0; i < CALLBACK_BUFFER_COUNT; i++) { 1225 mCamera.addCallbackBuffer(new byte[size]); 1226 } 1227 mCamera.setPreviewCallbackWithBuffer(this); 1228 } 1229 mLastCallbackTimestamp = -1; 1230 mCallbackFrameCount = 0; 1231 mCallbackAvgFrameDuration = 30; 1232 } 1233 1234 private void stopCallbacks() { 1235 if (mState >= CAMERA_OPEN) { 1236 mCamera.setPreviewCallbackWithBuffer(null); 1237 if (mCallbackProcessor != null) { 1238 if (!mCallbackProcessor.stop()) { 1239 logE("Can't stop preview callback processing!"); 1240 } 1241 } 1242 } 1243 } 1244 1245 @Override 1246 public void onPreviewFrame(byte[] data, Camera camera) { 1247 long timestamp = SystemClock.elapsedRealtime(); 1248 if (mLastCallbackTimestamp != -1) { 1249 long frameDuration = timestamp - mLastCallbackTimestamp; 1250 mCallbackAvgFrameDuration = 1251 mCallbackAvgFrameDuration * MEAN_FPS_HISTORY_COEFF + 1252 frameDuration * MEAN_FPS_MEASUREMENT_COEFF; 1253 } 1254 mLastCallbackTimestamp = timestamp; 1255 if (mState < CAMERA_PREVIEW || !mCallbacksEnabled) { 1256 mCamera.addCallbackBuffer(data); 1257 return; 1258 } 1259 mCallbackFrameCount++; 1260 if (mCallbackFrameCount % FPS_REPORTING_PERIOD == 0) { 1261 log("Got " + FPS_REPORTING_PERIOD + " callback frames, fps " 1262 + 1e3/mCallbackAvgFrameDuration); 1263 } 1264 mCallbackProcessor.displayCallback(data); 1265 1266 mCamera.addCallbackBuffer(data); 1267 } 1268 1269 @Override 1270 public void onError(int error, Camera camera) { 1271 String errorName; 1272 switch (error) { 1273 case Camera.CAMERA_ERROR_SERVER_DIED: 1274 errorName = "SERVER_DIED"; 1275 break; 1276 case Camera.CAMERA_ERROR_UNKNOWN: 1277 errorName = "UNKNOWN"; 1278 break; 1279 default: 1280 errorName = "?"; 1281 break; 1282 } 1283 logE("Camera error received: " + errorName + " (" + error + ")" ); 1284 logE("Shutting down camera"); 1285 resetCamera(); 1286 mCameraSpinner.setSelection(0); 1287 } 1288 1289 static final int MEDIA_TYPE_IMAGE = 0; 1290 static final int MEDIA_TYPE_VIDEO = 1; 1291 @SuppressLint("SimpleDateFormat") 1292 File getOutputMediaFile(int type){ 1293 // To be safe, you should check that the SDCard is mounted 1294 // using Environment.getExternalStorageState() before doing this. 1295 1296 String state = Environment.getExternalStorageState(); 1297 if (!Environment.MEDIA_MOUNTED.equals(state)) { 1298 return null; 1299 } 1300 1301 File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory( 1302 Environment.DIRECTORY_DCIM), "TestingCamera"); 1303 // This location works best if you want the created images to be shared 1304 // between applications and persist after your app has been uninstalled. 1305 1306 // Create the storage directory if it does not exist 1307 if (! mediaStorageDir.exists()){ 1308 if (! mediaStorageDir.mkdirs()){ 1309 logE("Failed to create directory for pictures/video"); 1310 return null; 1311 } 1312 } 1313 1314 // Create a media file name 1315 String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); 1316 File mediaFile; 1317 if (type == MEDIA_TYPE_IMAGE){ 1318 mediaFile = new File(mediaStorageDir.getPath() + File.separator + 1319 "IMG_"+ timeStamp + ".jpg"); 1320 } else if(type == MEDIA_TYPE_VIDEO) { 1321 mediaFile = new File(mediaStorageDir.getPath() + File.separator + 1322 "VID_"+ timeStamp + ".mp4"); 1323 } else { 1324 return null; 1325 } 1326 1327 return mediaFile; 1328 } 1329 1330 void notifyMediaScannerOfFile(File newFile, 1331 final MediaScannerConnection.OnScanCompletedListener listener) { 1332 final Handler h = new Handler(); 1333 MediaScannerConnection.scanFile(this, 1334 new String[] { newFile.toString() }, 1335 null, 1336 new MediaScannerConnection.OnScanCompletedListener() { 1337 @Override 1338 public void onScanCompleted(final String path, final Uri uri) { 1339 h.post(new Runnable() { 1340 @Override 1341 public void run() { 1342 log("MediaScanner notified: " + 1343 path + " -> " + uri); 1344 if (listener != null) 1345 listener.onScanCompleted(path, uri); 1346 } 1347 }); 1348 } 1349 }); 1350 } 1351 1352 private void deleteFile(File badFile) { 1353 if (badFile.exists()) { 1354 boolean success = badFile.delete(); 1355 if (success) log("Deleted file " + badFile.toString()); 1356 else log("Unable to delete file " + badFile.toString()); 1357 } 1358 } 1359 1360 private void startRecording() { 1361 log("Starting recording"); 1362 logIndent(1); 1363 log("Configuring MediaRecoder"); 1364 1365 mRecordHandoffCheckBox.setEnabled(false); 1366 if (mRecordHandoffCheckBox.isChecked()) { 1367 mCamera.release(); 1368 } else { 1369 mCamera.unlock(); 1370 } 1371 1372 if (mRecorder != null) { 1373 mRecorder.release(); 1374 } 1375 1376 mRecorder = new MediaRecorder(); 1377 mRecorder.setOnErrorListener(mRecordingErrorListener); 1378 mRecorder.setOnInfoListener(mRecordingInfoListener); 1379 if (!mRecordHandoffCheckBox.isChecked()) { 1380 mRecorder.setCamera(mCamera); 1381 } 1382 mRecorder.setPreviewDisplay(mPreviewHolder.getSurface()); 1383 1384 mRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER); 1385 mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); 1386 mRecorder.setProfile(mCamcorderProfiles.get(mCamcorderProfile)); 1387 Camera.Size videoRecordSize = mVideoRecordSizes.get(mVideoRecordSize); 1388 if (videoRecordSize.width > 0 && videoRecordSize.height > 0) { 1389 mRecorder.setVideoSize(videoRecordSize.width, videoRecordSize.height); 1390 } 1391 if (mVideoFrameRates.get(mVideoFrameRate) > 0) { 1392 mRecorder.setVideoFrameRate(mVideoFrameRates.get(mVideoFrameRate)); 1393 } 1394 File outputFile = getOutputMediaFile(MEDIA_TYPE_VIDEO); 1395 log("File name:" + outputFile.toString()); 1396 mRecorder.setOutputFile(outputFile.toString()); 1397 1398 boolean ready = false; 1399 log("Preparing MediaRecorder"); 1400 try { 1401 mRecorder.prepare(); 1402 ready = true; 1403 } catch (Exception e) { 1404 StringWriter writer = new StringWriter(); 1405 e.printStackTrace(new PrintWriter(writer)); 1406 logE("Exception preparing MediaRecorder:\n" + writer.toString()); 1407 } 1408 1409 if (ready) { 1410 try { 1411 log("Starting MediaRecorder"); 1412 mRecorder.start(); 1413 mState = CAMERA_RECORD; 1414 log("Recording active"); 1415 mRecordingFile = outputFile; 1416 } catch (Exception e) { 1417 StringWriter writer = new StringWriter(); 1418 e.printStackTrace(new PrintWriter(writer)); 1419 logE("Exception starting MediaRecorder:\n" + writer.toString()); 1420 ready = false; 1421 } 1422 } 1423 1424 if (!ready) { 1425 mRecordToggle.setChecked(false); 1426 mRecordHandoffCheckBox.setEnabled(true); 1427 1428 if (mRecordHandoffCheckBox.isChecked()) { 1429 mState = CAMERA_UNINITIALIZED; 1430 setUpCamera(); 1431 } 1432 } 1433 logIndent(-1); 1434 } 1435 1436 private MediaRecorder.OnErrorListener mRecordingErrorListener = 1437 new MediaRecorder.OnErrorListener() { 1438 @Override 1439 public void onError(MediaRecorder mr, int what, int extra) { 1440 logE("MediaRecorder reports error: " + what + ", extra " 1441 + extra); 1442 if (mState == CAMERA_RECORD) { 1443 stopRecording(true); 1444 } 1445 } 1446 }; 1447 1448 private MediaRecorder.OnInfoListener mRecordingInfoListener = 1449 new MediaRecorder.OnInfoListener() { 1450 @Override 1451 public void onInfo(MediaRecorder mr, int what, int extra) { 1452 log("MediaRecorder reports info: " + what + ", extra " 1453 + extra); 1454 } 1455 }; 1456 1457 private void stopRecording(boolean error) { 1458 log("Stopping recording"); 1459 mRecordHandoffCheckBox.setEnabled(true); 1460 mRecordToggle.setChecked(false); 1461 if (mRecorder != null) { 1462 mRecorder.stop(); 1463 1464 if (mRecordHandoffCheckBox.isChecked()) { 1465 mState = CAMERA_UNINITIALIZED; 1466 setUpCamera(); 1467 } else { 1468 mCamera.lock(); 1469 mState = CAMERA_PREVIEW; 1470 } 1471 1472 if (!error) { 1473 notifyMediaScannerOfFile(mRecordingFile, null); 1474 } else { 1475 deleteFile(mRecordingFile); 1476 } 1477 mRecordingFile = null; 1478 } else { 1479 logE("Recorder is unexpectedly null!"); 1480 } 1481 } 1482 1483 static int getCallbackBufferSize(int width, int height, int format) { 1484 int size = -1; 1485 switch (format) { 1486 case ImageFormat.NV21: 1487 size = width * height * 3 / 2; 1488 break; 1489 case ImageFormat.YV12: 1490 int y_stride = (int) (Math.ceil( width / 16.) * 16); 1491 int y_size = y_stride * height; 1492 int c_stride = (int) (Math.ceil(y_stride / 32.) * 16); 1493 int c_size = c_stride * height/2; 1494 size = y_size + c_size * 2; 1495 break; 1496 case ImageFormat.NV16: 1497 case ImageFormat.RGB_565: 1498 case ImageFormat.YUY2: 1499 size = 2 * width * height; 1500 break; 1501 case ImageFormat.JPEG: 1502 Log.e(TAG, "JPEG callback buffers not supported!"); 1503 size = 0; 1504 break; 1505 case ImageFormat.UNKNOWN: 1506 Log.e(TAG, "Unknown-format callback buffers not supported!"); 1507 size = 0; 1508 break; 1509 } 1510 return size; 1511 } 1512 1513 private int mLogIndentLevel = 0; 1514 private String mLogIndent = "\t"; 1515 /** Increment or decrement log indentation level */ 1516 synchronized void logIndent(int delta) { 1517 mLogIndentLevel += delta; 1518 if (mLogIndentLevel < 0) mLogIndentLevel = 0; 1519 char[] mLogIndentArray = new char[mLogIndentLevel + 1]; 1520 for (int i = -1; i < mLogIndentLevel; i++) { 1521 mLogIndentArray[i + 1] = '\t'; 1522 } 1523 mLogIndent = new String(mLogIndentArray); 1524 } 1525 1526 @SuppressLint("SimpleDateFormat") 1527 SimpleDateFormat mDateFormatter = new SimpleDateFormat("HH:mm:ss.SSS"); 1528 /** Log both to log text view and to device logcat */ 1529 void log(String logLine) { 1530 Log.d(TAG, logLine); 1531 logAndScrollToBottom(logLine, mLogIndent); 1532 } 1533 1534 void logE(String logLine) { 1535 Log.e(TAG, logLine); 1536 logAndScrollToBottom(logLine, mLogIndent + "!!! "); 1537 } 1538 1539 synchronized private void logAndScrollToBottom(String logLine, String logIndent) { 1540 StringBuffer logEntry = new StringBuffer(32); 1541 logEntry.append("\n").append(mDateFormatter.format(new Date())).append(logIndent); 1542 logEntry.append(logLine); 1543 mLogView.append(logEntry); 1544 final Layout layout = mLogView.getLayout(); 1545 if (layout != null){ 1546 int scrollDelta = layout.getLineBottom(mLogView.getLineCount() - 1) 1547 - mLogView.getScrollY() - mLogView.getHeight(); 1548 if(scrollDelta > 0) { 1549 mLogView.scrollBy(0, scrollDelta); 1550 } 1551 } 1552 } 1553} 1554