| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.testingcamera; |
| |
| import android.annotation.SuppressLint; |
| import android.app.Activity; |
| import android.app.FragmentManager; |
| import android.content.res.Resources; |
| import android.graphics.ImageFormat; |
| import android.hardware.Camera; |
| import android.hardware.Camera.Parameters; |
| import android.media.CamcorderProfile; |
| import android.media.MediaRecorder; |
| import android.media.MediaScannerConnection; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.os.Environment; |
| import android.os.Handler; |
| import android.os.SystemClock; |
| import android.view.View; |
| import android.view.SurfaceHolder; |
| import android.view.SurfaceView; |
| import android.view.View.OnClickListener; |
| import android.widget.AdapterView; |
| import android.widget.AdapterView.OnItemSelectedListener; |
| import android.widget.ArrayAdapter; |
| import android.widget.Button; |
| import android.widget.LinearLayout; |
| import android.widget.LinearLayout.LayoutParams; |
| import android.widget.Spinner; |
| import android.widget.TextView; |
| import android.widget.ToggleButton; |
| import android.renderscript.RenderScript; |
| import android.text.Layout; |
| import android.text.method.ScrollingMovementMethod; |
| import android.util.Log; |
| import android.util.SparseArray; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.text.SimpleDateFormat; |
| import java.util.ArrayList; |
| import java.util.Date; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * A simple test application for the camera API. |
| * |
| * The goal of this application is to allow all camera API features to be |
| * exercised, and all information provided by the API to be shown. |
| */ |
| public class TestingCamera extends Activity |
| implements SurfaceHolder.Callback, Camera.PreviewCallback { |
| |
| /** UI elements */ |
| private SurfaceView mPreviewView; |
| private SurfaceHolder mPreviewHolder; |
| private LinearLayout mPreviewColumn; |
| |
| private SurfaceView mCallbackView; |
| private SurfaceHolder mCallbackHolder; |
| |
| private Spinner mCameraSpinner; |
| private Button mInfoButton; |
| private Spinner mPreviewSizeSpinner; |
| private Spinner mPreviewFrameRateSpinner; |
| private ToggleButton mPreviewToggle; |
| private Spinner mAutofocusModeSpinner; |
| private Button mAutofocusButton; |
| private Button mCancelAutofocusButton; |
| private TextView mFlashModeSpinnerLabel; |
| private Spinner mFlashModeSpinner; |
| private ToggleButton mExposureLockToggle; |
| private Spinner mSnapshotSizeSpinner; |
| private Button mTakePictureButton; |
| private Spinner mCamcorderProfileSpinner; |
| private Spinner mVideoRecordSizeSpinner; |
| private Spinner mVideoFrameRateSpinner; |
| private ToggleButton mRecordToggle; |
| private ToggleButton mRecordStabilizationToggle; |
| private Spinner mCallbackFormatSpinner; |
| private ToggleButton mCallbackToggle; |
| |
| private TextView mLogView; |
| |
| private Set<View> mPreviewOnlyControls = new HashSet<View>(); |
| |
| private SparseArray<String> mFormatNames; |
| |
| /** Camera state */ |
| private int mCameraId = 0; |
| private Camera mCamera; |
| private Camera.Parameters mParams; |
| private List<Camera.Size> mPreviewSizes; |
| private int mPreviewSize = 0; |
| private List<Integer> mPreviewFrameRates; |
| private int mPreviewFrameRate = 0; |
| private List<Integer> mPreviewFormats; |
| private int mPreviewFormat = 0; |
| private List<String> mAfModes; |
| private int mAfMode = 0; |
| private List<String> mFlashModes; |
| private int mFlashMode = 0; |
| private List<Camera.Size> mSnapshotSizes; |
| private int mSnapshotSize = 0; |
| private List<CamcorderProfile> mCamcorderProfiles; |
| private int mCamcorderProfile = 0; |
| private List<Camera.Size> mVideoRecordSizes; |
| private int mVideoRecordSize = 0; |
| private List<Integer> mVideoFrameRates; |
| private int mVideoFrameRate = 0; |
| |
| private MediaRecorder mRecorder; |
| private File mRecordingFile; |
| |
| private RenderScript mRS; |
| |
| private boolean mCallbacksEnabled = false; |
| private CallbackProcessor mCallbackProcessor = null; |
| long mLastCallbackTimestamp = -1; |
| float mCallbackAvgFrameDuration = 30; |
| int mCallbackFrameCount = 0; |
| private static final float MEAN_FPS_HISTORY_COEFF = 0.9f; |
| private static final float MEAN_FPS_MEASUREMENT_COEFF = 0.1f; |
| private static final int FPS_REPORTING_PERIOD = 200; // frames |
| private static final int CALLBACK_BUFFER_COUNT = 3; |
| |
| private static final int CAMERA_UNINITIALIZED = 0; |
| private static final int CAMERA_OPEN = 1; |
| private static final int CAMERA_PREVIEW = 2; |
| private static final int CAMERA_TAKE_PICTURE = 3; |
| private static final int CAMERA_RECORD = 4; |
| private int mState = CAMERA_UNINITIALIZED; |
| |
| |
| |
| /** Misc variables */ |
| |
| private static final String TAG = "TestingCamera"; |
| |
| /** Activity lifecycle */ |
| |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| |
| setContentView(R.layout.main); |
| |
| mPreviewColumn = (LinearLayout) findViewById(R.id.preview_column); |
| |
| mPreviewView = (SurfaceView) findViewById(R.id.preview); |
| mPreviewView.getHolder().addCallback(this); |
| |
| mCallbackView = (SurfaceView)findViewById(R.id.callback_view); |
| |
| mCameraSpinner = (Spinner) findViewById(R.id.camera_spinner); |
| mCameraSpinner.setOnItemSelectedListener(mCameraSpinnerListener); |
| |
| mInfoButton = (Button) findViewById(R.id.info_button); |
| mInfoButton.setOnClickListener(mInfoButtonListener); |
| |
| mPreviewSizeSpinner = (Spinner) findViewById(R.id.preview_size_spinner); |
| mPreviewSizeSpinner.setOnItemSelectedListener(mPreviewSizeListener); |
| |
| mPreviewFrameRateSpinner = (Spinner) findViewById(R.id.preview_frame_rate_spinner); |
| mPreviewFrameRateSpinner.setOnItemSelectedListener(mPreviewFrameRateListener); |
| |
| mPreviewToggle = (ToggleButton) findViewById(R.id.start_preview); |
| mPreviewToggle.setOnClickListener(mPreviewToggleListener); |
| |
| mAutofocusModeSpinner = (Spinner) findViewById(R.id.af_mode_spinner); |
| mAutofocusModeSpinner.setOnItemSelectedListener(mAutofocusModeListener); |
| |
| mAutofocusButton = (Button) findViewById(R.id.af_button); |
| mAutofocusButton.setOnClickListener(mAutofocusButtonListener); |
| mPreviewOnlyControls.add(mAutofocusButton); |
| |
| mCancelAutofocusButton = (Button) findViewById(R.id.af_cancel_button); |
| mCancelAutofocusButton.setOnClickListener(mCancelAutofocusButtonListener); |
| mPreviewOnlyControls.add(mCancelAutofocusButton); |
| |
| mFlashModeSpinnerLabel = (TextView) findViewById(R.id.flash_mode_spinner_label); |
| |
| mFlashModeSpinner = (Spinner) findViewById(R.id.flash_mode_spinner); |
| mFlashModeSpinner.setOnItemSelectedListener(mFlashModeListener); |
| |
| mExposureLockToggle = (ToggleButton) findViewById(R.id.exposure_lock); |
| mExposureLockToggle.setOnClickListener(mExposureLockToggleListener); |
| |
| mSnapshotSizeSpinner = (Spinner) findViewById(R.id.snapshot_size_spinner); |
| mSnapshotSizeSpinner.setOnItemSelectedListener(mSnapshotSizeListener); |
| |
| mTakePictureButton = (Button) findViewById(R.id.take_picture); |
| mTakePictureButton.setOnClickListener(mTakePictureListener); |
| mPreviewOnlyControls.add(mTakePictureButton); |
| |
| mCamcorderProfileSpinner = (Spinner) findViewById(R.id.camcorder_profile_spinner); |
| mCamcorderProfileSpinner.setOnItemSelectedListener(mCamcorderProfileListener); |
| |
| mVideoRecordSizeSpinner = (Spinner) findViewById(R.id.video_record_size_spinner); |
| mVideoRecordSizeSpinner.setOnItemSelectedListener(mVideoRecordSizeListener); |
| |
| mVideoFrameRateSpinner = (Spinner) findViewById(R.id.video_frame_rate_spinner); |
| mVideoFrameRateSpinner.setOnItemSelectedListener(mVideoFrameRateListener); |
| |
| mRecordToggle = (ToggleButton) findViewById(R.id.start_record); |
| mRecordToggle.setOnClickListener(mRecordToggleListener); |
| mPreviewOnlyControls.add(mRecordToggle); |
| |
| mRecordStabilizationToggle = (ToggleButton) findViewById(R.id.record_stabilization); |
| mRecordStabilizationToggle.setOnClickListener(mRecordStabilizationToggleListener); |
| |
| mCallbackFormatSpinner = (Spinner) findViewById(R.id.callback_format_spinner); |
| mCallbackFormatSpinner.setOnItemSelectedListener(mCallbackFormatListener); |
| |
| mCallbackToggle = (ToggleButton) findViewById(R.id.enable_callbacks); |
| mCallbackToggle.setOnClickListener(mCallbackToggleListener); |
| |
| mLogView = (TextView) findViewById(R.id.log); |
| mLogView.setMovementMethod(new ScrollingMovementMethod()); |
| |
| mFormatNames = new SparseArray<String>(7); |
| mFormatNames.append(ImageFormat.JPEG, "JPEG"); |
| mFormatNames.append(ImageFormat.NV16, "NV16"); |
| mFormatNames.append(ImageFormat.NV21, "NV21"); |
| mFormatNames.append(ImageFormat.RGB_565, "RGB_565"); |
| mFormatNames.append(ImageFormat.UNKNOWN, "UNKNOWN"); |
| mFormatNames.append(ImageFormat.YUY2, "YUY2"); |
| mFormatNames.append(ImageFormat.YV12, "YV12"); |
| |
| int numCameras = Camera.getNumberOfCameras(); |
| String[] cameraNames = new String[numCameras]; |
| for (int i = 0; i < numCameras; i++) { |
| cameraNames[i] = "Camera " + i; |
| } |
| |
| mCameraSpinner.setAdapter( |
| new ArrayAdapter<String>(this, |
| R.layout.spinner_item, cameraNames)); |
| |
| mRS = RenderScript.create(this); |
| } |
| |
| @Override |
| public void onResume() { |
| super.onResume(); |
| log("onResume: Setting up camera"); |
| mPreviewHolder = null; |
| setUpCamera(); |
| } |
| |
| @Override |
| public void onPause() { |
| super.onPause(); |
| log("onPause: Releasing camera"); |
| mCamera.release(); |
| mState = CAMERA_UNINITIALIZED; |
| } |
| |
| /** SurfaceHolder.Callback methods */ |
| @Override |
| public void surfaceChanged(SurfaceHolder holder, |
| int format, |
| int width, |
| int height) { |
| if (holder == mPreviewView.getHolder()) { |
| if (mState >= CAMERA_OPEN) { |
| final int previewWidth = |
| mPreviewSizes.get(mPreviewSize).width; |
| final int previewHeight = |
| mPreviewSizes.get(mPreviewSize).height; |
| |
| if ( Math.abs((float)previewWidth / previewHeight - |
| (float)width/height) > 0.01f) { |
| Handler h = new Handler(); |
| h.post(new Runnable() { |
| @Override |
| public void run() { |
| layoutPreview(); |
| } |
| }); |
| } |
| } |
| |
| if (mPreviewHolder != null) { |
| return; |
| } |
| log("Surface holder available: " + width + " x " + height); |
| mPreviewHolder = holder; |
| try { |
| if (mCamera != null) { |
| mCamera.setPreviewDisplay(holder); |
| } |
| } catch (IOException e) { |
| logE("Unable to set up preview!"); |
| } |
| } else if (holder == mCallbackView.getHolder()) { |
| mCallbackHolder = holder; |
| } |
| } |
| |
| @Override |
| public void surfaceCreated(SurfaceHolder holder) { |
| |
| } |
| |
| @Override |
| public void surfaceDestroyed(SurfaceHolder holder) { |
| mPreviewHolder = null; |
| } |
| |
| /** UI controls enable/disable */ |
| private void enablePreviewOnlyControls(boolean enabled) { |
| for (View v : mPreviewOnlyControls) { |
| v.setEnabled(enabled); |
| } |
| } |
| |
| /** UI listeners */ |
| |
| private AdapterView.OnItemSelectedListener mCameraSpinnerListener = |
| new AdapterView.OnItemSelectedListener() { |
| @Override |
| public void onItemSelected(AdapterView<?> parent, |
| View view, int pos, long id) { |
| if (mCameraId != pos) { |
| mCameraId = pos; |
| setUpCamera(); |
| } |
| } |
| |
| @Override |
| public void onNothingSelected(AdapterView<?> parent) { |
| |
| } |
| }; |
| |
| private OnClickListener mInfoButtonListener = new OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| FragmentManager fm = getFragmentManager(); |
| InfoDialogFragment infoDialog = new InfoDialogFragment(); |
| infoDialog.updateInfo(mCameraId, mCamera); |
| infoDialog.show(fm, "info_dialog_fragment"); |
| } |
| }; |
| |
| private AdapterView.OnItemSelectedListener mPreviewSizeListener = |
| new AdapterView.OnItemSelectedListener() { |
| @Override |
| public void onItemSelected(AdapterView<?> parent, |
| View view, int pos, long id) { |
| if (pos == mPreviewSize) return; |
| if (mState == CAMERA_PREVIEW) { |
| log("Stopping preview and callbacks to switch resolutions"); |
| stopCallbacks(); |
| mCamera.stopPreview(); |
| } |
| |
| mPreviewSize = pos; |
| int width = mPreviewSizes.get(mPreviewSize).width; |
| int height = mPreviewSizes.get(mPreviewSize).height; |
| mParams.setPreviewSize(width, height); |
| |
| log("Setting preview size to " + width + "x" + height); |
| |
| mCamera.setParameters(mParams); |
| resizePreview(); |
| |
| if (mState == CAMERA_PREVIEW) { |
| log("Restarting preview"); |
| mCamera.startPreview(); |
| } |
| } |
| |
| @Override |
| public void onNothingSelected(AdapterView<?> parent) { |
| |
| } |
| }; |
| |
| private AdapterView.OnItemSelectedListener mPreviewFrameRateListener = |
| new AdapterView.OnItemSelectedListener() { |
| @Override |
| public void onItemSelected(AdapterView<?> parent, |
| View view, int pos, long id) { |
| if (pos == mPreviewFrameRate) return; |
| mPreviewFrameRate = pos; |
| mParams.setPreviewFrameRate(mPreviewFrameRates.get(mPreviewFrameRate)); |
| |
| log("Setting preview frame rate to " + ((TextView)view).getText()); |
| |
| mCamera.setParameters(mParams); |
| } |
| |
| @Override |
| public void onNothingSelected(AdapterView<?> parent) { |
| |
| } |
| }; |
| |
| private View.OnClickListener mPreviewToggleListener = |
| new View.OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| if (mState == CAMERA_TAKE_PICTURE) { |
| logE("Can't change preview state while taking picture!"); |
| return; |
| } |
| if (mPreviewToggle.isChecked()) { |
| log("Starting preview"); |
| mCamera.startPreview(); |
| mState = CAMERA_PREVIEW; |
| enablePreviewOnlyControls(true); |
| } else { |
| log("Stopping preview"); |
| mCamera.stopPreview(); |
| mState = CAMERA_OPEN; |
| |
| enablePreviewOnlyControls(false); |
| } |
| } |
| }; |
| |
| private OnItemSelectedListener mAutofocusModeListener = |
| new OnItemSelectedListener() { |
| @Override |
| public void onItemSelected(AdapterView<?> parent, |
| View view, int pos, long id) { |
| if (pos == mAfMode) return; |
| |
| mAfMode = pos; |
| String focusMode = mAfModes.get(mAfMode); |
| log("Setting focus mode to " + focusMode); |
| if (focusMode == Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE || |
| focusMode == Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO) { |
| mCamera.setAutoFocusMoveCallback(mAutofocusMoveCallback); |
| } |
| mParams.setFocusMode(focusMode); |
| |
| mCamera.setParameters(mParams); |
| } |
| |
| @Override |
| public void onNothingSelected(AdapterView<?> arg0) { |
| |
| } |
| }; |
| |
| private OnClickListener mAutofocusButtonListener = |
| new View.OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| log("Triggering autofocus"); |
| mCamera.autoFocus(mAutofocusCallback); |
| } |
| }; |
| |
| private OnClickListener mCancelAutofocusButtonListener = |
| new View.OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| log("Cancelling autofocus"); |
| mCamera.cancelAutoFocus(); |
| } |
| }; |
| |
| private Camera.AutoFocusCallback mAutofocusCallback = |
| new Camera.AutoFocusCallback() { |
| @Override |
| public void onAutoFocus(boolean success, Camera camera) { |
| log("Autofocus completed: " + (success ? "success" : "failure") ); |
| } |
| }; |
| |
| private Camera.AutoFocusMoveCallback mAutofocusMoveCallback = |
| new Camera.AutoFocusMoveCallback() { |
| @Override |
| public void onAutoFocusMoving(boolean start, Camera camera) { |
| log("Autofocus movement: " + (start ? "starting" : "stopped") ); |
| } |
| }; |
| |
| private OnItemSelectedListener mFlashModeListener = |
| new OnItemSelectedListener() { |
| @Override |
| public void onItemSelected(AdapterView<?> parent, |
| View view, int pos, long id) { |
| if (pos == mFlashMode) return; |
| |
| mFlashMode = pos; |
| String flashMode = mFlashModes.get(mFlashMode); |
| log("Setting flash mode to " + flashMode); |
| mParams.setFlashMode(flashMode); |
| mCamera.setParameters(mParams); |
| } |
| |
| @Override |
| public void onNothingSelected(AdapterView<?> arg0) { |
| |
| } |
| }; |
| |
| |
| private AdapterView.OnItemSelectedListener mSnapshotSizeListener = |
| new AdapterView.OnItemSelectedListener() { |
| @Override |
| public void onItemSelected(AdapterView<?> parent, |
| View view, int pos, long id) { |
| if (pos == mSnapshotSize) return; |
| |
| mSnapshotSize = pos; |
| int width = mSnapshotSizes.get(mSnapshotSize).width; |
| int height = mSnapshotSizes.get(mSnapshotSize).height; |
| log("Setting snapshot size to " + width + " x " + height); |
| |
| mParams.setPictureSize(width, height); |
| |
| mCamera.setParameters(mParams); |
| } |
| |
| @Override |
| public void onNothingSelected(AdapterView<?> parent) { |
| |
| } |
| }; |
| |
| private View.OnClickListener mTakePictureListener = |
| new View.OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| log("Taking picture"); |
| if (mState == CAMERA_PREVIEW) { |
| mState = CAMERA_TAKE_PICTURE; |
| enablePreviewOnlyControls(false); |
| mPreviewToggle.setChecked(false); |
| |
| mCamera.takePicture(mShutterCb, mRawCb, mPostviewCb, mJpegCb); |
| } else { |
| logE("Can't take picture while not running preview!"); |
| } |
| } |
| }; |
| |
| private AdapterView.OnItemSelectedListener mCamcorderProfileListener = |
| new AdapterView.OnItemSelectedListener() { |
| @Override |
| public void onItemSelected(AdapterView<?> parent, |
| View view, int pos, long id) { |
| if (pos != mCamcorderProfile) { |
| log("Setting camcorder profile to " + ((TextView)view).getText()); |
| mCamcorderProfile = pos; |
| } |
| |
| // Additionally change video recording size to match |
| mVideoRecordSize = 0; // "default", in case it's not found |
| int width = mCamcorderProfiles.get(pos).videoFrameWidth; |
| int height = mCamcorderProfiles.get(pos).videoFrameHeight; |
| for (int i = 0; i < mVideoRecordSizes.size(); i++) { |
| Camera.Size s = mVideoRecordSizes.get(i); |
| if (width == s.width && height == s.height) { |
| mVideoRecordSize = i; |
| break; |
| } |
| } |
| log("Setting video record size to " + mVideoRecordSize); |
| mVideoRecordSizeSpinner.setSelection(mVideoRecordSize); |
| } |
| |
| @Override |
| public void onNothingSelected(AdapterView<?> parent) { |
| |
| } |
| }; |
| |
| private AdapterView.OnItemSelectedListener mVideoRecordSizeListener = |
| new AdapterView.OnItemSelectedListener() { |
| @Override |
| public void onItemSelected(AdapterView<?> parent, |
| View view, int pos, long id) { |
| if (pos == mVideoRecordSize) return; |
| |
| log("Setting video record size to " + ((TextView)view).getText()); |
| mVideoRecordSize = pos; |
| } |
| |
| @Override |
| public void onNothingSelected(AdapterView<?> parent) { |
| |
| } |
| }; |
| |
| private AdapterView.OnItemSelectedListener mVideoFrameRateListener = |
| new AdapterView.OnItemSelectedListener() { |
| @Override |
| public void onItemSelected(AdapterView<?> parent, |
| View view, int pos, long id) { |
| if (pos == mVideoFrameRate) return; |
| |
| log("Setting video frame rate to " + ((TextView)view).getText()); |
| mVideoFrameRate = pos; |
| } |
| |
| @Override |
| public void onNothingSelected(AdapterView<?> parent) { |
| |
| } |
| }; |
| |
| private View.OnClickListener mRecordToggleListener = |
| new View.OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| mPreviewToggle.setEnabled(false); |
| if (mState == CAMERA_PREVIEW) { |
| startRecording(); |
| } else if (mState == CAMERA_RECORD) { |
| stopRecording(false); |
| } else { |
| logE("Can't toggle recording in current state!"); |
| } |
| mPreviewToggle.setEnabled(true); |
| } |
| }; |
| |
| private View.OnClickListener mRecordStabilizationToggleListener = |
| new View.OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| boolean on = ((ToggleButton) v).isChecked(); |
| mParams.setVideoStabilization(on); |
| |
| mCamera.setParameters(mParams); |
| } |
| }; |
| |
| private Camera.ShutterCallback mShutterCb = new Camera.ShutterCallback() { |
| @Override |
| public void onShutter() { |
| log("Shutter callback received"); |
| } |
| }; |
| |
| private Camera.PictureCallback mRawCb = new Camera.PictureCallback() { |
| @Override |
| public void onPictureTaken(byte[] data, Camera camera) { |
| log("Raw callback received"); |
| } |
| }; |
| |
| private Camera.PictureCallback mPostviewCb = new Camera.PictureCallback() { |
| @Override |
| public void onPictureTaken(byte[] data, Camera camera) { |
| log("Postview callback received"); |
| } |
| }; |
| |
| private Camera.PictureCallback mJpegCb = new Camera.PictureCallback() { |
| @Override |
| public void onPictureTaken(byte[] data, Camera camera) { |
| log("JPEG picture callback received"); |
| FragmentManager fm = getFragmentManager(); |
| SnapshotDialogFragment snapshotDialog = new SnapshotDialogFragment(); |
| |
| snapshotDialog.updateImage(data); |
| snapshotDialog.show(fm, "snapshot_dialog_fragment"); |
| |
| mPreviewToggle.setEnabled(true); |
| |
| mState = CAMERA_OPEN; |
| } |
| }; |
| |
| private AdapterView.OnItemSelectedListener mCallbackFormatListener = |
| new AdapterView.OnItemSelectedListener() { |
| public void onItemSelected(AdapterView<?> parent, |
| View view, int pos, long id) { |
| mPreviewFormat = pos; |
| |
| log("Setting preview format to " + |
| mFormatNames.get(mPreviewFormats.get(mPreviewFormat))); |
| |
| switch (mState) { |
| case CAMERA_UNINITIALIZED: |
| return; |
| case CAMERA_OPEN: |
| break; |
| case CAMERA_PREVIEW: |
| if (mCallbacksEnabled) { |
| log("Stopping preview and callbacks to switch formats"); |
| stopCallbacks(); |
| mCamera.stopPreview(); |
| } |
| break; |
| case CAMERA_RECORD: |
| logE("Can't update format while recording active"); |
| return; |
| } |
| |
| mParams.setPreviewFormat(mPreviewFormats.get(mPreviewFormat)); |
| |
| if (mCallbacksEnabled) { |
| mCamera.setParameters(mParams); |
| |
| if (mState == CAMERA_PREVIEW) { |
| mCamera.startPreview(); |
| } |
| } |
| |
| configureCallbacks(mCallbackView.getWidth(), mCallbackView.getHeight()); |
| } |
| |
| public void onNothingSelected(AdapterView<?> parent) { |
| |
| } |
| }; |
| |
| private View.OnClickListener mCallbackToggleListener = |
| new View.OnClickListener() { |
| public void onClick(View v) { |
| if (mCallbacksEnabled) { |
| log("Disabling preview callbacks"); |
| stopCallbacks(); |
| mCallbacksEnabled = false; |
| resizePreview(); |
| mCallbackView.setVisibility(View.GONE); |
| |
| } else { |
| log("Enabling preview callbacks"); |
| mCallbacksEnabled = true; |
| resizePreview(); |
| mCallbackView.setVisibility(View.VISIBLE); |
| } |
| } |
| }; |
| |
| |
| // Internal methods |
| |
| void setUpCamera() { |
| log("Setting up camera " + mCameraId); |
| logIndent(1); |
| if (mState >= CAMERA_OPEN) { |
| log("Closing old camera"); |
| mCamera.release(); |
| mState = CAMERA_UNINITIALIZED; |
| } |
| log("Opening camera " + mCameraId); |
| mCamera = Camera.open(mCameraId); |
| mState = CAMERA_OPEN; |
| |
| mParams = mCamera.getParameters(); |
| |
| // Set up preview size selection |
| |
| log("Configuring camera"); |
| logIndent(1); |
| |
| updatePreviewSizes(mParams); |
| updatePreviewFrameRate(mCameraId); |
| updatePreviewFormats(mParams); |
| updateAfModes(mParams); |
| updateFlashModes(mParams); |
| updateSnapshotSizes(mParams); |
| updateCamcorderProfile(mCameraId); |
| updateVideoRecordSize(mCameraId); |
| updateVideoFrameRate(mCameraId); |
| |
| // Trigger updating video record size to match camcorder profile |
| mCamcorderProfileSpinner.setSelection(mCamcorderProfile); |
| |
| if (mParams.isVideoStabilizationSupported()) { |
| log("Video stabilization is supported"); |
| mRecordStabilizationToggle.setEnabled(true); |
| } else { |
| log("Video stabilization not supported"); |
| mRecordStabilizationToggle.setEnabled(false); |
| } |
| |
| if (mParams.isAutoExposureLockSupported()) { |
| log("Auto-Exposure locking is supported"); |
| mExposureLockToggle.setEnabled(true); |
| } else { |
| log("Auto-Exposure locking is not supported"); |
| mExposureLockToggle.setEnabled(false); |
| } |
| |
| // Update parameters based on above updates |
| mCamera.setParameters(mParams); |
| |
| if (mPreviewHolder != null) { |
| log("Setting preview display"); |
| try { |
| mCamera.setPreviewDisplay(mPreviewHolder); |
| } catch(IOException e) { |
| Log.e(TAG, "Unable to set up preview!"); |
| } |
| } |
| |
| logIndent(-1); |
| |
| mPreviewToggle.setEnabled(true); |
| mPreviewToggle.setChecked(false); |
| enablePreviewOnlyControls(false); |
| |
| resizePreview(); |
| if (mPreviewToggle.isChecked()) { |
| log("Starting preview" ); |
| mCamera.startPreview(); |
| mState = CAMERA_PREVIEW; |
| } else { |
| mState = CAMERA_OPEN; |
| } |
| logIndent(-1); |
| } |
| |
| private void updateAfModes(Parameters params) { |
| mAfModes = params.getSupportedFocusModes(); |
| |
| mAutofocusModeSpinner.setAdapter( |
| new ArrayAdapter<String>(this, R.layout.spinner_item, |
| mAfModes.toArray(new String[0]))); |
| |
| mAfMode = 0; |
| |
| params.setFocusMode(mAfModes.get(mAfMode)); |
| |
| log("Setting AF mode to " + mAfModes.get(mAfMode)); |
| } |
| |
| private void updateFlashModes(Parameters params) { |
| mFlashModes = params.getSupportedFlashModes(); |
| |
| if (mFlashModes != null) { |
| mFlashModeSpinnerLabel.setVisibility(View.VISIBLE); |
| mFlashModeSpinner.setVisibility(View.VISIBLE); |
| mFlashModeSpinner.setAdapter( |
| new ArrayAdapter<String>(this, R.layout.spinner_item, |
| mFlashModes.toArray(new String[0]))); |
| |
| mFlashMode = 0; |
| |
| params.setFlashMode(mFlashModes.get(mFlashMode)); |
| |
| log("Setting Flash mode to " + mFlashModes.get(mFlashMode)); |
| } else { |
| // this camera has no flash |
| mFlashModeSpinnerLabel.setVisibility(View.GONE); |
| mFlashModeSpinner.setVisibility(View.GONE); |
| } |
| } |
| |
| private View.OnClickListener mExposureLockToggleListener = |
| new View.OnClickListener() { |
| public void onClick(View v) { |
| boolean on = ((ToggleButton) v).isChecked(); |
| log("Auto-Exposure was " + mParams.getAutoExposureLock()); |
| mParams.setAutoExposureLock(on); |
| log("Auto-Exposure is now " + mParams.getAutoExposureLock()); |
| } |
| }; |
| |
| private void updatePreviewSizes(Camera.Parameters params) { |
| mPreviewSizes = params.getSupportedPreviewSizes(); |
| |
| String[] availableSizeNames = new String[mPreviewSizes.size()]; |
| int i = 0; |
| for (Camera.Size previewSize: mPreviewSizes) { |
| availableSizeNames[i++] = |
| Integer.toString(previewSize.width) + " x " + |
| Integer.toString(previewSize.height); |
| } |
| mPreviewSizeSpinner.setAdapter( |
| new ArrayAdapter<String>( |
| this, R.layout.spinner_item, availableSizeNames)); |
| |
| mPreviewSize = 0; |
| |
| int width = mPreviewSizes.get(mPreviewSize).width; |
| int height = mPreviewSizes.get(mPreviewSize).height; |
| params.setPreviewSize(width, height); |
| log("Setting preview size to " + width + " x " + height); |
| } |
| |
| private void updatePreviewFrameRate(int cameraId) { |
| List<Integer> frameRates = mParams.getSupportedPreviewFrameRates(); |
| int defaultPreviewFrameRate = mParams.getPreviewFrameRate(); |
| |
| List<String> frameRateStrings = new ArrayList<String>(); |
| mPreviewFrameRates = new ArrayList<Integer>(); |
| |
| int currentIndex = 0; |
| for (Integer frameRate : frameRates) { |
| mPreviewFrameRates.add(frameRate); |
| if(frameRate == defaultPreviewFrameRate) { |
| frameRateStrings.add(frameRate.toString() + " (Default)"); |
| mPreviewFrameRate = currentIndex; |
| } else { |
| frameRateStrings.add(frameRate.toString()); |
| } |
| currentIndex++; |
| } |
| |
| String[] nameArray = (String[])frameRateStrings.toArray(new String[0]); |
| mPreviewFrameRateSpinner.setAdapter( |
| new ArrayAdapter<String>( |
| this, R.layout.spinner_item, nameArray)); |
| |
| mPreviewFrameRateSpinner.setSelection(mPreviewFrameRate); |
| log("Setting preview frame rate to " + nameArray[mPreviewFrameRate]); |
| } |
| |
| private void updatePreviewFormats(Camera.Parameters params) { |
| mPreviewFormats = params.getSupportedPreviewFormats(); |
| |
| String[] availableFormatNames = new String[mPreviewFormats.size()]; |
| int i = 0; |
| for (Integer previewFormat: mPreviewFormats) { |
| availableFormatNames[i++] = mFormatNames.get(previewFormat); |
| } |
| mCallbackFormatSpinner.setAdapter( |
| new ArrayAdapter<String>( |
| this, R.layout.spinner_item, availableFormatNames)); |
| |
| mPreviewFormat = 0; |
| mCallbacksEnabled = false; |
| mCallbackToggle.setChecked(false); |
| mCallbackView.setVisibility(View.GONE); |
| |
| params.setPreviewFormat(mPreviewFormats.get(mPreviewFormat)); |
| log("Setting preview format to " + |
| mFormatNames.get(mPreviewFormats.get(mPreviewFormat))); |
| } |
| |
| private void updateSnapshotSizes(Camera.Parameters params) { |
| String[] availableSizeNames; |
| mSnapshotSizes = params.getSupportedPictureSizes(); |
| |
| availableSizeNames = new String[mSnapshotSizes.size()]; |
| int i = 0; |
| for (Camera.Size snapshotSize : mSnapshotSizes) { |
| availableSizeNames[i++] = |
| Integer.toString(snapshotSize.width) + " x " + |
| Integer.toString(snapshotSize.height); |
| } |
| mSnapshotSizeSpinner.setAdapter( |
| new ArrayAdapter<String>( |
| this, R.layout.spinner_item, availableSizeNames)); |
| |
| mSnapshotSize = 0; |
| |
| int snapshotWidth = mSnapshotSizes.get(mSnapshotSize).width; |
| int snapshotHeight = mSnapshotSizes.get(mSnapshotSize).height; |
| params.setPictureSize(snapshotWidth, snapshotHeight); |
| log("Setting snapshot size to " + snapshotWidth + " x " + snapshotHeight); |
| } |
| |
| private void updateCamcorderProfile(int cameraId) { |
| // Have to query all of these individually, |
| final int PROFILES[] = new int[] { |
| CamcorderProfile.QUALITY_1080P, |
| CamcorderProfile.QUALITY_480P, |
| CamcorderProfile.QUALITY_720P, |
| CamcorderProfile.QUALITY_CIF, |
| CamcorderProfile.QUALITY_HIGH, |
| CamcorderProfile.QUALITY_LOW, |
| CamcorderProfile.QUALITY_QCIF, |
| CamcorderProfile.QUALITY_QVGA, |
| CamcorderProfile.QUALITY_TIME_LAPSE_1080P, |
| CamcorderProfile.QUALITY_TIME_LAPSE_480P, |
| CamcorderProfile.QUALITY_TIME_LAPSE_720P, |
| CamcorderProfile.QUALITY_TIME_LAPSE_CIF, |
| CamcorderProfile.QUALITY_TIME_LAPSE_HIGH, |
| CamcorderProfile.QUALITY_TIME_LAPSE_LOW, |
| CamcorderProfile.QUALITY_TIME_LAPSE_QCIF, |
| CamcorderProfile.QUALITY_TIME_LAPSE_QVGA |
| }; |
| |
| final String PROFILE_NAMES[] = new String[] { |
| "1080P", |
| "480P", |
| "720P", |
| "CIF", |
| "HIGH", |
| "LOW", |
| "QCIF", |
| "QVGA", |
| "TIME_LAPSE_1080P", |
| "TIME_LAPSE_480P", |
| "TIME_LAPSE_720P", |
| "TIME_LAPSE_CIF", |
| "TIME_LAPSE_HIGH", |
| "TIME_LAPSE_LOW", |
| "TIME_LAPSE_QCIF", |
| "TIME_LAPSE_QVGA" |
| }; |
| |
| List<String> availableCamcorderProfileNames = new ArrayList<String>(); |
| mCamcorderProfiles = new ArrayList<CamcorderProfile>(); |
| |
| for (int i = 0; i < PROFILES.length; i++) { |
| if (CamcorderProfile.hasProfile(cameraId, PROFILES[i])) { |
| availableCamcorderProfileNames.add(PROFILE_NAMES[i]); |
| mCamcorderProfiles.add(CamcorderProfile.get(cameraId, PROFILES[i])); |
| } |
| } |
| String[] nameArray = (String[])availableCamcorderProfileNames.toArray(new String[0]); |
| mCamcorderProfileSpinner.setAdapter( |
| new ArrayAdapter<String>( |
| this, R.layout.spinner_item, nameArray)); |
| |
| mCamcorderProfile = 0; |
| log("Setting camcorder profile to " + nameArray[mCamcorderProfile]); |
| |
| } |
| |
| private void updateVideoRecordSize(int cameraId) { |
| List<Camera.Size> videoSizes = mParams.getSupportedVideoSizes(); |
| if (videoSizes == null) { // TODO: surface this to the user |
| log("Failed to get video size list, using preview sizes instead"); |
| videoSizes = mParams.getSupportedPreviewSizes(); |
| } |
| |
| List<String> availableVideoRecordSizes = new ArrayList<String>(); |
| mVideoRecordSizes = new ArrayList<Camera.Size>(); |
| |
| availableVideoRecordSizes.add("Default"); |
| mVideoRecordSizes.add(mCamera.new Size(0,0)); |
| |
| for (Camera.Size s : videoSizes) { |
| availableVideoRecordSizes.add(s.width + "x" + s.height); |
| mVideoRecordSizes.add(s); |
| } |
| String[] nameArray = (String[])availableVideoRecordSizes.toArray(new String[0]); |
| mVideoRecordSizeSpinner.setAdapter( |
| new ArrayAdapter<String>( |
| this, R.layout.spinner_item, nameArray)); |
| |
| mVideoRecordSize = 0; |
| log("Setting video record profile to " + nameArray[mVideoRecordSize]); |
| } |
| |
| private void updateVideoFrameRate(int cameraId) { |
| // Use preview framerates as video framerates |
| List<Integer> frameRates = mParams.getSupportedPreviewFrameRates(); |
| |
| List<String> frameRateStrings = new ArrayList<String>(); |
| mVideoFrameRates = new ArrayList<Integer>(); |
| |
| frameRateStrings.add("Default"); |
| mVideoFrameRates.add(0); |
| |
| for (Integer frameRate : frameRates) { |
| frameRateStrings.add(frameRate.toString()); |
| mVideoFrameRates.add(frameRate); |
| } |
| String[] nameArray = (String[])frameRateStrings.toArray(new String[0]); |
| mVideoFrameRateSpinner.setAdapter( |
| new ArrayAdapter<String>( |
| this, R.layout.spinner_item, nameArray)); |
| |
| mVideoFrameRate = 0; |
| log("Setting recording frame rate to " + nameArray[mVideoFrameRate]); |
| } |
| |
| void resizePreview() { |
| // Reset preview layout parameters, to trigger layout pass |
| // This will eventually call layoutPreview below |
| Resources res = getResources(); |
| mPreviewView.setLayoutParams( |
| new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, 0, |
| mCallbacksEnabled ? |
| res.getInteger(R.integer.preview_with_callback_weight): |
| res.getInteger(R.integer.preview_only_weight) )); |
| } |
| |
| void layoutPreview() { |
| int width = mPreviewSizes.get(mPreviewSize).width; |
| int height = mPreviewSizes.get(mPreviewSize).height; |
| float previewAspect = ((float) width) / height; |
| |
| int viewHeight = mPreviewView.getHeight(); |
| int viewWidth = mPreviewView.getWidth(); |
| float viewAspect = ((float) viewWidth) / viewHeight; |
| if ( previewAspect > viewAspect) { |
| viewHeight = (int) (viewWidth / previewAspect); |
| } else { |
| viewWidth = (int) (viewHeight * previewAspect); |
| } |
| mPreviewView.setLayoutParams( |
| new LayoutParams(viewWidth, viewHeight)); |
| |
| if (mCallbacksEnabled) { |
| int callbackHeight = mCallbackView.getHeight(); |
| int callbackWidth = mCallbackView.getWidth(); |
| float callbackAspect = ((float) callbackWidth) / callbackHeight; |
| if ( previewAspect > callbackAspect) { |
| callbackHeight = (int) (callbackWidth / previewAspect); |
| } else { |
| callbackWidth = (int) (callbackHeight * previewAspect); |
| } |
| mCallbackView.setLayoutParams( |
| new LayoutParams(callbackWidth, callbackHeight)); |
| configureCallbacks(callbackWidth, callbackHeight); |
| } |
| } |
| |
| |
| private void configureCallbacks(int callbackWidth, int callbackHeight) { |
| if (mState >= CAMERA_OPEN && mCallbacksEnabled) { |
| mCamera.setPreviewCallbackWithBuffer(null); |
| int width = mPreviewSizes.get(mPreviewSize).width; |
| int height = mPreviewSizes.get(mPreviewSize).height; |
| int format = mPreviewFormats.get(mPreviewFormat); |
| |
| mCallbackProcessor = new CallbackProcessor(width, height, format, |
| getResources(), mCallbackView, |
| callbackWidth, callbackHeight, mRS); |
| |
| int size = getCallbackBufferSize(width, height, format); |
| log("Configuring callbacks:" + width + " x " + height + |
| " , format " + format); |
| for (int i = 0; i < CALLBACK_BUFFER_COUNT; i++) { |
| mCamera.addCallbackBuffer(new byte[size]); |
| } |
| mCamera.setPreviewCallbackWithBuffer(this); |
| } |
| mLastCallbackTimestamp = -1; |
| mCallbackFrameCount = 0; |
| mCallbackAvgFrameDuration = 30; |
| } |
| |
| private void stopCallbacks() { |
| if (mState >= CAMERA_OPEN) { |
| mCamera.setPreviewCallbackWithBuffer(null); |
| if (mCallbackProcessor != null) { |
| if (!mCallbackProcessor.stop()) { |
| logE("Can't stop preview callback processing!"); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void onPreviewFrame(byte[] data, Camera camera) { |
| long timestamp = SystemClock.elapsedRealtime(); |
| if (mLastCallbackTimestamp != -1) { |
| long frameDuration = timestamp - mLastCallbackTimestamp; |
| mCallbackAvgFrameDuration = |
| mCallbackAvgFrameDuration * MEAN_FPS_HISTORY_COEFF + |
| frameDuration * MEAN_FPS_MEASUREMENT_COEFF; |
| } |
| mLastCallbackTimestamp = timestamp; |
| if (mState < CAMERA_PREVIEW || !mCallbacksEnabled) { |
| mCamera.addCallbackBuffer(data); |
| return; |
| } |
| mCallbackFrameCount++; |
| if (mCallbackFrameCount % FPS_REPORTING_PERIOD == 0) { |
| log("Got " + FPS_REPORTING_PERIOD + " callback frames, fps " |
| + 1e3/mCallbackAvgFrameDuration); |
| } |
| mCallbackProcessor.displayCallback(data); |
| |
| mCamera.addCallbackBuffer(data); |
| } |
| |
| static final int MEDIA_TYPE_IMAGE = 0; |
| static final int MEDIA_TYPE_VIDEO = 1; |
| @SuppressLint("SimpleDateFormat") |
| File getOutputMediaFile(int type){ |
| // To be safe, you should check that the SDCard is mounted |
| // using Environment.getExternalStorageState() before doing this. |
| |
| String state = Environment.getExternalStorageState(); |
| if (!Environment.MEDIA_MOUNTED.equals(state)) { |
| return null; |
| } |
| |
| File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory( |
| Environment.DIRECTORY_DCIM), "TestingCamera"); |
| // This location works best if you want the created images to be shared |
| // between applications and persist after your app has been uninstalled. |
| |
| // Create the storage directory if it does not exist |
| if (! mediaStorageDir.exists()){ |
| if (! mediaStorageDir.mkdirs()){ |
| logE("Failed to create directory for pictures/video"); |
| return null; |
| } |
| } |
| |
| // Create a media file name |
| String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); |
| File mediaFile; |
| if (type == MEDIA_TYPE_IMAGE){ |
| mediaFile = new File(mediaStorageDir.getPath() + File.separator + |
| "IMG_"+ timeStamp + ".jpg"); |
| } else if(type == MEDIA_TYPE_VIDEO) { |
| mediaFile = new File(mediaStorageDir.getPath() + File.separator + |
| "VID_"+ timeStamp + ".mp4"); |
| } else { |
| return null; |
| } |
| |
| return mediaFile; |
| } |
| |
| void notifyMediaScannerOfFile(File newFile, |
| final MediaScannerConnection.OnScanCompletedListener listener) { |
| final Handler h = new Handler(); |
| MediaScannerConnection.scanFile(this, |
| new String[] { newFile.toString() }, |
| null, |
| new MediaScannerConnection.OnScanCompletedListener() { |
| @Override |
| public void onScanCompleted(final String path, final Uri uri) { |
| h.post(new Runnable() { |
| @Override |
| public void run() { |
| log("MediaScanner notified: " + |
| path + " -> " + uri); |
| if (listener != null) |
| listener.onScanCompleted(path, uri); |
| } |
| }); |
| } |
| }); |
| } |
| |
| private void deleteFile(File badFile) { |
| if (badFile.exists()) { |
| boolean success = badFile.delete(); |
| if (success) log("Deleted file " + badFile.toString()); |
| else log("Unable to delete file " + badFile.toString()); |
| } |
| } |
| |
| private void startRecording() { |
| log("Starting recording"); |
| logIndent(1); |
| log("Configuring MediaRecoder"); |
| mCamera.unlock(); |
| if (mRecorder != null) { |
| mRecorder.release(); |
| } |
| mRecorder = new MediaRecorder(); |
| mRecorder.setOnErrorListener(mRecordingErrorListener); |
| mRecorder.setOnInfoListener(mRecordingInfoListener); |
| mRecorder.setCamera(mCamera); |
| mRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER); |
| mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); |
| mRecorder.setProfile(mCamcorderProfiles.get(mCamcorderProfile)); |
| Camera.Size videoRecordSize = mVideoRecordSizes.get(mVideoRecordSize); |
| if (videoRecordSize.width > 0 && videoRecordSize.height > 0) { |
| mRecorder.setVideoSize(videoRecordSize.width, videoRecordSize.height); |
| } |
| if (mVideoFrameRates.get(mVideoFrameRate) > 0) { |
| mRecorder.setVideoFrameRate(mVideoFrameRates.get(mVideoFrameRate)); |
| } |
| File outputFile = getOutputMediaFile(MEDIA_TYPE_VIDEO); |
| log("File name:" + outputFile.toString()); |
| mRecorder.setOutputFile(outputFile.toString()); |
| |
| boolean ready = false; |
| log("Preparing MediaRecorder"); |
| try { |
| mRecorder.prepare(); |
| ready = true; |
| } catch (Exception e) { |
| StringWriter writer = new StringWriter(); |
| e.printStackTrace(new PrintWriter(writer)); |
| logE("Exception preparing MediaRecorder:\n" + writer.toString()); |
| } |
| |
| if (ready) { |
| try { |
| log("Starting MediaRecorder"); |
| mRecorder.start(); |
| mState = CAMERA_RECORD; |
| log("Recording active"); |
| mRecordingFile = outputFile; |
| } catch (Exception e) { |
| StringWriter writer = new StringWriter(); |
| e.printStackTrace(new PrintWriter(writer)); |
| logE("Exception starting MediaRecorder:\n" + writer.toString()); |
| } |
| } else { |
| mPreviewToggle.setChecked(false); |
| } |
| logIndent(-1); |
| } |
| |
| private MediaRecorder.OnErrorListener mRecordingErrorListener = |
| new MediaRecorder.OnErrorListener() { |
| @Override |
| public void onError(MediaRecorder mr, int what, int extra) { |
| logE("MediaRecorder reports error: " + what + ", extra " |
| + extra); |
| if (mState == CAMERA_RECORD) { |
| stopRecording(true); |
| } |
| } |
| }; |
| |
| private MediaRecorder.OnInfoListener mRecordingInfoListener = |
| new MediaRecorder.OnInfoListener() { |
| @Override |
| public void onInfo(MediaRecorder mr, int what, int extra) { |
| log("MediaRecorder reports info: " + what + ", extra " |
| + extra); |
| } |
| }; |
| |
| private void stopRecording(boolean error) { |
| log("Stopping recording"); |
| if (mRecorder != null) { |
| mRecorder.stop(); |
| mCamera.lock(); |
| mState = CAMERA_PREVIEW; |
| if (!error) { |
| notifyMediaScannerOfFile(mRecordingFile, null); |
| } else { |
| deleteFile(mRecordingFile); |
| } |
| mRecordingFile = null; |
| } else { |
| logE("Recorder is unexpectedly null!"); |
| } |
| } |
| |
| static int getCallbackBufferSize(int width, int height, int format) { |
| int size = -1; |
| switch (format) { |
| case ImageFormat.NV21: |
| size = width * height * 3 / 2; |
| break; |
| case ImageFormat.YV12: |
| int y_stride = (int) (Math.ceil( width / 16.) * 16); |
| int y_size = y_stride * height; |
| int c_stride = (int) (Math.ceil(y_stride / 32.) * 16); |
| int c_size = c_stride * height/2; |
| size = y_size + c_size * 2; |
| break; |
| case ImageFormat.NV16: |
| case ImageFormat.RGB_565: |
| case ImageFormat.YUY2: |
| size = 2 * width * height; |
| break; |
| case ImageFormat.JPEG: |
| Log.e(TAG, "JPEG callback buffers not supported!"); |
| size = 0; |
| break; |
| case ImageFormat.UNKNOWN: |
| Log.e(TAG, "Unknown-format callback buffers not supported!"); |
| size = 0; |
| break; |
| } |
| return size; |
| } |
| |
| private int mLogIndentLevel = 0; |
| private String mLogIndent = "\t"; |
| /** Increment or decrement log indentation level */ |
| synchronized void logIndent(int delta) { |
| mLogIndentLevel += delta; |
| if (mLogIndentLevel < 0) mLogIndentLevel = 0; |
| char[] mLogIndentArray = new char[mLogIndentLevel + 1]; |
| for (int i = -1; i < mLogIndentLevel; i++) { |
| mLogIndentArray[i + 1] = '\t'; |
| } |
| mLogIndent = new String(mLogIndentArray); |
| } |
| |
| @SuppressLint("SimpleDateFormat") |
| SimpleDateFormat mDateFormatter = new SimpleDateFormat("HH:mm:ss.SSS"); |
| /** Log both to log text view and to device logcat */ |
| void log(String logLine) { |
| Log.d(TAG, logLine); |
| logAndScrollToBottom(logLine, mLogIndent); |
| } |
| |
| void logE(String logLine) { |
| Log.e(TAG, logLine); |
| logAndScrollToBottom(logLine, mLogIndent + "!!! "); |
| } |
| |
| synchronized private void logAndScrollToBottom(String logLine, String logIndent) { |
| StringBuffer logEntry = new StringBuffer(32); |
| logEntry.append("\n").append(mDateFormatter.format(new Date())).append(logIndent); |
| logEntry.append(logLine); |
| mLogView.append(logEntry); |
| final Layout layout = mLogView.getLayout(); |
| if (layout != null){ |
| int scrollDelta = layout.getLineBottom(mLogView.getLineCount() - 1) |
| - mLogView.getScrollY() - mLogView.getHeight(); |
| if(scrollDelta > 0) { |
| mLogView.scrollBy(0, scrollDelta); |
| } |
| } |
| } |
| |
| } |