SnapdragonCamera源码分析

SnapdragonCamera源码分析

SnapdragonCamera是高通平台下的相机的源码,具有良好的架构,我们可以在这个基础上进行定制,而不需要从头到尾开发一个相机类的app。新入坑的读者可以通过阅读源码和笔者对该项目源码的分析,从而对该项目有一个大致的整体的理解,从而更快读懂源码,以便对其进行定制开发。

相机的基本配置

先来看一看怎样用最少代码实现相机功能。

1.布局文件。可以用SurfaceView来作为拍照预览的控件。TextureView也可以。

<!-- Camera预览显示的控件 -->
<SurfaceView
    android:id="@+id/surface_camera"
    android:layout_width="match_parent"
    android:layout_height="match_parent"         />

2.配置SurfaceView。

surfaceCamera = findViewById(R.id.surface_camera);
holder = surfaceCamera.getHolder();
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
holder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                initCamera();
            }
            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            }
            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                if( camera != null ){
                    camera.stopPreview();
                }
            }
        });

3.初始化Camera的参数,并将Camera对象与SurfaceView绑定。

private void initCamera() {
    camera = Camera.open();
    if( camera != null ){
       camera.setDisplayOrientation(90);
        try {
            Camera.Parameters parameters = camera.getParameters();
           parameters.setPictureFormat(ImageFormat.JPEG);
            parameters.set("jpeg-quality", 90); 
            //viewWidth是SurfaceView的宽度,viewHeight是SurfaceView的高度
            parameters.setPictureSize(viewWidth, viewHeight);
            camera.setPreviewDisplay(holder);
            camera.startPreview();
        }catch (Exception e){
                e.printStackTrace();
      }
    }
  }

到了这里,就完成了预览了功能。Parameters类管理相机的参数,setPictureFormat是设置生成照片的格式,setPictureSize是设置生成图片的大小,camera.setPreviewDisplay()是将SurfaceView与camera关联。执行了camera.startPreview()后便开始预览。

4.拍照。预览是相机的一大核心功能,另一个核心功能是拍照。拍照的方法是Camera类的takePicture()方法。

public final void takePicture(ShutterCallback shutter, PictureCallback raw,
            PictureCallback postview, PictureCallback jpeg) 
            
public interface PictureCallback {
void onPictureTaken(byte[] data, Camera camera);
}

以下是官方文档的注释:
触发异步图像捕获。相机服务将会随着捕获进度初始化一系列的回调。ShutterCallback会在图片被捕获后回调,这个回调可以用来触发一个声音,让用户知道图片已经被捕获了。raw callback会在原生图片数据可用后回调。
当缩放、完全处理了图片后,postview callback回调发生。当压缩图片可用的时候,jpeg回调发生。一般用jpeg这个回调。

data就是照片的byte[]数据,可以通过IO流写入SD卡或内部存储,这样就把照片保存了。

  1. Camera对象。Camera类是相机功能的核心类。
    官方注释对它的介绍是这样的:

The Camera class is used to set image capture settings, start/stop preview,snap pictures, and retrieve frames for encoding for video. This class is a client for the Camera service, which manages the actual camera hardware.

(百度翻译加本人翻译)这个Camera类用于设置拍照的设置,开始或停止预览,拍照,和获取每一帧来编码视频.这个类是Camera Service的客户端,Camera Service管理着实际的Camera硬件。

常用的方法:

// 一般用这个方法来实例化Camera对象,cameraId为0是后置摄像头,为1是前置摄像头
public static Camera open(int cameraId) {
        return new Camera(cameraId);
    }
    
    
// 将SurfaceHolder设置到camera中去,最后是设置Surface对象进去
public final void setPreviewDisplay(SurfaceHolder holder) throws IOException {
        if (holder != null) {
            setPreviewSurface(holder.getSurface());
        } else {
            setPreviewSurface((Surface)null);
        }
    }
    
//用于将参数设置进Camera,更改Camera的设置
    public void setParameters(Parameters params) {
        // If using preview allocations, don't allow preview size changes
        if (mUsingPreviewAllocation) {
            Size newPreviewSize = params.getPreviewSize();
            Size currentPreviewSize = getParameters().getPreviewSize();
            if (newPreviewSize.width != currentPreviewSize.width ||
                    newPreviewSize.height != currentPreviewSize.height) {
                throw new IllegalStateException("Cannot change preview size" +
                        " while a preview allocation is configured.");
            }
        }

        native_setParameters(params.flatten());
    }

