我是靠谱客的博主 机灵雨,这篇文章主要介绍Camera 中的三方调用以及连拍设计一、三方调用系统Camera二、连拍,现在分享给大家,希望可以做个参考。

这里讲的camera 为android 手机系统camera,看看其是如何设计三方调用和连拍的。

一、三方调用系统Camera

在我们开发apk的时候,比如设置一个头像时,会到手机系统中调用Camera进行拍照,我们的操作是:
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.example.hoperun.cameratest; 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.

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<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进行判断,从而决定当前模式以及需要显示的界面内容等等

复制代码
1
mCameraAppUI = new CameraAppUI(this, (MainActivityLayout) findViewById(R.id.activity_root_view), isCaptureIntent()); //显示正确的UI,将其他功能隐藏
复制代码
1
mCurrentModule.init(this, isSecureCamera(), isCaptureIntent()); //当前正确模式

复制代码
1
2
3
4
5
6
7
8
9
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 为例
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
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); }


三、setResult 传递给调用者

一般在调用camera 拍照或录像结束后,会出现叉叉以及勾勾的界面,一个为取消,一个为完成
在PhotoModule.java 以及 VideoModule.java 中

1. 取消

复制代码
1
2
mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent()); //传递一个requestcode 为取消 并退出自己Activity mActivity.finish();

2. 完成

PhotoModule.java
复制代码
1
2
mActivity.setResultEx(Activity.RESULT_OK, new Intent("inline-data").putExtra("data", bitmap)); //传递一个requestcode 为ok,以及一个bitmap mActivity.finish();

VideoModule.java
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
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进行压缩处理
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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)连拍的用户体验

一、如何判断是连拍

主要对Camera bottombar 拍照按钮的监听为长按时,我自己是这样判断的:
当点击下去时,监听到了Event.Act,此时创建 CountDownTimer 倒计时设定600 ms,其有finish()用来监听600ms时候倒计时完毕
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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; }
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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的方法判断连拍是否继续。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
private final class LongshotShutterCallback implements CameraShutterCallback { @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)); } } } }

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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"); } } }


ps: 连拍这不一定上层做,其实BSP可以很快的拍照传上来。

三、用户体验

这里的用户体验体现在2个方面,一个是界面,一个是声音。

1. 界面

界面的话,可以通过刷新缩略图或者其他操作告诉用户在连拍。

2. 声音

用此方法进行连拍,注定是不能拍一张响一次声音,因为shuttercallback的回调速度是不一样的,这样会导致声音特别难听。
那么可以通过一个声音的循环播放,控制速率,达到好听的效果,提高用户体验;当连拍结束时,停止声音的播放
Camera中是通过soundpool
复制代码
1
2
mSoundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0); mRefocusSound = mSoundPool.load(mActivity, R.raw.camera_click_x5, 1);
复制代码
1
2
mSoundPool.play(mRefocusSound, 1.0f, 1.0f, 0, 0, 1.0f); //这边可以控制速率,循环等操作
复制代码
1
mSoundPool.stop(mRefocusSound);












  

最后

以上就是机灵雨最近收集整理的关于Camera 中的三方调用以及连拍设计一、三方调用系统Camera二、连拍的全部内容,更多相关Camera内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(87)

评论列表共有 0 条评论

立即
投稿
返回
顶部