[Android] [SnapdragonCamera] 单摄(横屏)阶段总结

2024-09-07 13:28

本文主要是介绍[Android] [SnapdragonCamera] 单摄(横屏)阶段总结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        在研高通平台的单摄项目中遇到了很多适配问题,做一下初步的总结,为今后遇到相似的问题,提供参考方案。
        

1. 横屏设置相机预览显示不正常         

     1.1问题现象

             

        1.2分析与解决

             骁龙相机默认的预览方向是“portrait”。在横屏设备上显示的时候就会出现上面效果。实际操作中这样的预览效果很不友好,所以针对这种情况,需要作出如下修改: 我们以XXX-A项目为例,XXX-A的代码是共用V660C的代码,其适用的范围包含单摄像头和双摄像头的机器。所以不能直接修改AndroidManifest.xml的“screenOrientation”参数。需要做一个判断,所以其具体修改下:
a.先移除AndroidManifest.xml中的固定设置:

b. 再在主Activity: CameraActivity.java中添加相关的判断及设置:

   @Overridepublic void onCreate(Bundle state) {super.onCreate(state);if (Build.isLandScapeDevice()) {setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);} else {setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);}if (PersistUtil.isTraceEnable())Trace.beginSection("CameraActivity onCreate");try {//Print version info hereString versionName = getPackageManager().getPackageInfo(getPackageName(), 0).versionName;Log.d(TAG, "snapdragoncamera_version: " + versionName);} catch (PackageManager.NameNotFoundException e) {e.printStackTrace();}// Check if this is in the secure camera mode.....}

如果是屏幕横置的机器就将预览的Orientation设置成“LANDSCAPE”。如果是BBB的单一项目的代码可以将AndroidManifest.xml中的参数直接修改成“landscape”即可。
修改后的效果:

相机的菜单和拍摄按钮都调整到屏幕的下方。


2. 预览画面和菜单显示不匹配

        问题现象:

   

相机打开的预览画面是“Photo”,但是在下方的模式菜单的选项中,选中的却是“ProMode”. 造成了预览图像和菜单设置的不匹配的现象。

        分析与解决:

         造成这个问题的原因是:由于双摄像头项目在预览画面会有四个选项模式:“Video/HFR/Photo/ProMode”。而单摄项目在前置摄像头的预览时只有三个选项模式:“Video/Photo/ProMode”. 追踪Code,定位到 Camera2ModeAdapter.java 中是设置这个模式的位置:

public class Camera2ModeAdapter extends RecyclerView.Adapter<Camera2ModeAdapter.ViewHolder> {private List<String> mModeList;private int mSelectedPos = 2;  //设置默认的模式  private OnItemClickListener mOnItemClickListener;public Camera2ModeAdapter(List<String> list) {this.mModeList = list;}public void setOnItemClickListener(OnItemClickListener listener) {this.mOnItemClickListener = listener;}@Overridepublic ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.camera2_mode_item,parent, false);ViewHolder viewHolder = new ViewHolder(view);return viewHolder;}@Overridepublic void onBindViewHolder(ViewHolder holder, int position) {holder.mCameraModeText.setText(mModeList.get(position));holder.mCameraModeText.setSelected(mSelectedPos == position); //选中预览模式holder.mCameraModeText.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (mOnItemClickListener.onItemClick(position) >= 0) {mSelectedPos = position;notifyDataSetChanged();}}});}@Overridepublic int getItemCount() {return mModeList.size();}public interface OnItemClickListener {int onItemClick(int mode);}public void setSelectedPosition(int position) {mSelectedPos = position;notifyDataSetChanged(); }class ViewHolder extends RecyclerView.ViewHolder{protected TextView mCameraModeText;public ViewHolder(View itemView) {super(itemView);mCameraModeText = (TextView) itemView.findViewById(R.id.mode_text);}}
}

mSelectedPos = 2 在四个模式下对应的是“Photo”, 但是在只有三个模式的情况下对应的mode name则是“ProMode”.

继续追Code,发现是CaptureUI.java中使用到这个adapter:

 mCameraModeAdapter = new Camera2ModeAdapter(mModule.getCameraModeList());mCameraModeAdapter.setOnItemClickListener(mModule.getModeItemClickListener());mModeSelectLayout.setAdapter(mCameraModeAdapter);

决定“mModeSelectLayout”中显示的预览模式是“mModule.getCameraModeList()” 其对应的Code则是在CaptureModule.java

public List<String> getCameraModeList() {ArrayList<String> cameraModes = new ArrayList<>();for (SceneModule sceneModule : mSceneCameraIds) {cameraModes.add(mSelectableModes[sceneModule.mode.ordinal()]);}return cameraModes;
}

预设的模式:

private String[] mSelectableModes = {"Video", "HFR", "Photo", "Bokeh", "SAT", "ProMode"};
...
public enum CameraMode {VIDEO,HFR,DEFAULT,RTB,SAT,PRO_MODE}

在初始化的时候,将六种默认的模式都加载到 mSceneCameraIds 中

for (int i = 0; i < mSelectableModes.length; i++) {module = new SceneModule();module.mode = CameraMode.values()[i];mSceneCameraIds.add(module);}

DEFAULT对应的模式则是:“Photo”。接下来程序会根据不同的情况进行筛选:

在initCameraIds()的时候,会默认将所有模式的删除属性设置成“true”:

private void initCameraIds() {CameraManager manager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);boolean isFirstDefault = true;boolean[] removeList = new boolean[mSelectableModes.length];for (int i = 0; i < mSelectableModes.length; i++) {removeList[i] = true;}....for (int i = 0; i < cameraIdList.length; i++) {String cameraId = cameraIdList[i];mCameraId[i] = cameraId;CameraCharacteristics characteristics;try {characteristics = manager.getCameraCharacteristics(cameraId);} catch (CameraAccessException e) {e.printStackTrace();continue;}isFirstDefault = setUpLocalMode(i, characteristics, removeList,isFirstDefault, cameraId);}}       