相机类的App调用的就是这些方法,有的相机类的App架构比较复杂,有许多的中间层,但到了最后,是会调用到这些方法的,理解了这些,再去读懂高通相机 SnapdragonCamera 就有了基础。

Parameters参数

Camera的参数有很多,比如照片的尺寸、预览的帧率、白平衡、滤镜等等,参数个数多达几十上百种,好的硬件支持更多的参数类型,也有更好的效果。一个好的相机应用不仅仅是拍照,还要能很方便地对各种的参数进行调整,使相机硬件的功能充分发挥出来。好的相机应用会把各种参数用恰当的图标表示出来,用户一眼就明白是什么意思。

先来看一段官方的注释:

相机服务设置。
使相机参数生效,应用程序必须调用Camera#setParameters(Camera.Parameters)。例如,在调用了Camera.Parameters#setWhiteBalance后,白平衡会在Camera#setParameters(Camera.Parameters)调用后生效。

不同的设备可能具有不同的相机功能,如
图片大小或闪存模式。应用程序应该在设置参数之前查询当前设备是否具有这种功能参数。例如,应用程序在设置滤镜(Camera.Parameters#setColorEffect(String))之前,调用Camera.Parameters#getSupportedColorEffects(),如果设备不支持滤镜会返回null。
下面是一些参数的键:

// 通过键值对的方式来保存参数
        private static final String KEY_PREVIEW_SIZE = "preview-size"; // 预览画面的大小
        private static final String KEY_PREVIEW_FORMAT = "preview-format";        
        private static final String KEY_PREVIEW_FRAME_RATE = "preview-frame-rate";        // 预览的帧率
        private static final String KEY_PREVIEW_FPS_RANGE = "preview-fps-range";        // FPS(frame per second)
        private static final String KEY_PICTURE_SIZE = "picture-size";                // 拍照图片的大小 (比例要和 preview-size 的一致,不然会出现拉伸,变扁的情况)
        private static final String KEY_PICTURE_FORMAT = "picture-format";            // 图片的格式
        private static final String KEY_JPEG_THUMBNAIL_SIZE = "jpeg-thumbnail-size";        // thumbnail的尺寸,拍完照后一般出现在拍照按钮左侧那个缩略图
        private static final String KEY_JPEG_THUMBNAIL_WIDTH = "jpeg-thumbnail-width";        // thumbnail的宽
        private static final String KEY_JPEG_THUMBNAIL_HEIGHT = "jpeg-thumbnail-height";    // thumbnail的高
        private static final String KEY_JPEG_THUMBNAIL_QUALITY = "jpeg-thumbnail-quality";    // thumbnail的质量
        private static final String KEY_JPEG_QUALITY = "jpeg-quality";                // 图片的质量
        private static final String KEY_ROTATION = "rotation";                    // 照片的旋转角度
        private static final String KEY_GPS_LATITUDE = "gps-latitude";                // gps 的纬度
        private static final String KEY_GPS_LONGITUDE = "gps-longitude";            // gps 的经度
        private static final String KEY_GPS_ALTITUDE = "gps-altitude";                // 海拔高度
        private static final String KEY_GPS_TIMESTAMP = "gps-timestamp";            // 时间戳
        private static final String KEY_GPS_PROCESSING_METHOD = "gps-processing-method";    
        private static final String KEY_WHITE_BALANCE = "whitebalance";                // 白平衡。分为incandescent、fluorescent 、auto 、 Daylight 、Cloudy
        private static final String KEY_EFFECT = "effect";                    // 滤镜效果,比如黑白,旧相片效果等
        private static final String KEY_ANTIBANDING = "antibanding";                
        private static final String KEY_SCENE_MODE = "scene-mode";                // 场景。分为 Auto 、Portrait 、Night等等
        private static final String KEY_FLASH_MODE = "flash-mode";                // 闪光灯模式。分为 on、off、auto
        private static final String KEY_FOCUS_MODE = "focus-mode";                // 对焦模式。有 auto、macro 、 inifite 、continuous-picture等
        private static final String KEY_FOCUS_AREAS = "focus-areas";
        private static final String KEY_MAX_NUM_FOCUS_AREAS = "max-num-focus-areas";
        private static final String KEY_FOCAL_LENGTH = "focal-length";
        private static final String KEY_HORIZONTAL_VIEW_ANGLE = "horizontal-view-angle";
        private static final String KEY_VERTICAL_VIEW_ANGLE = "vertical-view-angle";
        private static final String KEY_EXPOSURE_COMPENSATION = "exposure-compensation";    // 曝光
        private static final String KEY_MAX_EXPOSURE_COMPENSATION = "max-exposure-compensation";    // 最大曝光值
        private static final String KEY_MIN_EXPOSURE_COMPENSATION = "min-exposure-compensation";    // 最小曝光值
        private static final String KEY_EXPOSURE_COMPENSATION_STEP = "exposure-compensation-step";    // 曝光梯度(假设梯度为3,那每一个等级的曝光+3)
        private static final String KEY_AUTO_EXPOSURE_LOCK = "auto-exposure-lock";
        private static final String KEY_AUTO_EXPOSURE_LOCK_SUPPORTED = "auto-exposure-lock-supported";
        private static final String KEY_AUTO_WHITEBALANCE_LOCK = "auto-whitebalance-lock";
        private static final String KEY_AUTO_WHITEBALANCE_LOCK_SUPPORTED = "auto-whitebalance-lock-supported";
        private static final String KEY_METERING_AREAS = "metering-areas";
        private static final String KEY_MAX_NUM_METERING_AREAS = "max-num-metering-areas";
        private static final String KEY_ZOOM = "zoom";                        // 变焦系数
        private static final String KEY_MAX_ZOOM = "max-zoom";                    // 最大变焦
        private static final String KEY_ZOOM_RATIOS = "zoom-ratios";                
        private static final String KEY_ZOOM_SUPPORTED = "zoom-supported";            // 是否支持变焦
        private static final String KEY_SMOOTH_ZOOM_SUPPORTED = "smooth-zoom-supported";    // 平滑变焦
        private static final String KEY_FOCUS_DISTANCES = "focus-distances";            // 距离
        private static final String KEY_VIDEO_SIZE = "video-size";                // 视频的尺寸                
        private static final String KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO =
                                            "preferred-preview-size-for-video";
        private static final String KEY_MAX_NUM_DETECTED_FACES_HW = "max-num-detected-faces-hw";
        private static final String KEY_MAX_NUM_DETECTED_FACES_SW = "max-num-detected-faces-sw";
        private static final String KEY_RECORDING_HINT = "recording-hint";
        private static final String KEY_VIDEO_SNAPSHOT_SUPPORTED = "video-snapshot-supported";    // 录像过程中拍照
        private static final String KEY_VIDEO_STABILIZATION = "video-stabilization";        // 视频稳定
        private static final String KEY_VIDEO_STABILIZATION_SUPPORTED = "video-stabilization-supported";    // 是否视频稳定(防抖)


   // Formats for setPreviewFormat and setPictureFormat.            数据格式
        private static final String PIXEL_FORMAT_YUV422SP = "yuv422sp";
        private static final String PIXEL_FORMAT_YUV420SP = "yuv420sp";
        private static final String PIXEL_FORMAT_YUV422I = "yuv422i-yuyv";
        private static final String PIXEL_FORMAT_YUV420P = "yuv420p";
        private static final String PIXEL_FORMAT_RGB565 = "rgb565";
        private static final String PIXEL_FORMAT_JPEG = "jpeg";
        private static final String PIXEL_FORMAT_BAYER_RGGB = "bayer-rggb";

如何设置参数:
调用Parameter的set方法就可以对参数进行设置。

Parameters#set(String, String)
第一个参数为键,第二个参数为值。

下面是一个设置Preview Size,也就是预览画面的大小的例子:

// 设置preview size
public void setPreviewSize(int width, int height) {
            String v = Integer.toString(width) + "x" + Integer.toString(height);
            set(KEY_PREVIEW_SIZE, v);
        }

在最后面调用到了set(String)方法。

如何获取参数:
Parameters#get(String)
获取到相对应的键的值。

获取Preview Size的大小:

// 获取到preview size
public Size getPreviewSize() {
            String pair = get(KEY_PREVIEW_SIZE);
            return strToSize(pair);
        }

最后调用到了 get(String)方法。

到了这里,基础部分已经讲完,下一节开始介绍源码。

SnapdragonCamera的Parameters参数管理

这里开始进入到SnapdragonCamera的源码中。首先从Parameters参数源码开始讲起,SnapdragonCamera通过Sharedpreference对Parameters参数进行管理。

上一节中我们知道照相机的参数有几十个,为了更方便地管理这么多的参数,SnapdragonCamera将参数放到xml文件中,这样就能在一个文件里查看和配置参数了。

照相模式的参数放在 camera_preferences.xml文件里。

<PreferenceGroup
        xmlns:camera="http://schemas.android.com/apk/res/org.codeaurora.snapcam"
        camera:title="@string/pref_camera_settings_category">
    <IconListPreference
            camera:key="pref_camera_flashmode_key"
            camera:defaultValue="@string/pref_camera_flashmode_default"
            camera:title="@string/pref_camera_flashmode_title"
            camera:icons="@array/camera_flashmode_icons"
            camera:largeIcons="@array/camera_flashmode_largeicons"
            camera:entries="@array/pref_camera_flashmode_entries"
            camera:entryValues="@array/pref_camera_flashmode_entryvalues"
            camera:labelList="@array/pref_camera_flashmode_labels"
            camera:singleIcon="@drawable/ic_settings_flash" />
    <IconListPreference
            camera:key="pref_camera_exposure_key"
            camera:defaultValue="@string/pref_exposure_default"
            camera:title="@string/pref_exposure_title"
            camera:singleIcon="@drawable/ic_settings_exposure" />
            ……
 </PreferenceGroup>           

录像模式的参数放在 video_preference.xml里面。

<PreferenceGroup
        xmlns:camera="http://schemas.android.com/apk/res/org.codeaurora.snapcam"
        camera:title="@string/pref_camcorder_settings_category">
    <IconListPreference
            camera:key="pref_video_quality_key"
            camera:title="@string/pref_video_quality_title"
            camera:entries="@array/pref_video_quality_entries"
            camera:singleIcon="@drawable/ic_settings_quality"
            camera:entryValues="@array/pref_video_quality_entryvalues"/>
    <ListPreference
            camera:key="pref_camera_videoencoder_key"
            camera:defaultValue="@string/pref_camera_videoencoder_default"
            camera:title="@string/pref_camera_videoencoder_title"
            camera:entries="@array/pref_camera_videoencoder_entries"
            camera:entryValues="@array/pref_camera_videoencoder_entryvalues" />
    <ListPreference
            camera:key="pref_camera_audioencoder_key"
            camera:defaultValue="@string/pref_camera_audioencoder_default"
            camera:title="@string/pref_camera_audioencoder_title"
            camera:entries="@array/pref_camera_audioencoder_entries"
            camera:entryValues="@array/pref_camera_audioencoder_entryvalues" />
    <ListPreference
            camera:key="pref_camera_video_cds_mode_key"
            camera:defaultValue="@string/pref_camera_video_cds_default"
            camera:title="@string/pref_camera_video_cds_title"
            camera:entries="@array/pref_camera_video_cds_entries"
            camera:entryValues="@array/pref_camera_video_cds_entryvalues" />
    <ListPreference
            camera:key="pref_camera_video_tnr_mode_key"
            camera:defaultValue="@string/pref_camera_video_tnr_default"
            camera:title="@string/pref_camera_video_tnr_title"
            camera:entries="@array/pref_camera_video_tnr_entries"
            camera:entryValues="@array/pref_camera_video_tnr_entryvalues" />
    <IconListPreference
            camera:key="pref_camera_video_duration_key"
            camera:defaultValue="@string/pref_camera_video_duration_default"
            camera:title="@string/pref_camera_video_duration_title"
            camera:singleIcon="@drawable/ic_settings_duration"
            camera:entries="@array/pref_camera_video_duration_entries"
            camera:entryValues="@array/pref_camera_video_duration_entryvalues" />
            ……
</PreferenceGroup> 

我们看到很多节点以及节点里面的属性。每个节点代表一种参数。

camera:key 属性的值就是SharedPreference的key。

camera:title 属性就是对外显示的名称。

camera:entryValues 属性的值包含了这个key里面的可选的参数列表。

camera:entries 属性的值是entryValues对外显示的内容。

为了获取到xml文件里面的内容,就不得不使用到xml文件解析技术。xml文件的解析是Android的基础知识,就不多介绍了。SnapdragonCamera中解析xml相关的类有PreferenceInflater类,从xml文件中解析出数据,输出的类是CameraPreference。CameraPreference是这些xml文件中的标签的根类,
ListPreference(参数列表)、IconListPreference(参数列表的基础上多了一些图标)等都是CameraPreference的子类。

ListPreference和IconListPreference这两个类出现的次数比较多,我们在camera_preference.xml和video_preference.xml两个文件中看到不少这两个类。着重介绍下这两个类。

ListPreference是CameraPreference的子类。拥有几个成员变量:

    private final String mKey;                      // SharedPreference的键的名称
    private String mValue;                          // 属性的参数值
    public final CharSequence[] mDefaultValues;     // 默认属性值
    private CharSequence[] mEntries;                // 参数值列表
    private CharSequence[] mEntryValues;            // 参数值对外显示的名称的列表
    private CharSequence[] mDependencyList;         
    private CharSequence[] mLabels;
    <declare-styleable name="CameraPreference">
        <attr name="title" format="string" />
    </declare-styleable>
    <declare-styleable name="ListPreference">
        <attr name="key" format="string" />
        <attr name="defaultValue" format="string|reference" />
        <attr name="entryValues" format="reference" />
        <attr name="entries" format="reference" />
        <attr name="labelList" format="reference" />
        <attr name="dependencyList" format="reference" />
    </declare-styleable>

在ListPreference的构造方法中,通过下面的代码,将xml文件中的属性值获取到,并赋值给对应的成员变量:

        setEntries(a.getTextArray(R.styleable.ListPreference_entries));
        setEntryValues(a.getTextArray(
                R.styleable.ListPreference_entryValues));
        setLabels(a.getTextArray(
                R.styleable.ListPreference_labelList));
        setDependencyList(a.getTextArray(
                R.styleable.ListPreference_dependencyList));

使用这些setXXX()方法可以给对应的属性赋值。

而IconListPreference是ListPreference的子类。多出了几个成员变量,这些成员变量与图标相关。

    private int mSingleIconId;
    private int mIconIds[];
    private int mLargeIconIds[];
    private int mImageIds[];
    private int mThumbnailIds[];

attr.xml文件对新属性的声明:

<declare-styleable name="IconListPreference">
        <!-- If a preference does not have individual icons for each entry, it can has a single icon to represent it. -->
        <attr name="singleIcon" format="reference" />
        <attr name="icons" />
        <attr name="largeIcons" format="reference" />
        <attr name="thumbnails" format="reference" />
        <attr name="images" format="reference" />
    </declare-styleable>

在构造方法中,获取到xml文件的值。

        mSingleIconId = a.getResourceId(
                R.styleable.IconListPreference_singleIcon, 0);
        mIconIds = getIds(res, a.getResourceId(
                R.styleable.IconListPreference_icons, 0));
        mLargeIconIds = getIds(res, a.getResourceId(
                R.styleable.IconListPreference_largeIcons, 0));
        mImageIds = getIds(res, a.getResourceId(
                R.styleable.IconListPreference_images, 0));
        mThumbnailIds = getIds(res, a.getResourceId(
                R.styleable.IconListPreference_thumbnails, 0));

设置参数:

    public void setPreference(String key, String value) {
        ListPreference pref = mPreferenceGroup.findPreference(key);
        if (pref != null && !value.equals(pref.getValue())) {
            pref.setValue(value);
            reloadPreferences();
        }
    }

PhotoMenu这个类里面有个设置参数方法,传入key和value,就能设置进去了。设置完之后调用onSettingChange()方法,PhotoMenu的父类MenuController的onSettingChange()方法会调用到OnPreferenceChangedListener接口的onSharedPreferenceChange(),而PhotoModule实现了OnPreferenceChangedListener接口,在PhtotoModule的onSharedPreferenceChanged()方法中,将参数值设置到Parameters对象中,这样就把新效果显示出来了。

CameraSettings类是一个参数配置的类,与上述的类有很强的关联。这个类将上述配置的SharedPreference的key的值用 public final static String 声明出来。

    ……
    public static final String KEY_VERSION = "pref_version_key";
    public static final String KEY_LOCAL_VERSION = "pref_local_version_key";
    public static final String KEY_RECORD_LOCATION = "pref_camera_recordlocation_key";
    public static final String KEY_VIDEO_QUALITY = "pref_video_quality_key";
    public static final String KEY_VIDEO_TIME_LAPSE_FRAME_INTERVAL = "pref_video_time_lapse_frame_interval_key";
    public static final String KEY_PICTURE_SIZE = "pref_camera_picturesize_key";
    public static final String KEY_JPEG_QUALITY = "pref_camera_jpegquality_key";
    public static final String KEY_FOCUS_MODE = "pref_camera_focusmode_key";
    public static final String KEY_FLASH_MODE = "pref_camera_flashmode_key";
    public static final String KEY_VIDEOCAMERA_FLASH_MODE = "pref_camera_video_flashmode_key";
    public static final String KEY_WHITE_BALANCE = "pref_camera_whitebalance_key";
    public static final String KEY_SCENE_MODE = "pref_camera_scenemode_key";
    public static final String KEY_EXPOSURE = "pref_camera_exposure_key";
    public static final String KEY_TIMER = "pref_camera_timer_key";
    public static final String KEY_TIMER_SOUND_EFFECTS = "pref_camera_timer_sound_key";
    public static final String KEY_VIDEO_EFFECT = "pref_video_effect_key";
    public static final String KEY_CAMERA_ID = "pref_camera_id_key";
    public static final String KEY_CAMERA_HDR = "pref_camera_hdr_key";
    public static final String KEY_CAMERA_HQ = "pref_camera_hq_key";
    public static final String KEY_CAMERA_HDR_PLUS = "pref_camera_hdr_plus_key";
    public static final String KEY_CAMERA_FIRST_USE_HINT_SHOWN = "pref_camera_first_use_hint_shown_key";
    public static final String KEY_VIDEO_FIRST_USE_HINT_SHOWN = "pref_video_first_use_hint_shown_key";
    public static final String KEY_PHOTOSPHERE_PICTURESIZE = "pref_photosphere_picturesize_key";
    ……

这样在设置参数的时候就可以通过代码补全很快地打出来了。

SnapdragonCamera的代码结构

基本的功能放置于一个Activity中,切换拍照模式后,比如从普通拍照模式切换到录像模式后,界面和功能会有对应的改变,就像常见的Fragment+Viewpage那样。

public interface CameraModule {
    void init(CameraActivity activity, View frame);        //初始化布局和参数,相当于Fragment的onCreateView().

    // 这两个方法与onPause()方法相关,onPause()在离开当前Activity时调用,因此适合释放某些资源,以节省内存。
    void onPauseBeforeSuper();//在super.onPause()方法之前调用
    void onPauseAfterSuper();//在super.onPause()方法后面调用

    // 这两个方法与onResume相关,从别的Activity回来,或者灭屏后回来,可以省略掉初始化布局之类的步骤,来初始化参数.
    void onResumeBeforeSuper();//在super.onResume()方法前面调用
    void onResumeAfterSuper();//在super.onResume()方法后面调用

    ……

    }

常用的Module有 PhotoModule(拍照模式)、VideoModule(录像模式)、WideAnglePanoramaModule(全景模式),它们都实现了CameraModule接口。较为常用的是 PhotoModule,也就是普通拍照模式,就以PhotoModule为切入点,来分析SnapdragonCamera的代码结构。

PhotoModule实现的接口有: CameraModule、PhotoController、FocusOverlayManager.Listener(与对焦有关)、CameraPreference.OnPreferenceChangedListener(参数变化回调)、ShutterButton.OnShutterButtonListener(点击了拍照按钮的事件有关)、MediaSaveService.Listener(与保存图片有关)、OnCountDownFinishedListener(与倒计时拍照有关)、LocationManager.Listener(与位置信息出错有关)、SensorEventListener(与传感器回调参数有关)。拥有几个重要的成员变量:

PhotoUI mUI; // 与UI的显示有关,当执行了某个操作需要UI来响应就在PhotoUI里面写

PhotoMenu mMenu; // PhotoUI的成员变量,主要用于设置Camera的参数

顺便一提,VideoModule也是这样的结构。VideoModule有VideoUI、VideoMenu。主要的参数设置是在PhotoModule设置的,有一种情况是某两个参数是互斥的,比如开了HDR就不能开闪光灯。这个在PhotoModule里面会进行判断处理,把相应的参数设为关闭。

PhotoModule程序运行流程:

init()初始化各种View控件,如果 mOpenCameraThread 为空,则实例化 OpenCameraThread,OpenCameraThread是开启照相机Camera对象的线程。如下所示:

	private class OpenCameraThread extends Thread {
	        @Override
	        public void run() {
	            openCamera();
	            startPreview();
	        }
	}

openCamera()方法实例化了 Camera 对象,startPreview()方法将 SurfaceView 与 Camera 对象绑定起来,以开启预览。预览开启后,camera会初始化参数,setCameraParameters(UPDATE_PARAM_ALL),UPDATE_PARAM_ALL为更新所有的参数。

setCameraParameters()方法如下所示:

	private void setCameraParameters(int updateSet) {
	        if (mCameraDevice == null) {
	            return;
	        }
	        synchronized (mCameraDevice) {
	            boolean doModeSwitch = false;
	
	            if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
	                updateCameraParametersInitialize();
	            }
	
	            if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
	                updateCameraParametersZoom();
	            }
	
	            if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
	                doModeSwitch = updateCameraParametersPreference();
	            }
	
	            mCameraDevice.setParameters(mParameters);
	
	            // Switch to gcam module if HDR+ was selected
	            if (doModeSwitch && !mIsImageCaptureIntent) {
	                mHandler.sendEmptyMessage(SWITCH_TO_GCAM_MODULE);
	            }
	        }
	    }

