Android Camera的一些调试技巧总结

把之前开发遇到的一些问题总结一下, 由于很多东西都是和具体平台(高通/MKT)相关的, 本来有更多的内容可以总结, 但由于不在之前公司了,一些源码和环境都没了, 只能写一下记得比较清楚的东西了.

dumpsys

dumpsys是Android系统中一个调试神器, 可以看内存信息, 电池信息, 相机参数等等, 基本Android中每一个大的模块, 都能通过dumpsys来查看. 调试Camera我们通常需要知道一些参数信息, 默认参数, 实际设置参数, 支持的参数, 这些信息都可以通过dumpsys media.camera来查看, 具体用法如下:

//执行这个adb命令会打印所有默认参数和当前使用参数
adb shell dumpsys media.camera
//查看某一项参数,使用过滤命令grep(linux), windows下可使用findstr来过滤
adb shell dumpsys media.camera |grep picture-size
//输出
picture-size: 3264x2448
picture-size-values:
5520x4140,5984x3366,3840x2160,3264x2448,2048x1536,1920x1080,1280x720,640x480,480x320,320x240

其中参数后面带values(比如上面picture-size-values)表示这个参数可设置的值,需要注意的是, 如果当前没有打开Camera, 执行命令也会有相应输出, 此时输出的值是默认值(打开camera不设置任何参数).
上面说的是针对HAL1的参数, 如果是HAL3参数, 输出内容就不一样了,如下:

adb shell dumpsys media.camera 
// HAL3部分参数输出
android.control.aeLockAvailable (10024): byte[1]
        [TRUE ]
android.control.awbLockAvailable (10025): byte[1]
        [TRUE ]
android.control.availableModes (10026): byte[2]
        [1 2 ]
android.shading.availableModes (100002): byte[2]
        [1 2 ]
android.statistics.info.availableLensShadingMapModes (120007): byte[1]
        [0 ]
android.sensor.info.preCorrectionActiveArraySize (f000a): int32[4]

可以看到, HAL1和HAL3差别很大, HAL3参数格式和Android Camera API2是对应的,如果要看HAL3的相关参数, 直接使用grep命令存在问题, 因为grep只会输出有关键字的那一行,但HAL3参数的Key和Values一般都不在一行, 所以要把输出保存到文件中,然后搜索关键字

adb shell dumpsys media.camera > camera.txt

No Display Mode

通常情况下,正常使用Camera的流程是:

  1. 打开Camera
  2. 设置参数
  3. 设置预览Surface
  4. 开启预览:startPreview()
  5. 拍照:takePicture()

但有些情况下, 我们希望不设置预览Surface就能拍照(默认情况下不设置预览进行拍照会Crash), 常见的应用场景是双摄项目, 打开了两个Camera,但副摄预览不需要用户看到, 这样做也可以减少系统资源占用, 高通和MTK平台都提供了不设置预览也可以拍照的功能, 使用方法如下:

  • 高通平台
    高通平台可以在ZSL开启的情况下, 通过设置参数Parameters.set("no-display-mode", "1");来达到不用设置Surface也能拍照的效果(参数必须在startPreview()之前设置).
  • MTK平台
    MTK平台要更简单些, 保证ZSD开启的情况下, 直接不用设置Surface, 进行startPreview()和takePicture()即可.

Camera内存占用问题

我们都知道, Camera App和CameraService是两个进程, App内存占用大家一般用Android Studio就能看到, 但如果我们在HAL层加入了一些修改, 要看内存占用, 这个时候就需要看CameraService的内存占用了, CameraService调用HAL层接口, 所以HAL层内存占用就体现在CameraService中, 要找到CameraService的进程, 可通过如下方法:
Android 7.1及以下版本

adb shell ps |grep -i camera
//高通Android 7.1.1, 输出如下:
cameraserver 371   1     20456  2240  binder_thr a84b396c S /system/bin/cameraserver
camera    401   1     73188  3404  poll_sched ab1a8a6c S /system/bin/mm-qcamera-daemon

可以看到和Camera相关的进程有两个, 一个是我们要找的CameraService,名称为cameraserver
另一个就高通的Camera守护进程qcamera-daemon
找到进程后, 查看内存就简单了,也是通过dumpsys来查看(meminfo后面也可以使用pid作为参数):

$ adb shell dumpsys meminfo cameraserver
Applications Memory Usage (in Kilobytes):
Uptime: 24402928 Realtime: 83208095
                   Pss  Private  Private     Swap     Heap     Heap     Heap
                 Total    Dirty    Clean    Dirty     Size    Alloc     Free
                ------   ------   ------   ------   ------   ------   ------
  Native Heap      196      196        0       40        0        0        0
  Dalvik Heap        0        0        0        0        0        0        0
        Stack       32       32        0       64
    Other dev        5        0        4        0
     .so mmap      470      204      176      508
   Other mmap       14        8        4        8
      Unknown       88       88        0       88
        TOTAL      805      528      184      708        0        0        0

 App Summary
                       Pss(KB)
                        ------
           Java Heap:        0
         Native Heap:      196
                Code:      380
               Stack:       32
            Graphics:        0
       Private Other:      104
              System:       93

               TOTAL:      805      TOTAL SWAP (KB):      708

Android 8.0及以上版本
在Android 8.0上, Google推出了一项Project Treble计划,旨在规范HAL接口, 简化Android的版本升级,其中一个重要改变就是, Framework层和HAL层也是通过Binder(此Binder和我们通常使用Binder不是同一个)通信, 因此HAL层内存占用并不在CameraService中, 而是在名为android.hardware.camera.provider@xx(xx表示版本号)中,比如SDM450平台就是[email protected],我们找到这个进程也是通过 adb shell ps |grep -i camera, 然后通过pid(名字可能会重复)来查看内存即可, 基本方法和上面一样, 只是进程不是CameraService.

