import android.app.Activity;
import android.app.DownloadManager;
import android.app.VoiceInteractor;
import android.content.Intent;
import android.graphics.Bitmap;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
public class StartCameraActivity extends Activity implements View.OnClickListener{
    private static final int CODE = 1;
    private TextView textView = null;
    private ImageView imageView = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_start_camera);
        init();
    private void init() {
        textView = (TextView) findViewById(R.id.startcamera);
        imageView = (ImageView) findViewById(R.id.showImage);
        textView.setOnClickListener(this);
    @Override
    public void onClick(View view) {
        int id = view.getId();
        switch (id){
            case R.id.startcamera:
                Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);        //打开camera的action
                startActivityForResult(intent, CODE);       //如果不设置MediaStore.OUT_PUT  默认返回bitmap
                break;
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == Activity.RESULT_OK) {              //返回ok
            if (data.getExtras() != null) {
                Bundle bundle = data.getExtras();
                Bitmap mbitmap = (Bitmap) bundle.get("data");
                imageView.setImageBitmap(mbitmap);
  
  为了提高兼容性,系统camera的第三方调用有如下规则:   我们这边以Camera2 为例
  
   1. 三方调用系统所需要的Action
  
  
   MediaStore.ACTION_IMAGE_CAPTURE
   
   MediaStore.ACTION_VIDEO_CAPTURE
   
  
  对应的Camera2中,AndroidManifest.xml 如下,
  
   通过intent-filter进行对Intent-action过滤
  
  。  CaptureActivity为空方法,继承CameraActivity。所以最终还是从CameraActivity的onCreateTasks开始打开Camera.
  
        <activity
            android:name="com.android.camera.CaptureActivity"
            android:label="@string/app_name"
            android:theme="@style/Theme.Camera"
            android:configChanges="orientation|screenSize|keyboardHidden"
            android:windowSoftInputMode="stateAlwaysHidden|adjustPan">
            <intent-filter>
                <action android:name="android.media.action.IMAGE_CAPTURE" />  //拍照的
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        <!-- Video camera and capture use the Camcorder label and icon. -->
        <activity-alias
            android:name="com.android.camera.VideoCamera"
            android:label="@string/video_camera_label"
            android:targetActivity="com.android.camera.CaptureActivity">
            <intent-filter>
                <action android:name="android.media.action.VIDEO_CAMERA" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.media.action.VIDEO_CAPTURE" />   //录像的
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity-alias>
  
   在CameraActivity 中 会对Intent Action进行判断,从而决定当前模式以及需要显示的界面内容等等
  
  mCameraAppUI = new CameraAppUI(this, (MainActivityLayout) findViewById(R.id.activity_root_view), isCaptureIntent());   //显示正确的UI,将其他功能隐藏
  mCurrentModule.init(this, isSecureCamera(), isCaptureIntent());   //当前正确模式
      private boolean isCaptureIntent() {         //判断当前Action
        if (MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())
                || MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction())
                || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) {
            return true;
        } else {
            return false;
二、传入的路径
 
  默认情况下返回的data为bitmap 
  当开发者设置了 
 MediaStore.OUT_PUT的时候,传入uri, 那么系统Camera将会把照片保存到对应的uri位置。 
  PhotoModule.java   拍照模式的总管理  ,VideoModule.java   录像模式的总管理  在保存图片前进行下面操作video 为例 
 if (mIsVideoCaptureIntent && myExtras != null) {
            Uri saveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
            if (saveUri != null) {
                try {
                    mVideoFileDescriptor = mContentResolver.openFileDescriptor(saveUri, "rw");
                    mCurrentVideoUri = saveUri;
                } catch (java.io.FileNotFoundException ex) {
                    // invalid uri
                    Log.e(TAG, ex.toString());
            requestedSizeLimit = myExtras.getLong(MediaStore.EXTRA_SIZE_LIMIT);
 1. 取消
 
  mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());   //传递一个requestcode 为取消 并退出自己Activity
mActivity.finish();
 
  2. 完成
 PhotoModule.java 
   mActivity.setResultEx(Activity.RESULT_OK, new Intent("inline-data").putExtra("data", bitmap));   //传递一个requestcode 为ok,以及一个bitmap
 mActivity.finish();
 
  
 VideoModule.java 
  private void doReturnToCaller(boolean valid) {
        Intent resultIntent = new Intent();
        int resultCode;
        if (valid) {
            resultCode = Activity.RESULT_OK;
            resultIntent.setData(mCurrentVideoUri);
            resultIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        } else {
            resultCode = Activity.RESULT_CANCELED;
        mActivity.setResultEx(resultCode, resultIntent);
        mActivity.finish();
  涉及到bitmap时,需要注意OOM,bitmap及时回收,如果为低端机,需对bitmap进行压缩处理 
  bitmap 进行一个压缩:
				    // 首先不加载图片,仅获取图片尺寸  
				    final BitmapFactory.Options options = new BitmapFactory.Options();  
				    // 当inJustDecodeBounds设为true时,不会加载图片仅获取图片尺寸信息  
				    options.inJustDecodeBounds = true;  
				    // 此时仅会将图片信息会保存至options对象内,decode方法不会返回bitmap对象  
				    BitmapFactory.decodeResource(res, resId, options);  
				    // 计算压缩比例,如inSampleSize=4时,图片会压缩成原图的1/4  
				    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);  
				    // 当inJustDecodeBounds设为false时,BitmapFactory.decode...就会返回图片对象了  
				    options. inJustDecodeBounds = false;  
				    // 利用计算的比例值获取压缩后的图片对象  
				    BitmapFactory.decodeResource(res, resId, options);  
 
  以高通Camera为例连拍需要考虑到一下几点: 
 (1)如何判断他是连拍 
 (2)连拍的速度 
 (3)连拍的用户体验 
                       case MotionEvent.ACTION_DOWN:
				//begin handler the touchEvent;
				tickCountDown(true);     //开启倒计时
				break;
			case MotionEvent.ACTION_CANCEL:
			case MotionEvent.ACTION_UP:
				//end the countdown;
				press = tickCountDown(false);	 //关闭倒计时
				if(press){
					v.setPressed(false);
				break;
 
 private boolean tickCountDown(boolean start){
		boolean rec = false;
		if(start){
			if(mContinuousShotModeCountdownTimer != null){
				mContinuousShotModeCountdownTimer.cancel();
				mContinuousShotModeCountdownTimer = null;
			if(mContinuousShotModeCountdownTimer == null){
				mContinuousShotModeCountdownTimer = new MyCountDownTimer(
						mContinuousShotModeCountdownMilliSec,
						mContinuousShotModeCountdownMilliSec);
			mContinuousShotModeCountdownTimer.start();   //倒计时开始,等执行finish方法
		}else{
			if(mContinuousShotModeCountdownTimer != null){
				mContinuousShotModeCountdownTimer.cancel();    //取消倒计时
				mContinuousShotModeCountdownTimer = null;
				//when onfinish have been called,we should cancel the long press;
				if(mListener != null ){
					rec = mListener.onCancelLongPressShutterButton();   //取消连怕
		return rec;
 private class MyCountDownTimer extends CountDownTimer {
		public MyCountDownTimer(long millisInFuture, long countDownInterval) {
			super(millisInFuture, countDownInterval);
		@Override
		public void onTick(long millisUntilFinished) {
			Log.v(TAG, "Time remaining until entering Continuous Shot Mode "
					+ millisUntilFinished + " .\n");
		@Override
		public void onFinish() {
			Log.d(TAG, "onFinish()---come---onLongPressShutButton---begin!!!");
			if(mListener != null){
				mListener.onLongPressShutButton();    //倒计时结束,开始连拍
二、连拍的速度
 
  在系统Camera中,是通过Camera 的TakePicture进行的 mCamera.takePicture(shutter, raw, postView, jpeg); 
  现在高通手机基本都支持ZSL(零延时),MTK叫ZSD,可以快速抓拍,并且preview界面不会卡顿。 
  那么在高通中,是通过在除了第一次其他takePicture都在shuttercallback中进行,shutterCallback返回速度很快,所以我们takePicture速度也很快。(通过log可以看出,50张控制在5s到6s,当然手机性能很重要) 
  此时,可以通过计数的方法,来控制连拍的张数,可以通过flag的方法判断连拍是否继续。 
        @Override
        public void onShutter(CameraProxy camera) {
            mShutterCallbackTime = System.currentTimeMillis();
            mShutterLag = mShutterCallbackTime - mCaptureStartTime;
            Log.e(TAG, "[KPI Perf] PROFILE_SHUTTER_LAG mShutterLag = " + mShutterLag + "ms");
            synchronized(mCameraDevice) {
                if (mCameraState != LONGSHOT ||                          //判断一下Camera的一个状态
                    !mLongshotActive) {
                    return;
                if(isLongshotNeedCancel()) {
                    return;                                                               //当抬起手指时,会取消连拍,会通过一个false判断
                if(mLongShotCaptureCount == mLongShotCaptureCountLimit) {     //判断拍照的最大数,当然我们可以设置
                    mLongshotActive = false;
                    return;
                mUI.doShutterAnimation();                    //拍照的动画
                Location loc = getLocationAccordPictureFormat(mParameters.get(KEY_PICTURE_FORMAT));
                mLongShotCaptureCount++;
                if (mLongshotSave) {                                                             //进行下一次的拍照,最终的实现是在AndroidCameraManagerImpl中实现。通过handler开启的post方法
                    mCameraDevice.takePicture(mHandler,                           加入消息队列进行拍照。
                            new LongshotShutterCallback(),
                            mRawPictureCallback, mPostViewPictureCallback,
                            new LongshotPictureCallback(loc));
                } else {
                    mCameraDevice.takePicture(mHandler,new LongshotShutterCallback(),
                            mRawPictureCallback, mPostViewPictureCallback,
                            new JpegPictureCallback(loc));
     private final class LongshotPictureCallback implements CameraPictureCallback {
        Location mLocation;
        public LongshotPictureCallback(Location loc) {
            mLocation = loc;
        @Override
        public void onPictureTaken(final byte [] jpegData, CameraProxy camera) {
            if (mPaused) {
                return;
            mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
            String jpegFilePath = new String(jpegData);                                  //对照片的信息的处理
            mNamedImages.nameNewImage(mCaptureStartTime);
            NamedEntity name = mNamedImages.getNextNameEntity();
            String title = (name == null) ? null : name.title;
            long date = (name == null) ? -1 : name.date;
            if (title == null) {
                Log.e(TAG, "Unbalanced name/data pair");
                return;
            if  (date == -1 ) {
                Log.e(TAG, "Invalid filename date");
                return;
            String dstPath = Storage.DIRECTORY;
            File sdCard = android.os.Environment.getExternalStorageDirectory();
            File dstFile = new File(dstPath);
            if (dstFile == null) {
                Log.e(TAG, "Destination file path invalid");
                return;
            File srcFile = new File(jpegFilePath);
            if (srcFile == null) {
                Log.e(TAG, "Source file path invalid");
                return;
            if ( srcFile.renameTo(dstFile) ) {                                                       //进行数据的存储
                Size s = mParameters.getPictureSize();
                String pictureFormat = mParameters.get(KEY_PICTURE_FORMAT);
                mActivity.getMediaSaveService().addImage(
                       null, title, date, mLocation, s.width, s.height,
                       0, null, mOnMediaSavedListener, mContentResolver, pictureFormat);
            } else {
                Log.e(TAG, "Failed to move jpeg file");
  mSoundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0);
 mRefocusSound = mSoundPool.load(mActivity, R.raw.camera_click_x5, 1);
 
 mSoundPool.play(mRefocusSound, 1.0f, 1.0f, 0, 0, 1.0f);     //这边可以控制速率,循环等操作
 mSoundPool.stop(mRefocusSound);
 
作为标准的capture picture功能的入口,主要完成了以下两件事情:
updateProcessorStream(mJpegProcessor, l.mParameters);
mCaptureSequencer->startCapture(msgType)
对于JpegProcess
capture session配置阶段:这是预览之前的阶段。
	预览流程:这段时间,camera不断出帧,显示在TextureView 上。
	拍照流程:点击拍照到最终生效图片的流程。
Note:将预览流程与拍照流程合成一个大的流程,因为我们本文所说的优化重点就在这里。
二、核心思想
预览出帧是
ZSL (zero shutter lag) 中文名称为零延时拍照,是为了减少拍照延时,让拍照&回显瞬间完成的一种技术。
Single Shot
当开始预览后,sensor 和 VFE 会产生 preview 和 snapshot帧, 而最新的snapshot 帧数据会被存储在buffer 中。当拍照被触发,系统计算实际的拍照时间,找出在buffer中的相应帧,然后返回帧到用户
在高通平台, 如果要集成第三方视频或图像处理算法, 通常会在HAL层进行集成, 当然App层一般也可以通过JNI或者OpenGL来实现, 但效率一般没有直接在HAL层集成高, 原因主要由两点:
HAL层能直接获取YUV数据(高通平台App一般可通过设置pictureFormat为NV21来返回YUV数据, MTK平台大多数不支持App层获取YUV数据), 且算法库多数是用C/C++写的, 可...
一、CAM CHI API功能介绍:
  CHI API建立在Google HAL3的灵活性基础之上,目的是将Camera2/HAL3接口分离出来用于使用相机功能,它是一个灵活的图像处理驱动程序(摄像头硬件接...
        if (mSecureCamera) {
            // Change the window flags so that secure camera can show when locked
所拍即所得,按下快门那一刻拍到的照片就是当时所看到的照片,它是相对于普通模式拍照来讲的。
普通模式拍照:
按下快门后需要进行一系列地处理和校正,如对焦、曝光、白平衡等动作,再进行编码。譬如在预览30fps的情况下,按下快门时在第1帧,而实际上拍得的照片是第8帧的图像。
ZSL模式拍照:
会缓存若干帧,在按下快门那一刻,直接提取缓存帧进行编码保存照片。譬如在预览30fps的情况下,按下快门时是在第1帧,则实际上拍得的照片是第1帧的图像。