因为 UPDATE_PARAM_ALL = -1, (updateSet & UPDATE_PARAM_INITIALIZE) != 0 、(updateSet & UPDATE_PARAM_ZOOM) != 0 和 (updateSet & UPDATE_PARAM_PREFERENCE) != 0,所以执行

updateCameraParametersInitialize();//(设置PreviewFpsRange、RECORDING_HINT、video-stabilization-supported)
updateCameraParametersZoom();//(设置zoom系数)
updateCameraParametersPreference();

updateCameraParametersPreference()设置的参数比较多:

	setAutoExposureLockIfSupported();
    setAutoWhiteBalanceLockIfSupported();
    setFocusAreasIfSupported();
    setMeteringAreasIfSupported();

另外设置的有:
pictureSize、previewSize、JpegThumbnailSize、SceneMode、JpegQuality、ExposureCompensation、FlashMode、WhiteBalance。

有一个比较特殊且实用的功能,移动手机自动对焦。

if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
            updateAutoFocusMoveCallback();
}

如果对焦是 Continuous-picture 则添加场景移动时,自动对焦的回调。
除此以外,还有“tsmakeup”,“tsmakeup_whiten”,“tsmakeup_clean”,最后调用qcomUpdateCameraParametersPreference()。

剩下的参数还有:
luma-adaptation(亮度) ,long-shot(长按拍照按钮连拍),touchAfAec ,picture-format,jpegQuality, PictureSize, selectableZoneAf, Denoise, RedeyeReduction, ISOValue, colorEffect(滤镜),saturation(饱和度),contrast(对比度),sharpness(锐度),tnr-mode,hdr-mode(HDR),hdr-need-1x,还有一些高级特征(Advanced Feature : ubiFocus、chromaFlash(色度闪光灯)、optiZoom、reFocus、fssr、truePortrait、multiTouchFocus、stillMore)。

