如何在不建立新会话的情况下在 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 设置将被忽略。