setUpLocalMode 方法中会根据预设的情况进行初步的筛选:

private boolean setUpLocalMode(int cameraId, CameraCharacteristics characteristics,boolean[] removeList, boolean isFirstDefault, String physicalId) {Byte type = 0;try {type = characteristics.get(logical_camera_type);} catch (IllegalArgumentException e) {Log.e(TAG, "setUpLocalMode no vendorTag logical_camera_type:" + logical_camera_type);}Log.d(TAG,"init cameraId " + cameraId + " | logical_camera_type = " + type +" | physical id = " + physicalId);int facing = characteristics.get(CameraCharacteristics.LENS_FACING);switch (type) {case TYPE_DEFAULT:// defaultremoveList[CameraMode.DEFAULT.ordinal()] = false;removeList[CameraMode.VIDEO.ordinal()] = false;removeList[CameraMode.PRO_MODE.ordinal()] = false;if (facing == CameraCharacteristics.LENS_FACING_FRONT) {CaptureModule.FRONT_ID = cameraId;mSceneCameraIds.get(CameraMode.DEFAULT.ordinal()).frontCameraId = cameraId;mSceneCameraIds.get(CameraMode.VIDEO.ordinal()).frontCameraId = cameraId;mSceneCameraIds.get(CameraMode.HFR.ordinal()).frontCameraId = cameraId;mSceneCameraIds.get(CameraMode.PRO_MODE.ordinal()).frontCameraId = cameraId;} else {...}... return isFirstDefault;}

上面部分代码将“Photo/Video/ProMode”三个模式的删除属性设置成“false"

最终确定通过下面的筛选方式将需要显示的Mode存放在mSceneCameraIds 列表中

for (int i = 0; i<mSceneCameraIds.size(); i++){Log.i("WKS","START----->mSceneCameraIds[" + i + "]: " + mSceneCameraIds.get(i).mode);}  for (int i = 0; i < removeList.length; i++) {if (!removeList[i]) {continue; //"Photo/Video/ProMode"在上面已经将改属性设置成false}for (SceneModule sceneModule : mSceneCameraIds) {if (sceneModule.mode.ordinal() == i) {mSceneCameraIds.remove(sceneModule); //不需要的Mode在此从mSceneCameraIds中移除break;}}}}   for (int i = 0; i<mSceneCameraIds.size(); i++){Log.i("WKS","END----->mSceneCameraIds[" + i + "]: " + mSceneCameraIds.get(i).mode);}  

通过上面的流程操作,可以看到筛选前mSceneCameraIds里存放的对象是:“VIDEO/HFR/DEFAULT/RTB/SAT/PRO_MODE”

筛选后则是:

“VIDEO/DEFAULT/PRO_MODE”

3. 强制断电后图片无法保存

        问题分析:

在突然断电的情况下,Android设备可能会丢失已拍摄的图片。这是因为设备的电源管理策略和文件系统缓存机制可能导致图片数据未能及时写入存储设备。

        问题解决:

在每次拍照完成后进行一次数据同步。以确保拍摄的图片和视频有同步到储存空间里:

try {Runtime.getRuntime().exec("sync data/misc/apexdata/com.android.media/");}  catch (Exception e) {Log.e(TAG, "Run error:" + e);}

在Android系统中,sync data/misc/apexdata/com.android.media/这个命令的含义是同步com.android.media模块的数据。com.android.media模块通常包含与媒体处理相关的库和资源,例如音频和视频编解码器、媒体播放服务等。

这个问题和单摄/横屏没有直接关系。在其它设备上也能复制出问题,但是由于目前在研的横屏设备没有电池,是通过AC直接供电,操作的时候如果直接移除电源就会比较容易复制出此类的现象。

4. 滑动屏幕不能切换相机的模式

        问题分析:

复制现象的时候发现,点击模式按钮进行切换的时候是正常操作的。结合问题描述的状况看,应该是滑动的时候,程序处理出现了问题。那首先我们需要定位到程序里滑动功能的位置,看其是如何定义和操作的。然后再根据其提供的线索去追踪到模式功能切换的实现位置。

        解决方案:

确定实现滑动的代码在PreviewGestures.java: 在其onScroll的方法,定义了向左isLeftSwipe,向右isRightSwipe以及上下滑动isUpSwipe的行为。

@Overridepublic boolean onScroll (MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {if (e1 == null) {// e1 can be null if for some cases.return false;}if (mZoomOnly || mMode == MODE_ZOOM) return false;int deltaX = (int) (e1.getX() - e2.getX());int deltaY = (int) (e1.getY() - e2.getY());if((Math.abs(deltaX) > 40 || Math.abs(deltaY) > 40) && Math.abs(e1.getY()) < 1800) {int orientation = 0;if (mCaptureUI != null)orientation = mCaptureUI.getOrientation();if (isLeftSwipe(orientation, deltaX, deltaY)) {waitUntilNextDown = true;if (mCaptureUI != null)mCaptureUI.swipeCameraMode(-1);if (mMultiCameraUI != null)mMultiCameraUI.swipeCameraMode(-1);return true;}if (isRightSwipe(orientation, deltaX, deltaY)) {waitUntilNextDown = true;if (mCaptureUI != null)mCaptureUI.swipeCameraMode(1);if (mMultiCameraUI != null)mMultiCameraUI.swipeCameraMode(1);return true;}if (isUpSwipe(orientation, deltaX, deltaY) ||isDownSwipe(orientation, deltaX, deltaY)) {if (Camera.getNumberOfCameras() == 1 || mModelName.equals(Build.MODEL)) {return false;} else {if (e1.getY() < 200) {return false;}waitUntilNextDown = true;if (mCaptureUI != null)mCaptureUI.switchFrontBackCamera();return true;}}}return false;}

继而追踪到更新UI的位置是在CaptureUI.java的 swipeCameraMode()方法里:

public void swipeCameraMode(int move) {Log.i("TD","---->mModule.getCameraModeSwitcherAllowed(): " + mModule.getCameraModeSwitcherAllowed());  if (mIsVideoUI || !mModule.getCameraModeSwitcherAllowed() ||mModule.getCurrentIntentMode() != CaptureModule.INTENT_MODE_NORMAL) { return;}Log.i("TD","---->mModule.getCurrentModeIndex(): " + mModule.getCurrentModeIndex());int index = mModule.getCurrentModeIndex() + move;int modeListSize = mModule.getCameraModeList().size();if (index >= modeListSize || index == -1) {return;}int mode = index % modeListSize;mModule.setCameraModeSwitcherAllowed(false);mCameraModeAdapter.setSelectedPosition(mode);mModeSelectLayout.smoothScrollToPosition(mode);mModule.selectCameraMode(mode);
}       

在swipeCameraModed方法的关键位置加上log. 发现 mModule.getCurrentModeIndex()的初始值是"2".

此时如果向右滑动,move = 1,index的值就会是“3”,方法直接return. 此时如果向左滑动,move = -1,index的值为“1”。而默认的模式对应的index也是为“1”。这就造成了预览画面没有发生变化,那就无法走到下面代码的方法里去设置mCameraModeSwitcherAllowed值为“true”。而swipeCameraMoed 方法里mCameraModeSwitcherAllowed的值已经设置成“false”.后续继续滑动的话,直接就return出来了。这就造成了滑动无作用的现象。

CaptureModule.java:

private void createSession(final int id) {Log.d(TAG, "createSession,id: " + id + ",mPaused:" + mPaused + ",mCameraOpened:" + !mCameraOpened[id] + ",mCameraDevice:"+ (mCameraDevice[id] == null));if (mPaused || !mCameraOpened[id] || (mCameraDevice[id] == null)) return;List<Surface> list = new LinkedList<Surface>();mState[id] = STATE_PREVIEW;mControlAFMode = CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE;try {// We set up a CaptureRequest.Builder with the output Surface.mPreviewRequestBuilder[id] = getRequestBuilder(id);mPreviewRequestBuilder[id].setTag(id);CameraCaptureSession.StateCallback captureSessionCallback =new CameraCaptureSession.StateCallback() {@Overridepublic void onConfigured(CameraCaptureSession cameraCaptureSession) {if (mPaused || null == mCameraDevice[id] ||cameraCaptureSession == null) {return;}Log.i(TAG, "cameracapturesession - onConfigured "+ id);setCameraModeSwitcherAllowed(true);// When the session is ready, we start displaying the preview....
}                            

定位到造成问题的原因是初始状态下,由于是单个摄像头,mCurrentModeIndex 的值不能直接等于CameraMode数组里Photo对应的坐标。所以需要在下面的代码里作出如下处理:如果是单个摄像头的设备,需要将默认的mCurrentModeIndex 值设置成“1”。

if (mCurrentSceneMode == null) {int index = mIntentMode == INTENT_MODE_VIDEO ?CameraMode.VIDEO.ordinal() : CameraMode.DEFAULT.ordinal(); // CameraMode.DEFAULT.ordinal()的值是2if ((cameraIdList.length == 1 || mModelName.equals(Build.MODEL)) && mIntentMode != INTENT_MODE_VIDEO) {mCurrentModeIndex = mNextModeIndex = 1;} else {mCurrentModeIndex = mNextModeIndex = index;}mCurrentSceneMode = mSceneCameraIds.get(index);
}public int getCurrentModeIndex() {return mCurrentModeIndex;
}

在后续的操作中,会通过下面的方法将index的值传递给mCurrentModeIndex

public void setNextSceneMode(int index) {mNextModeIndex = index;
}private void reinitSceneMode() {mCurrentSceneMode = mSceneCameraIds.get(mNextModeIndex);mCurrentModeIndex = mNextModeIndex;CURRENT_MODE = mCurrentSceneMode.mode;CURRENT_ID = mCurrentSceneMode.getNextCameraId(CURRENT_MODE);Log.d(TAG, "reinitSceneMode: CURRENT_ID :" + CURRENT_ID);
}

5. 相机Promode下UI显示异常

        问题现象:

在横屏机器上,ProMode的选项发生了偏移

        分析与解决:

根据Promode中的按钮控件找到对应的布局文件pro_mode_layout.xml, 再通过布局文件的引用找到对应的java文件:src/com/android/camera/ui/OneUICameraControls.java 

在其初始化的时候有对promode的控件做了一些位置的调整:

private void initializeProMode(boolean promode) {if (!promode) {mProMode.setMode(ProMode.NO_MODE);mProModeLayout.setVisibility(INVISIBLE);return;}mProModeLayout.setVisibility(VISIBLE);mProModeLayout.setY(mHeight - mBottom - mProModeLayout.getHeight() - 48);
}

通过上述代码可以知晓其是对mProModeLayout 的竖直方向上做了一些调整。而对于横屏的机器遇到的问题是水平方向上发生了偏移。根据机器在水平方向上的偏移量(这边需要根据不同设备的情况作出相应的数据调整)作出相应的修改:

private void initializeProMode(boolean promode) {if (!promode) {mProMode.setMode(ProMode.NO_MODE);mProModeLayout.setVisibility(INVISIBLE);return;}mProModeLayout.setVisibility(VISIBLE);if (Camera.getNumberOfCameras() == 1 || mModelName.equals(Build.MODEL)) {if (density >= 280){mProModeLayout.setY(mHeight - mBottom - mProModeLayout.getHeight() + 120);} else {mProModeLayout.setY(mHeight - mBottom - mProModeLayout.getHeight());}mProModeLayout.setX(242);} else {mProModeLayout.setY(mHeight - mBottom - mProModeLayout.getHeight() - 48);}}

上面部分的代码是由于另外一个需求:

Promode的选项和功能部分会发生重叠,影响实际的功能操作。所以需要对竖直方向上坐标位置进行相应的调整。这个还需要考虑到Display Size 的调整会对其控件大小和布局的影响,所以要根据不同的density进行调整规划。 修改后的效果如下:

6.相机拍照/摄像时没有声音

        问题分析:

抓取拍照/录像时的即时log:

8-02 19:43:09.941 V/SnapCam_CaptureUI( 3687): surfaceChanged: width =1280, height = 720
08-02 19:43:09.943 E/SoundPool( 3687): error loading /product/media/audio/ui/VideoRecord.ogg
08-02 19:43:09.943 E/SoundPool( 3687): error loading /system/media/audio/ui/VideoRecord.ogg
08-02 19:43:09.943 E/MediaActionSound( 3687): load() error loading sound: 2
08-02 19:43:09.944 E/SoundPool( 3687): error loading /product/media/audio/ui/VideoStop.ogg
08-02 19:43:09.944 E/SoundPool( 3687): error loading /system/media/audio/ui/VideoStop.ogg
08-02 19:43:09.944 E/MediaActionSound( 3687): load() error loading sound: 3
08-02 19:43:09.944 E/SoundPool( 3687): error loading /product/media/audio/ui/camera_focus.ogg
08-02 19:43:09.945 E/SoundPool( 3687): error loading /system/media/audio/ui/camera_focus.ogg
08-02 19:43:09.945 E/MediaActionSound( 3687): load() error loading sound: 1
08-02 19:43:09.945 E/SoundPool( 3687): error loading /product/media/audio/ui/camera_click.ogg
08-02 19:43:09.945 E/SoundPool( 3687): error loading /system/media/audio/ui/camera_click.ogg
08-02 19:43:09.945 E/MediaActionSound( 3687): load() error loading sound: 0
08-02 19:43:09.946 D/SnapCam_CaptureModule( 3687): Chosen postproc filter id : 0

通过上面log可以看出拍照/摄像时无声的根本原因是加载音频文件的时候出现了异常。进入系统内部检查是否存在这些音频文件:

可以见当前设备的目标位置并未存放相关的资源。 正常可播放声音的机器在当前目录下会存放相关资源:

        解决方案:

找到音频文件的存放位置,将相关资源Copy到系统中:

$(LOCAL_PATH)/effects/ogg/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \

另外有些机器考虑到本身的配置比较低,加上这些资源之后可能会影响到性能。会主动移除这些功能,本案最后结合自身情况就选择不加入相关的音频资源。

这篇关于[Android] [SnapdragonCamera] 单摄(横屏)阶段总结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1145181

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

git使用的说明总结

Git使用说明 下载安装(下载地址) macOS: Git - Downloading macOS Windows: Git - Downloading Windows Linux/Unix: Git (git-scm.com) 创建新仓库 本地创建新仓库:创建新文件夹,进入文件夹目录,执行指令 git init ,用以创建新的git 克隆仓库 执行指令用以创建一个本地仓库的

二分最大匹配总结

HDU 2444  黑白染色 ,二分图判定 const int maxn = 208 ;vector<int> g[maxn] ;int n ;bool vis[maxn] ;int match[maxn] ;;int color[maxn] ;int setcolor(int u , int c){color[u] = c ;for(vector<int>::iter

整数Hash散列总结

方法:    step1  :线性探测  step2 散列   当 h(k)位置已经存储有元素的时候,依次探查(h(k)+i) mod S, i=1,2,3…,直到找到空的存储单元为止。其中,S为 数组长度。 HDU 1496   a*x1^2+b*x2^2+c*x3^2+d*x4^2=0 。 x在 [-100,100] 解的个数  const int MaxN = 3000

状态dp总结

zoj 3631  N 个数中选若干数和(只能选一次)<=M 的最大值 const int Max_N = 38 ;int a[1<<16] , b[1<<16] , x[Max_N] , e[Max_N] ;void GetNum(int g[] , int n , int s[] , int &m){ int i , j , t ;m = 0 ;for(i = 0 ;

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动