一个好的Camera app是要包含很多的功能,来最大发挥手机硬件的提供性能和底层驱动提供的功能。

PhotoModule还有一些手势操作来丰富用户和Camera的交互方式。ScaleGestureDetector类包含了放大缩小的手势操作方法,拥有一个内部接口,OnScaleGestureListener,主要是放大缩小的操作。

public interface OnScaleGestureListener {

    boolean onScale(ScaleGestureDetector detector);
    
    boolean onScaleBegin(ScaleGestureDetector detector);
    
    void onScaleEnd(ScaleGestureDetector detector);
}

GestureDetector类是手势操作类:单击、双击、滑动、轻扫(fling)、长按等,SimpleOnGestureListener是它的静态内部类,实现了多个接口OnGestureListener,OnDoubleTapListener,OnContextClickListener。

实现这些手势操作的接口的类是PreviewGresture,顾名思义,这个类是在预览的时候的手势操作。PreviewGresture实现了ScaleGestureDetector.OnScaleGestureListener接口。

PreviewGesture拥有RenderOverlay和PieRenderer,RenderOverlay也持有PreviewGesture的引用。RenderOverlay在 photo_module.xml 布局文件中,继承于FrameLayout,高度宽度都是 match_parent。如此以来屏幕的点击事件就可以靠RenderOverlay来传递了,在 dispatchTouchEvent()方法里,把事件一层层传递下去,选择在哪一层消耗掉这个事件。

