概述
这里讲的camera 为android 手机系统camera,看看其是如何设计三方调用和连拍的。
一、三方调用系统Camera
在我们开发apk的时候,比如设置一个头像时,会到手机系统中调用Camera进行拍照,我们的操作是:
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
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);
}
三、setResult 传递给调用者
一般在调用camera 拍照或录像结束后,会出现叉叉以及勾勾的界面,一个为取消,一个为完成
在PhotoModule.java 以及 VideoModule.java 中
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)连拍的用户体验
一、如何判断是连拍
主要对Camera bottombar 拍照按钮的监听为长按时,我自己是这样判断的:
当点击下去时,监听到了Event.Act,此时创建
CountDownTimer 倒计时设定600 ms,其有finish()用来监听600ms时候倒计时完毕
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的方法判断连拍是否继续。
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));
}
}
}
}
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
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);
最后
以上就是机灵雨为你收集整理的Camera 中的三方调用以及连拍设计一、三方调用系统Camera二、连拍的全部内容,希望文章能够帮你解决Camera 中的三方调用以及连拍设计一、三方调用系统Camera二、连拍所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复