如何在不建立新会话的情况下在 Camera2 API 中启用闪光灯?
How to enable flash light in Camera2 API without building new session?
我正在开发基于 Camera2 的应用程序 API。我想在此应用程序中添加 enabling/disabling 手电筒功能。
有些问题。我使用 3 个不同的线程进行渲染等一个,并通过处理程序在这些线程之间进行通信。当用户按下闪光灯按钮时,我想启用它,我可以这样做:
mPreviewBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_SINGLE);
mPreviewSession.setRepeatingRequest(mPreviewBuilder.build(), null, null);
但是,据我所知,创建新的预览会话是一个昂贵的过程,而且我也不想从相机中丢失任何帧。
有什么方法可以在不建立新会话的情况下启用手电筒吗?或者我错了,它根本不需要很多资源?
我也想知道它是如何在标准相机应用程序中实现的。也许有任何开源代码?
先谢谢大家的解答!
试试这个,使用 camera2 拍照的完整代码 API,Fragment_Camera4
public class Fragment_Camera4 extends Fragment implements
AspectRatioFragment.Listener {
private View view;
private static final String TAG = "MainActivity";
private static final int REQUEST_CAMERA_PERMISSION = 1;
private static final String FRAGMENT_DIALOG = "dialog";
private static final int[] FLASH_OPTIONS = {
CameraView.FLASH_OFF,
CameraView.FLASH_ON,
};
private static final int[] FLASH_ICONS = {
R.drawable.ic_flash_off,
R.drawable.ic_flash_on,
};
private static final int[] FLASH_TITLES = {
R.string.flash_auto,
R.string.flash_off,
R.string.flash_on,
};
private int mCurrentFlash;
private CameraView mCameraView;
private Handler mBackgroundHandler;
private View.OnClickListener mOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.take_picture:
if (mCameraView != null) {
mCameraView.takePicture();
}
break;
case R.id.btn_switch_camera:
if (mCameraView != null) {
int facing = mCameraView.getFacing();
mCameraView.setFacing(facing == CameraView.FACING_FRONT ?
CameraView.FACING_BACK : CameraView.FACING_FRONT);
if (facing == CameraView.FACING_FRONT) {
btn_flash_onOff.setVisibility(View.VISIBLE);
} else {
btn_flash_onOff.setVisibility(View.GONE);
}
}
break;
case R.id.btn_flash_onOff:
if (mCameraView != null) {
mCurrentFlash = (mCurrentFlash + 1) % FLASH_OPTIONS.length;
btn_flash_onOff.setImageResource(FLASH_ICONS[mCurrentFlash]);
mCameraView.setFlash(FLASH_OPTIONS[mCurrentFlash]);
}
break;
}
}
};
ImageButton btn_flash_onOff;
public Fragment_Camera4() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
view = inflater.inflate(R.layout.fragment_fragment__camera4, container, false);
mCameraView = (CameraView) view.findViewById(R.id.camera);
if (mCameraView != null) {
mCameraView.addCallback(mCallback);
}
ImageButton fab = (ImageButton) view.findViewById(R.id.take_picture);
ImageButton btn_switch_camera = (ImageButton) view.findViewById(R.id.btn_switch_camera);
btn_flash_onOff = (ImageButton) view.findViewById(R.id.btn_flash_onOff);
if (fab != null) {
fab.setOnClickListener(mOnClickListener);
btn_switch_camera.setOnClickListener(mOnClickListener);
btn_flash_onOff.setOnClickListener(mOnClickListener);
}
return view;
}
@Override
public void onResume() {
super.onResume();
mCameraView.start();
}
@Override
public void onPause() {
mCameraView.stop();
super.onPause();
}
@Override
public void onDestroy() {
super.onDestroy();
if (mBackgroundHandler != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
mBackgroundHandler.getLooper().quitSafely();
} else {
mBackgroundHandler.getLooper().quit();
}
mBackgroundHandler = null;
}
}
@Override
public void onAspectRatioSelected(@NonNull AspectRatio ratio) {
if (mCameraView != null) {
Toast.makeText(getActivity(), ratio.toString(), Toast.LENGTH_SHORT).show();
mCameraView.setAspectRatio(ratio);
}
}
private Handler getBackgroundHandler() {
if (mBackgroundHandler == null) {
HandlerThread thread = new HandlerThread("background");
thread.start();
mBackgroundHandler = new Handler(thread.getLooper());
}
return mBackgroundHandler;
}
private CameraView.Callback mCallback
= new CameraView.Callback() {
@Override
public void onCameraOpened(CameraView cameraView) {
Log.d(TAG, "onCameraOpened");
}
@Override
public void onCameraClosed(CameraView cameraView) {
Log.d(TAG, "onCameraClosed");
}
@Override
public void onPictureTaken(CameraView cameraView, final byte[] data) {
Log.d(TAG, "onPictureTaken " + data.length);
Toast.makeText(cameraView.getContext(), R.string.picture_taken, Toast.LENGTH_SHORT)
.show();
}
};
}
那么我的这个githublink的Cameraview文件就在这里
https://github.com/quicklearner4991/Camera2/blob/master/CameraView
从这个 link 获取资源,我不能 post 在这里回答那么长,我已经使用不容易获得的闪光选项编辑了 camera2 的代码。
Link 使用此 repo 下载资源
https://github.com/googlesamples/android-Camera2Basic
xml 的观看次数
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorgrey">
<com.google.android.cameraview.CameraView
android:id="@+id/camera"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:adjustViewBounds="true"
android:background="@android:color/black" />
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true" />
<ImageButton
android:id="@+id/take_picture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="20dp"
android:background="#0000"
app:srcCompat="@drawable/ic_take_pic" />
<ImageButton
android:id="@+id/btn_switch_camera"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_margin="20dp"
android:background="#0000"
app:srcCompat="@drawable/ic_reset_camera" />
<ImageButton
android:id="@+id/btn_flash_onOff"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_margin="20dp"
android:background="#0000"
app:srcCompat="@drawable/ic_flash_off" />
</RelativeLayout>
你只需要创建一个 CameraCaptureSession
,然后调用它的 capture()
或 setRepeatingRequest()
方法来发送 CaptureRequest
给它,所以它不是一个昂贵的过程根本上,唯一的 CameraCaptureSession
是在您打开 CameraDevice
并开始预览时创建的,CameraCaptureSession
将处理您发送的所有 CaptureRequest
并执行它最好不要丢帧。
查看 google/cameraview 中的代码:
void setFlash(int flash) {
if (mFlash == flash) {
return;
}
int saved = mFlash;
mFlash = flash;
if (mPreviewRequestBuilder != null) {
updateFlash(); // Set the new flash settings
if (mCaptureSession != null) {
try {
mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(),
mCaptureCallback, null);
} catch (CameraAccessException e) {
mFlash = saved; // Revert
}
}
}
}
void updateFlash() {
switch (mFlash) {
case Constants.FLASH_OFF:
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON);
mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_OFF);
break;
case Constants.FLASH_ON:
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_OFF);
break;
case Constants.FLASH_TORCH:
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON);
mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_TORCH);
break;
case Constants.FLASH_AUTO:
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_OFF);
break;
case Constants.FLASH_RED_EYE:
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE);
mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_OFF);
break;
}
}
更改会话的重复请求根本不昂贵。它不会重新创建会话(只有当您再次调用 CameraDevice.createCaptureSession 时才会发生),并且通常不会因为更改这样的设置而出现任何故障。更改重复请求正是您打开相机设备时打开手电筒的方式。
请注意,在重复请求中将闪光模式设置为 SINGLE 可能不是您想要的;你可能想要 TORCH 的闪光模式(为了连续平滑的手电筒,而不是频闪主闪光灯闪光),你还需要确保你的 CONTROL_AE_MODE 设置不是 AE_MODE_ON_FLASH* 模式之一;否则相机设备的自动曝光例程由闪光灯控制,FLASH_MODE 设置将被忽略。
我正在开发基于 Camera2 的应用程序 API。我想在此应用程序中添加 enabling/disabling 手电筒功能。
有些问题。我使用 3 个不同的线程进行渲染等一个,并通过处理程序在这些线程之间进行通信。当用户按下闪光灯按钮时,我想启用它,我可以这样做:
mPreviewBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_SINGLE);
mPreviewSession.setRepeatingRequest(mPreviewBuilder.build(), null, null);
但是,据我所知,创建新的预览会话是一个昂贵的过程,而且我也不想从相机中丢失任何帧。
有什么方法可以在不建立新会话的情况下启用手电筒吗?或者我错了,它根本不需要很多资源?
我也想知道它是如何在标准相机应用程序中实现的。也许有任何开源代码?
先谢谢大家的解答!
试试这个,使用 camera2 拍照的完整代码 API,Fragment_Camera4
public class Fragment_Camera4 extends Fragment implements
AspectRatioFragment.Listener {
private View view;
private static final String TAG = "MainActivity";
private static final int REQUEST_CAMERA_PERMISSION = 1;
private static final String FRAGMENT_DIALOG = "dialog";
private static final int[] FLASH_OPTIONS = {
CameraView.FLASH_OFF,
CameraView.FLASH_ON,
};
private static final int[] FLASH_ICONS = {
R.drawable.ic_flash_off,
R.drawable.ic_flash_on,
};
private static final int[] FLASH_TITLES = {
R.string.flash_auto,
R.string.flash_off,
R.string.flash_on,
};
private int mCurrentFlash;
private CameraView mCameraView;
private Handler mBackgroundHandler;
private View.OnClickListener mOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.take_picture:
if (mCameraView != null) {
mCameraView.takePicture();
}
break;
case R.id.btn_switch_camera:
if (mCameraView != null) {
int facing = mCameraView.getFacing();
mCameraView.setFacing(facing == CameraView.FACING_FRONT ?
CameraView.FACING_BACK : CameraView.FACING_FRONT);
if (facing == CameraView.FACING_FRONT) {
btn_flash_onOff.setVisibility(View.VISIBLE);
} else {
btn_flash_onOff.setVisibility(View.GONE);
}
}
break;
case R.id.btn_flash_onOff:
if (mCameraView != null) {
mCurrentFlash = (mCurrentFlash + 1) % FLASH_OPTIONS.length;
btn_flash_onOff.setImageResource(FLASH_ICONS[mCurrentFlash]);
mCameraView.setFlash(FLASH_OPTIONS[mCurrentFlash]);
}
break;
}
}
};
ImageButton btn_flash_onOff;
public Fragment_Camera4() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
view = inflater.inflate(R.layout.fragment_fragment__camera4, container, false);
mCameraView = (CameraView) view.findViewById(R.id.camera);
if (mCameraView != null) {
mCameraView.addCallback(mCallback);
}
ImageButton fab = (ImageButton) view.findViewById(R.id.take_picture);
ImageButton btn_switch_camera = (ImageButton) view.findViewById(R.id.btn_switch_camera);
btn_flash_onOff = (ImageButton) view.findViewById(R.id.btn_flash_onOff);
if (fab != null) {
fab.setOnClickListener(mOnClickListener);
btn_switch_camera.setOnClickListener(mOnClickListener);
btn_flash_onOff.setOnClickListener(mOnClickListener);
}
return view;
}
@Override
public void onResume() {
super.onResume();
mCameraView.start();
}
@Override
public void onPause() {
mCameraView.stop();
super.onPause();
}
@Override
public void onDestroy() {
super.onDestroy();
if (mBackgroundHandler != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
mBackgroundHandler.getLooper().quitSafely();
} else {
mBackgroundHandler.getLooper().quit();
}
mBackgroundHandler = null;
}
}
@Override
public void onAspectRatioSelected(@NonNull AspectRatio ratio) {
if (mCameraView != null) {
Toast.makeText(getActivity(), ratio.toString(), Toast.LENGTH_SHORT).show();
mCameraView.setAspectRatio(ratio);
}
}
private Handler getBackgroundHandler() {
if (mBackgroundHandler == null) {
HandlerThread thread = new HandlerThread("background");
thread.start();
mBackgroundHandler = new Handler(thread.getLooper());
}
return mBackgroundHandler;
}
private CameraView.Callback mCallback
= new CameraView.Callback() {
@Override
public void onCameraOpened(CameraView cameraView) {
Log.d(TAG, "onCameraOpened");
}
@Override
public void onCameraClosed(CameraView cameraView) {
Log.d(TAG, "onCameraClosed");
}
@Override
public void onPictureTaken(CameraView cameraView, final byte[] data) {
Log.d(TAG, "onPictureTaken " + data.length);
Toast.makeText(cameraView.getContext(), R.string.picture_taken, Toast.LENGTH_SHORT)
.show();
}
};
}
那么我的这个githublink的Cameraview文件就在这里 https://github.com/quicklearner4991/Camera2/blob/master/CameraView 从这个 link 获取资源,我不能 post 在这里回答那么长,我已经使用不容易获得的闪光选项编辑了 camera2 的代码。 Link 使用此 repo 下载资源
https://github.com/googlesamples/android-Camera2Basic xml 的观看次数
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorgrey">
<com.google.android.cameraview.CameraView
android:id="@+id/camera"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:adjustViewBounds="true"
android:background="@android:color/black" />
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true" />
<ImageButton
android:id="@+id/take_picture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="20dp"
android:background="#0000"
app:srcCompat="@drawable/ic_take_pic" />
<ImageButton
android:id="@+id/btn_switch_camera"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_margin="20dp"
android:background="#0000"
app:srcCompat="@drawable/ic_reset_camera" />
<ImageButton
android:id="@+id/btn_flash_onOff"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_margin="20dp"
android:background="#0000"
app:srcCompat="@drawable/ic_flash_off" />
</RelativeLayout>
你只需要创建一个 CameraCaptureSession
,然后调用它的 capture()
或 setRepeatingRequest()
方法来发送 CaptureRequest
给它,所以它不是一个昂贵的过程根本上,唯一的 CameraCaptureSession
是在您打开 CameraDevice
并开始预览时创建的,CameraCaptureSession
将处理您发送的所有 CaptureRequest
并执行它最好不要丢帧。
查看 google/cameraview 中的代码:
void setFlash(int flash) {
if (mFlash == flash) {
return;
}
int saved = mFlash;
mFlash = flash;
if (mPreviewRequestBuilder != null) {
updateFlash(); // Set the new flash settings
if (mCaptureSession != null) {
try {
mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(),
mCaptureCallback, null);
} catch (CameraAccessException e) {
mFlash = saved; // Revert
}
}
}
}
void updateFlash() {
switch (mFlash) {
case Constants.FLASH_OFF:
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON);
mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_OFF);
break;
case Constants.FLASH_ON:
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_OFF);
break;
case Constants.FLASH_TORCH:
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON);
mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_TORCH);
break;
case Constants.FLASH_AUTO:
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_OFF);
break;
case Constants.FLASH_RED_EYE:
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE);
mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_OFF);
break;
}
}
更改会话的重复请求根本不昂贵。它不会重新创建会话(只有当您再次调用 CameraDevice.createCaptureSession 时才会发生),并且通常不会因为更改这样的设置而出现任何故障。更改重复请求正是您打开相机设备时打开手电筒的方式。
请注意,在重复请求中将闪光模式设置为 SINGLE 可能不是您想要的;你可能想要 TORCH 的闪光模式(为了连续平滑的手电筒,而不是频闪主闪光灯闪光),你还需要确保你的 CONTROL_AE_MODE 设置不是 AE_MODE_ON_FLASH* 模式之一;否则相机设备的自动曝光例程由闪光灯控制,FLASH_MODE 设置将被忽略。