PieRenderer用于显示对焦的UI。一般的Camera应用都会有对焦框,当用户点击预览画面的时候,会显示一个圈或者框,用于表示正在进行对焦,和对焦失败或成功的结果。PieRenderer继承抽象类OverlayRenderer,OverlayRenderer实现了RenderOverlay.Renderer接口,可以简单认为PieRenderer实现了RenderOverlay.Renderer接口,另外PieRenderer也实现了FocusIndicator接口,SnapdragonCamera的人脸检测框也是实现了这个接口。

这里稍微提一句,RenderOverlay和RenderOverlay.Renderer之间的关系,读者可以自行阅读源码去理解,如果我把这些细节全部都说明白的话,估计能写一本书了。本文是一个概略,希望能让刚入门学习SnapdragonCamera的开发者了解它的一个大概的架构。

来看PieRenderer的一段关键代码:

public void drawFocus(Canvas canvas) {
        if (mBlockFocus) return;
        mFocusPaint.setStrokeWidth(mOuterStroke);
        canvas.drawCircle((float) mFocusX, (float) mFocusY, (float) mCircleSize, mFocusPaint);
        ……
    }

这段代码把对焦框画了出来,当我们用手指点击了预览画面,由singleTapUp()方法开始,最终会调用到这个drawFocus()方法。

