Camera:前后闪光灯

news/2024/12/13 4:14:35/

    手机Camera的前后摄闪光灯虽然是同一个feature,但是从软硬件任意角度来讲,都有着很大的区别。后摄闪光灯是有专门的物理空间支持的,就是手机的手电筒物理控件,当后摄开启闪光灯拍照时候,会触发俩次打闪,第一次会给200mA左右的脉冲电流,称为预闪,为的是获取当前环境下的亮度等信息;第二次会给800mA甚至更强的脉冲电流,称为主闪。反观前摄闪光啥也没有,仅仅靠屏幕补光来实现效果,视觉效果就是一张糊上去的白色的View。下面从代码的角度来研究一下闪光灯效果的实现。

 

1.后摄闪光应用层控件

    应用层简单点就是添加闪光灯控件,下发闪光灯不同模式下相对应的tag,拍照时唤起底层的俩次打闪并进行处理。

@Overridepublic void init(IApp app,ICameraContext cameraContext,ISettingManager.SettingController settingController) {super.init(app, cameraContext, settingController);String value = mDataStore.getValue(FLASH_KEY, FLASH_DEFAULT_VALUE, getStoreScope());setValue(value);if (mFlashViewController == null) {mFlashViewController = new FlashViewController(this, app);}mStatusMonitor.registerValueChangedListener(KEY_CSHOT, mStatusChangeListener);// [Add for CCT tool] Receive keycode and enable/disable flash @{mKeyEventListener = mFlashViewController.getKeyEventListener();mApp.registerKeyEventListener(mKeyEventListener, IApp.DEFAULT_PRIORITY);IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);mActivity.registerReceiver(mBatteryLevelReceiver, filter);// @}}@Overridepublic void addViewEntry() {//前摄闪光灯模块用其他的控件处理if (!FRONT_CAMERA_ID.equals(((CameraAppUI)mAppUi).getCurrentCameraId())) {mFlashViewController.addQuickSwitchIcon();//滤镜模式下的闪光灯互斥处理if(isFilterOn()) {mFlashViewController.showQuickSwitchIcon(false);} else {mFlashViewController.showQuickSwitchIcon(getEntryValues().size() > 1);}//低电量情况闪光灯灰置处理if (mApp.hasLowBattery()) {onFlashValueChanged(FLASH_OFF_VALUE);if (mFlashViewController.mFlashEntryView != null) {mFlashViewController.mFlashEntryView.setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY);}} else {if (mFlashViewController.mFlashEntryView != null) {mFlashViewController.mFlashEntryView.setColorFilter(Color.WHITE, PorterDuff.Mode.MULTIPLY);}}} else {mFlashViewController.removeQuickSwitchIcon();mFlashViewController.showQuickSwitchIcon(false);}}private ImageView initFlashEntryView() {Activity activity = mApp.getActivity();//相机界面显示的闪光灯图标RotateImageView view = (RotateImageView) activity.getLayoutInflater().inflate(R.layout.flash_icon, null);view.setOnClickListener(mFlashEntryListener);//点击闪光灯图标显示出的隐藏选项mFlashIndicatorView = (RotateImageView) activity.getLayoutInflater().inflate(R.layout.flash_indicator, null);return view;}/*** This listener used to monitor the flash quick switch icon click item.*/private final View.OnClickListener mFlashEntryListener = new View.OnClickListener() {public void onClick(View view) {//低电量不可点击final int lowBatteryWarningLevel = CameraUtil.getLowBatteryLevel(mApp);if (mFlash.barrtyLevel <= lowBatteryWarningLevel) {OnScreenHint.makeText(mApp.getActivity(), mApp.getActivity().getString(R.string.low_battery_disable_flash)).showToast();updateFlashEntryView(FLASH_OFF_VALUE);mFlash.onFlashValueChanged(FLASH_OFF_VALUE);mFlashEntryView.setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY);return;}//支持的闪光灯模式<=1不可点击if (mFlash.getEntryValues().size() <= 1) {return;}//点击闪光灯图标的处理if (mFlash.getEntryValues().size() > FLASH_ENTRY_LIST_SWITCH_SIZE) {initializeFlashChoiceView();updateChoiceView();//显示闪光灯隐藏选项mApp.getAppUi().showQuickSwitcherOption(mOptionLayout);} else {String value = mFlash.getEntryValues().get(FLASH_ENTRY_LIST_INDEX_0);if (value.equals(mFlash.getValue())) {value = mFlash.getEntryValues().get(FLASH_ENTRY_LIST_INDEX_1);}updateFlashEntryView(value);// Flash indicator no need to show now,would be enable later// updateFlashIndicator(value);mFlash.onFlashValueChanged(value);}}};//闪光灯隐藏选项的点击事件处理,支持on/auto/off三种选项private View.OnClickListener mFlashChoiceViewListener = new View.OnClickListener() {@Overridepublic void onClick(View view) {String value = "";if (mFlashAutoIcon == view) {value = FLASH_AUTO_VALUE;} else if (mFlashOnIcon == view) {value = FLASH_ON_VALUE;} else {value = FLASH_OFF_VALUE;}mApp.getAppUi().setHideQuickSwitchOptionByUser(true);mApp.getAppUi().hideQuickSwitcherOption();updateFlashEntryView(value);// Flash indicator no need to show now,would be enable later// updateFlashIndicator(value);mFlash.onFlashValueChanged(value);}};

    应用层的UI设计没有技术含量,关键在于应用层和HAL之前的tag下发管理这一块,简单中隐藏着一丝不简单,老是让你有着被干扰的不痛快的感觉。

@Overridepublic void configCaptureRequest(CaptureRequest.Builder captureBuilder) {if (captureBuilder == null) {LogHelper.d(TAG, "[configCaptureRequest] captureBuilder is null");return;}if (mIsFlashSupported || mIsPanelFlashSupported) {updateFlashMode();captureBuilder.set(CaptureRequest.FLASH_MODE, mFlashMode);LogHelper.i(TAG, "[configCaptureRequest], mFlashMode = " + mFlashMode);}}private void updateFlashMode() {if (Flash.FLASH_ON_VALUE.equalsIgnoreCase(mFlash.getValue())) {if (mNeedChangeFlashModeToTorch ||mFlash.getCurrentModeType() == ICameraMode.ModeType.VIDEO) {mFlashMode = CameraMetadata.FLASH_MODE_TORCH;return;}// AE mode -> ON_ALWAYS_FLASH, flash mode value doesn't affectmFlashMode = CameraMetadata.FLASH_MODE_OFF;return;}if (Flash.FLASH_AUTO_VALUE.equalsIgnoreCase(mFlash.getValue())) {if (mNeedChangeFlashModeToTorch) {mFlashMode = CameraMetadata.FLASH_MODE_TORCH;LogHelper.d(TAG, "[updateFlashMode] change flash mode to torch");return;}// AE mode -> ON_AUTO_FLASH, flash mode value doesn't affectmFlashMode = CameraMetadata.FLASH_MODE_OFF;return;}// flash -> off, must need AE mode -> onmFlashMode = CameraMetadata.FLASH_MODE_OFF;}

    在应用层对Flash的Tag管理中需要特别注意注释里面的AE mode -> ON_ALWAYS_FLASH 以及 AE mode -> ON_AUTO_FLASH, 有一说一,我对MTK原生Camera apk 里面长按屏幕触发AE/AF锁模式,保持对焦以及聚光状态的feature理解不太深,一直没有明白为什么触发AE mode要强行僭越Flash的管控机制,一直保持Flash开启状态;真心深恶痛觉以及有点烦这个设计,你对焦你聚光你就聚呗,一定时间的闪光灯开启然后去拍照是可以理解的,不过往往是提示框消失很久了,AE mode的状态还是保持长按触发不改变,导致用户拍照时候即使闪光灯未开启也有闪光灯效果。我认为,AE mode并不是老大,是否打闪的权利完全由Flash自己主宰,所以经我手的Camera apk对于这方面Flash和AE的管控, 我都给改了。

修改文件:ExposureCaptureRequestConfigure.java

private void updateAeMode() {if (mAEMode == CameraMetadata.CONTROL_AE_MODE_ON_EXTERNAL_FLASH) {return;}setOriginalAeMode();}private void setOriginalAeMode() {String flashValue = mExposure.getCurrentFlashValue();if (FLASH_ON_VALUE.equalsIgnoreCase(flashValue)) {if (mNeedChangeFlashModeToTorch ||mExposure.getCurrentModeType() == ICameraMode.ModeType.VIDEO) {mAEMode = CameraMetadata.CONTROL_AE_MODE_ON;return;}//AE mode强制Flash效果mAEMode = CameraMetadata.CONTROL_AE_MODE_ON_ALWAYS_FLASH;// AE mode -> ON_ALWAYS_FLASH, flash mode value doesn't affectreturn;}if (FLASH_AUTO_VALUE.equalsIgnoreCase(flashValue)) {if (mNeedChangeFlashModeToTorch) {mAEMode = CameraMetadata.CONTROL_AE_MODE_ON;return;}//AE mode强制Flash效果AE mode -> ON_AUTO_FLASH, flash mode value doesn't affect//mAEMode = CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH;return;}// flash -> off, must need AE mode -> onmAEMode = CameraMetadata.CONTROL_AE_MODE_ON;}

 

2.后摄闪光灯拍照之后的底层处理

    时间关系,这一部分我就不拎出来仔细讲了,之后应该会再出一版Flash有关的内容讲一下开启闪光灯功能底层拍照流程走的代码,以及为啥AE mode就能主宰Flash Mode的底层设定;甚至可能会通过代码研究一下Camera Tunning那一块根据预闪获得的环境参数,配合一定算法咋么调整拍出来的照片的RGB参数。今天就先根据一份log看一下底层的处理。

evaluteCaptureSetting是根据应用层下发的CaptureRequest得到的拍照要处理的feature;strategyMultiFramePlugin是拍照处理哥哥响应的多帧feature,很多feature并不是简单的拍一张图片,涉及到多帧图像的处理;updateCaptureDummyFrames这边就是拍照对多帧图片的一个处理,post了几张之类的,isFlashOn的标志位就是HAL层直接根据应用层下发的Flash mode、AE mode得到的相对应的值。log里面所有显示的内容都是可以通过代码来层层获取相对的行为以及处理方法。庞大而精巧,希望以后的时间里能抽丝剥茧的将这些都拎出来晒晒。

 

3.前摄闪光灯补光处理

     应用层对前摄闪光灯的控件管理类似后摄,由于前摄补光功能完全是由应用层完成的,所以不涉及tag的下发,应用层要简单很多。主要内容就是补光功能,代码如下:

 @Overrideprotected boolean doShutterButtonClick() {if (storageReady && isDeviceReady && mIsResumed) {//开起补光动画,白色的mCoreView//trigger capture animationstartCaptureAnimation();updateModeDeviceState(MODE_DEVICE_STATE_CAPTURING);mIDeviceController.updateGSensorOrientation(mIApp.getGSensorOrientation());if ("on".equals(getFrontFlashValue()) && mIApp.getAppUi().getCurrentCameraId().equals("1")) {mApp.getActivity().runOnUiThread(new Runnable() {@Overridepublic void run() {((CameraActivity)mIApp).setAlpha(0.8f);}});//开始拍照抓取补光效果的帧takeFrontFlashPicture();} else {mIDeviceController.takePicture(this);}}return true;}public void animationStart(AnimationType type, IAppUi.AnimationData data) {LogHelper.d(TAG, "Start animation type: " + type);switch (type) {case TYPE_CAPTURE:if ("on".equals(getFrontFlashValue()) && mAppUI.getCurrentCameraId().equals("1") && mAppUI.getVideoState() != VideoState.STATE_RECORDING) {if("Z3".equals(MODEL) || "Z4".equals(MODEL)){mCoverView.setBackgroundColor(Color.parseColor("#fff0e6"));}else{//将View背景绘成白色,便于补光mCoverView.setBackgroundColor(Color.WHITE);}} else {mCoverView.setBackgroundColor(Color.BLACK);}mCoverView.setVisibility(View.VISIBLE);//开始补光的动作playCaptureAnimation();break;default:break;}}private void playCaptureAnimation() {LogHelper.d(TAG, "playCaptureAnimation +");AnimatorSet captureAnimation =(AnimatorSet) AnimatorInflater.loadAnimator(mApp.getActivity(),R.animator.cature_anim);if ("on".equals(getFrontFlashValue()) && mAppUI.getCurrentCameraId().equals("1") && mAppUI.getVideoState() != VideoState.STATE_RECORDING) {captureAnimation =(AnimatorSet) AnimatorInflater.loadAnimator(mApp.getActivity(),R.animator.cature_anim_fill);}captureAnimation.setTarget(mCoverView);//补光动作结束事件的监听captureAnimation.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {super.onAnimationEnd(animation);mCoverView.setVisibility(View.GONE);mApp.getActivity().runOnUiThread(new Runnable() {@Overridepublic void run() {((CameraActivity)mApp).setAlpha(1.0f);}});}});captureAnimation.start();LogHelper.d(TAG, "playCaptureAnimation -");}

 


http://www.ppmy.cn/news/295369.html

相关文章

Android如何打开闪光灯

Android如何打开闪光灯 在android中打开闪光灯的方法有两种&#xff0c;一种是获取硬件服务&#xff0c;通过反射的方式来操作闪光灯。另外一种是获得Camera对象&#xff0c;通过设置Camera的参数来操作闪光灯。一下是一个操作闪光灯的工具类&#xff1a;实现了两种方式操作闪…

最简单闪光灯电路图

2个三极管控制灯闪烁 1. 2个三极管控制灯闪烁最简单闪光灯电路图&#xff08;二&#xff09;最简单闪光灯电路图&#xff08;三&#xff09;最简单闪光灯电路图&#xff08;四&#xff09;最简单闪光灯电路图&#xff08;五&#xff09;最简单闪光灯电路图&#xff08;六&#…

nested exception is java.lang.NoClassDefFoundError

出现这种问题&#xff0c;一般都是jar有问题&#xff0c;排查是哪个jar包&#xff0c;重新导入maven仓库一下就行了&#xff0c;有的时候需要把原来仓库里的包删掉&#xff0c;重新打包&#xff0c;有的时候要切换分支&#xff0c;到其他分支打包。 打包时候没有打进去&#xf…

世界研发管理组织在美国成立,中国籍研发管理专家江新安当选总干事

World R&D Management Organization世界研发管理组织&#xff08;WRDMO&#xff09;由来自世界各地的研发管理研究组织&#xff0c;创新技术研究机构&#xff0c;院校以及研发管理咨询机构联合发起。是一个具有开放性&#xff0c;无党派性&#xff0c;非营利性的国际先进研…

完整的销售过程会有哪些业务活动产生呢

一次完整的销售管理过程&#xff0c;按照售前、售中和售后三个阶段划分&#xff0c;通常会包括以下环节和业务活动&#xff1a; 售前阶段&#xff1a; 需求调研&#xff1a;了解客户需求&#xff0c;收集相关信息。销售策划&#xff1a;制定销售计划、定价策略、促销方案等。…

05. Web大前端时代之:HTML5+CSS3入门系列~H5 多媒体系

1.引入 概述 音频文件或视频文件都可以看做是一个容器文件&#xff08;类似于压缩的zip&#xff09; 编解码器就是读取特定的容器格式&#xff0c;对其中的音频与视频轨进行解码&#xff0c;然后实现播放 解码器 解码器&#xff08;decoder&#xff09;&#xff0c;是一种…

从小白到大神之路之学习运维第37天---第三阶段---mysql数据库之拓展知识

拓展知识 目录 一、MySQL数据库目录结构以及存放位置 二、MySQL Enterprise Backup 三、MySQL读写分离器 四、进程和线程 五、CentOS 7 中配置静态 IP 一、MySQL数据库目录结构以及存放位置 1. 数据库存储目录&#xff1a;MySQL数据库的数据文件存储在指定的数据目录下。M…

移动端APP组件化架构实践

作者&#xff1a;何乐乐 前言 对于中大型移动端APP开发来讲&#xff0c;组件化是一种常用的项目架构方式。个人最近几年在工作项目中也一直使用组件化的方式来开发&#xff0c;在这过程中也积累了一些经验和思考。主要是来自在日常开发中使用组件化开发遇到的问题以及和其他开…