API和HAL版本

Camera由于其复杂的特点, Android在发展过程中, Camera API也进行了更新, 在Android 5.0上, Google推出了Camera2 API, 同样HAL版本中API1和API2对应的版本是HAL1和HAL3. 但由于一些原因, HAL3和API2普及并不理想, 到目前为止, 中低端手机基本都是API1+HAL1, 对于开发者来说,我们可能想知道一个App用的API和HAL的版本,这个时候可以通过过滤Log方式来实现(高通和MTK都通用):

$ adb logcat |grep CameraService
04-07 14:07:43.174   371  4415 I CameraService: CameraService::connect call (PID -1 "com.smewise.camera2", camera ID 0) for HAL version default and Camera API version 1

可以看到Log打印的信息有App API的版本和HAL版本, API版本只有API1 和API2, HAL版本一般有三个结果: 256, 768, default.
256代表使用HAL1, 768为HAL3, default一般表示调用的是没有特别指定版本, 根据平台配置来决定,这种情况如果想知道到底使用的哪个版本, 需要看平台代码或者一些Log.

注:如果平台只支持HAL1, App通过API2去调用Camera的话, Framework层会将API2的调用转为用API1去调用, 也就是说虽然App使用的API2, 单对于CameraService来说使用的还是API1.

高通平台副摄可见

做过双摄项目的一般都知道, 副摄(一般id为2)对上层App是不可见的, 但我们在开发过程用需要对副摄进行一些测试, 因此是需要能打开副摄的,高通平台隐藏副摄是在Framework层做的处理, 代码如下:
API1 代码:
frameworks/base/core/java/android/hardware/Camera.java

    public static int getNumberOfCameras() {
        boolean exposeAuxCamera = false;
        String packageName = ActivityThread.currentOpPackageName();
        /* Force to expose only two cameras
         * if the package name does not falls in this bucket
         */
        String packageList = SystemProperties.get("camera.aux.packagelist");
        if (packageList.length() > 0) {
            TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
            splitter.setString(packageList);
            for (String str : splitter) {
                if (packageName.equals(str)) {
                    exposeAuxCamera = true;
                    break;
                }
            }
        }
        int numberOfCameras = _getNumberOfCameras();
        if (exposeAuxCamera == false && (numberOfCameras > 2)) {
            numberOfCameras = 2;
        }
        return numberOfCameras;
    }

API2 代码:
frameworks/base/core/java/android/hardware/camera2/CameraManager.java

private ArrayList getOrCreateDeviceIdListLocked() throws CameraAccessException {
 // 部分代码省略
            try {
                numCameras = cameraService.getNumberOfCameras(CAMERA_TYPE_ALL);
                /* Force to expose only two cameras
                 * if the package name does not falls in this bucket
                 */
                boolean exposeAuxCamera = false;
                String packageName = ActivityThread.currentOpPackageName();
                String packageList = SystemProperties.get("camera.aux.packagelist");
                if (packageList.length() > 0) {
                    TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
                    splitter.setString(packageList);
                    for (String str : splitter) {
                        if (packageName.equals(str)) {
                            exposeAuxCamera = true;
                            break;
                        }
                    }
                }
                if (exposeAuxCamera == false && (numCameras > 2)) {
                    numCameras = 2;
                }
//部分代码省略

可以看到,这段代码逻辑是如果App包名在camera.aux.packagelist这个属性中,则可以打开副摄, 否则不行.因此打开副摄有两种方法:

  1. 设置camera.aux.packagelist这个属性, 把要打开的App包名添加进去
  2. 删除这部分限制Camera个数的代码(不推荐)

SnapdragonCamera高级设置

高通平台调试Camera一般用的就是SnapdragonCamera这个App, 这个App默认的设置参数比较有限, 有些我们想调节的参数没有, 比如anti-banding, 其实高通埋了个彩蛋在设置中的, 只要疯狂点击设置菜单中的Reduce Red Eye(减少红眼)选项, 然后重新进入设置菜单, 就能看到额外的设置选项了, 内容非常多, 比较有用, 控制这个额外菜单选项(DeveloperMenu)的代码如下:
packages/apps/SnapdragonCamera/src/com/android/camera/PhotoMenu.java

 public void onPreferenceClicked(ListPreference pref, int y) {
        if (!mActivity.isDeveloperMenuEnabled()) {
            if (pref.getKey().equals(CameraSettings.KEY_REDEYE_REDUCTION)) {
                privateCounter++;
                if (privateCounter >= DEVELOPER_MENU_TOUCH_COUNT) {
                    mActivity.enableDeveloperMenu();
                    SharedPreferences prefs = PreferenceManager
                            .getDefaultSharedPreferences(mActivity);
                    prefs.edit().putBoolean(CameraSettings.KEY_DEVELOPER_MENU, true).apply();
                    RotateTextToast.makeText(mActivity,
                            "Camera developer option is enabled now", Toast.LENGTH_SHORT).show();
                }
            } else {
                privateCounter = 0;
            }
        }
   //其他代码省略

CameraService启动流程

CameraService是在开机的的时候启动的, 并且不管有没有App使用Camera, 进程会一直存在, 在开机时,CameraService会发起一次对底层Camera信息的查询, 这个操作主要是确定Camera的个数和Camera基本信息的, 也就是说如果在HAL层及以下, 修改了Camera个数, 需要重启手机才会生效.
另外由于CameraService开机启动, 如果HAL层修改东西存在问题, 可能会导致手机无法开机, 但据我所知, 高通平台Android 7.1.1及以后的版本, CameraService无法启动应该是不会影响整个系统的启动.

你可能感兴趣的:(Android Camera的一些调试技巧总结)