SnapdragonCamera中的设计模式

单例模式和工厂模式

SnapdragonCamera声明了一个接口CameraManager,CameraManager里面包含了多个子接口,里面包含了一般Camera的常规操作的方法。

public interface CameraManager {

    /**
     * An interface which wraps
     * {@link android.hardware.Camera.AutoFocusCallback}.
     */
    public interface CameraAFCallback {
        public void onAutoFocus(boolean focused, CameraProxy camera);
    }

    /**
     * An interface which wraps
     * {@link android.hardware.Camera.AutoFocusMoveCallback}.
     */
    public interface CameraAFMoveCallback {
        public void onAutoFocusMoving(boolean moving, CameraProxy camera);
    }

    /**
     * An interface which wraps
     * {@link android.hardware.Camera.ShutterCallback}.
     */
    public interface CameraShutterCallback {
        public void onShutter(CameraProxy camera);
    }
    ……
    }

具体的实现类是AndroidCameraManagerImpl。为了保证AndroidCameraManagerImpl的唯一性,使用CameraManagerFactory类来实例化它:

public class CameraManagerFactory {

    private static AndroidCameraManagerImpl sAndroidCameraManager;

    /**
     * Returns the android camera implementation of {@link CameraManager}.
     *
     * @return The {@link CameraManager} to control the camera device.
     */
    public static synchronized CameraManager getAndroidCameraManager() {
        if (sAndroidCameraManager == null) {
            sAndroidCameraManager = new AndroidCameraManagerImpl();
        }
        return sAndroidCameraManager;
    }
}

代理模式

CameraManager接口中,有一个内部接口,CameraManager.CameraProxy,该接口的实现类是AndroidCameraProxyImpl。

代理模式的作用是为其他对象提供一种代理以控制对这个对象的访问。

AndroidCameraProxyImp是AndroidCameraManagerImpl的内部类,持有Camera类的引用--mCamera,里面所有的方法的最终实现是靠mCamera。

@Override
        public void setMetadataCb(CameraMetaDataCallback cb){
            mCameraHandler.obtainMessage(SET_AUTO_HDR_MODE, cb).sendToTarget();
        }

        @Override
        public void setPreviewTexture(SurfaceTexture surfaceTexture) {
            mCameraHandler.obtainMessage(SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture).sendToTarget();
        }

        @Override
        public void setPreviewDisplay(SurfaceHolder surfaceHolder) {
            mCameraHandler.obtainMessage(SET_PREVIEW_DISPLAY_ASYNC, surfaceHolder).sendToTarget();
        }

        @Override
        public void startPreview() {
            mCameraHandler.sendEmptyMessage(START_PREVIEW_ASYNC);
        }

有关Camera的介绍就大致到这里了。网上的相关文章也有很多,但比较深入,没有比较适合刚入手的,就写了这篇。SnapdragonCamera的架构很科学合理,如果要完成一个支持多种模式的Camera,新手的第一个想法可能就是用Fragment+ViewPager,感觉用这样的方式写会很不合理。SnapdragonCamera的设计思想,可以让我们开阔眼界,感受到国外大神程序员的设计的精髓,对面向对象的思想有更深刻的理解。

如果读者觉得有帮助可以扫扫支付宝的领红包二维码。

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值