分享

Android用摄像头的那点破事 | eyeHere

 點點滴滴 2011-12-06

Android用摄像头的那点破事

星期五, 29. 七月 2011

摄像头这两天玩Android玩的废寝忘食,Blog都好几天没加东西了,惭愧!记录一下这两天最崩溃的一个问题。

好早就装了开发环境,真正着手还是这两天,非常的生疏,虽然有SDK文档,那么多蚊子一般的字,实在没心思慢慢研究。这不想调用摄像头,原以为很容易就能搞定的,累计花了大概有一天的时间才只能保证不出错……至于效果嘛,难说啊!

先看API-examples里有调用 摄像头的例子,在模拟器上虽然看不出什么效果,毕竟还是能执行的,就是一个方块在黑白相间的背景上移动呗。

就这么一个Google提供的范例,传到我的HTC G2上也能一执行就报错,我对Google的尊敬之情顿时减少了0.0001%啊……(当然有可能是G2不够标准,但毕竟其他的软件都是能用的,看来是有 不少健壮代码了啊)。联机调试看了一下,出错的这一行(android-7里的):

parameters.setPreviewSize(w, h);

查一下,摄像头不是所有随便的(w, h)都能够认识的,所以呢,我们有了下面这样的增强版:

 
List<Size> mSupportedPreviewSizes;
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
 
private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio = (double) w / h;
        if (sizes == null) return null;
 
        Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;
 
        int targetHeight = h;
 
        // Try to find an size match aspect ratio and size
        for (Size size : sizes) {
            double ratio = (double) size.width / size.height;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }
 
        // Cannot find the one match the aspect ratio, ignore the requirement
        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }
        return optimalSize;
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
List<Size> mSupportedPreviewSizes;
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
 
private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio = (double) w / h;
        if (sizes == null) return null;
 
        Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;
 
        int targetHeight = h;
 
        // Try to find an size match aspect ratio and size
        for (Size size : sizes) {
            double ratio = (double) size.width / size.height;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }
 
        // Cannot find the one match the aspect ratio, ignore the requirement
        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }
        return optimalSize;
    }

后来的Sample里有了这段代码,看起来强大了不少。然而非常不幸的,首先getSupportedPreviewSizes()这个函数在2.1之后才有,我一开始是打算用1.6开发的……好吧我改,这个先不说,自己的手机已经刷到2.1了,这个函数的返回值居然是null?!如果确实想老版本上也用的话,怎么办??

有鉴于有软件可以达成,所以肯定是有方法的!得这么写:

 
public class SupportedSizesReflect {
    private static Method Parameters_getSupportedPreviewSizes = null;
    private static Method Parameters_getSupportedPictureSizes = null;
 
    static {
        initCompatibility();
    };
 
    private static void initCompatibility() {
        try {
            Parameters_getSupportedPreviewSizes = Camera.Parameters.class.getMethod(
                    "getSupportedPreviewSizes", new Class[] {});
 
            Parameters_getSupportedPictureSizes = Camera.Parameters.class.getMethod(
                    "getSupportedPictureSizes", new Class[] {});
 
        } catch (NoSuchMethodException nsme) {
            nsme.printStackTrace();
            Parameters_getSupportedPreviewSizes = Parameters_getSupportedPictureSizes = null;
        }
    }
 
    /**
     * Android 2.1之后有效
     * @param p
     * @return Android1.x返回null
     */
    public static List<Size> getSupportedPreviewSizes(Camera.Parameters p) {
        return getSupportedSizes(p, Parameters_getSupportedPreviewSizes);
    }
 
    public static List<Size> getSupportedPictureSizes(Camera.Parameters p){
        return getSupportedSizes(p, Parameters_getSupportedPictureSizes);
    }   
 
    @SuppressWarnings("unchecked")
    private static List<Size> getSupportedSizes(Camera.Parameters p, Method method){
        try {
            if (method != null) {
                return (List<Size>) method.invoke(p);
            } else {
                return null;
            }
        } catch (InvocationTargetException ite) {
            Throwable cause = ite.getCause();
            if (cause instanceof RuntimeException) {
                throw (RuntimeException) cause;
            } else if (cause instanceof Error) {
                throw (Error) cause;
            } else {
                throw new RuntimeException(ite);
            }
        } catch (IllegalAccessException ie) {
            return null;
        }
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class SupportedSizesReflect {
    private static Method Parameters_getSupportedPreviewSizes = null;
    private static Method Parameters_getSupportedPictureSizes = null;
 
    static {
        initCompatibility();
    };
 
    private static void initCompatibility() {
        try {
            Parameters_getSupportedPreviewSizes = Camera.Parameters.class.getMethod(
                    "getSupportedPreviewSizes", new Class[] {});
 
            Parameters_getSupportedPictureSizes = Camera.Parameters.class.getMethod(
                    "getSupportedPictureSizes", new Class[] {});
 
        } catch (NoSuchMethodException nsme) {
            nsme.printStackTrace();
            Parameters_getSupportedPreviewSizes = Parameters_getSupportedPictureSizes = null;
        }
    }
 
    /**
     * Android 2.1之后有效
     * @param p
     * @return Android1.x返回null
     */
    public static List<Size> getSupportedPreviewSizes(Camera.Parameters p) {
        return getSupportedSizes(p, Parameters_getSupportedPreviewSizes);
    }
 
    public static List<Size> getSupportedPictureSizes(Camera.Parameters p){
        return getSupportedSizes(p, Parameters_getSupportedPictureSizes);
    }   
 
    @SuppressWarnings("unchecked")
    private static List<Size> getSupportedSizes(Camera.Parameters p, Method method){
        try {
            if (method != null) {
                return (List<Size>) method.invoke(p);
            } else {
                return null;
            }
        } catch (InvocationTargetException ite) {
            Throwable cause = ite.getCause();
            if (cause instanceof RuntimeException) {
                throw (RuntimeException) cause;
            } else if (cause instanceof Error) {
                throw (Error) cause;
            } else {
                throw new RuntimeException(ite);
            }
        } catch (IllegalAccessException ie) {
            return null;
        }
    }
}

啊啊~,リフレクションなんか、大嫌い……然后还要用类似这样的方法调用~

 
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {
 
    Camera.Parameters params = camera.getParameters();
 
    List<Size> supportedPictureSizes
                = SupportedSizesReflect.getSupportedPictureSizes(params);
    List<Size> supportedPreviewSizes
                = SupportedSizesReflect.getSupportedPreviewSizes(params);
 
    if ( supportedPictureSizes != null &&
        supportedPreviewSizes != null &&
        supportedPictureSizes.size() > 0 &&
        supportedPreviewSizes.size() > 0) {
 
        //2.x
        pictureSize = supportedPictureSizes.get(0);
 
        int maxSize = 1280;
        if(maxSize > 0){
            for(Size size : supportedPictureSizes){
                if(maxSize >= Math.max(size.width,size.height)){
                    pictureSize = size;
                    break;
                }
            }
        }
 
        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        Display display = windowManager.getDefaultDisplay();
        DisplayMetrics displayMetrics = new DisplayMetrics();
        display.getMetrics(displayMetrics);
 
        previewSize = getOptimalPreviewSize(
                            supportedPreviewSizes,
                            display.getWidth(),
                            display.getHeight());
 
        params.setPictureSize(pictureSize.width, pictureSize.height);
        params.setPreviewSize(previewSize.width, previewSize.height);                               
 
    }
    this.camera.setParameters(params);
    try {
        this.camera.setPreviewDisplay(holder);
    } catch (IOException e) {
        e.printStackTrace();
    }
    this.camera.startPreview();
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {
 
    Camera.Parameters params = camera.getParameters();
 
    List<Size> supportedPictureSizes
                = SupportedSizesReflect.getSupportedPictureSizes(params);
    List<Size> supportedPreviewSizes
                = SupportedSizesReflect.getSupportedPreviewSizes(params);
 
    if ( supportedPictureSizes != null &&
        supportedPreviewSizes != null &&
        supportedPictureSizes.size() > 0 &&
        supportedPreviewSizes.size() > 0) {
 
        //2.x
        pictureSize = supportedPictureSizes.get(0);
 
        int maxSize = 1280;
        if(maxSize > 0){
            for(Size size : supportedPictureSizes){
                if(maxSize >= Math.max(size.width,size.height)){
                    pictureSize = size;
                    break;
                }
            }
        }
 
        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        Display display = windowManager.getDefaultDisplay();
        DisplayMetrics displayMetrics = new DisplayMetrics();
        display.getMetrics(displayMetrics);
 
        previewSize = getOptimalPreviewSize(
                            supportedPreviewSizes,
                            display.getWidth(),
                            display.getHeight());
 
        params.setPictureSize(pictureSize.width, pictureSize.height);
        params.setPreviewSize(previewSize.width, previewSize.height);                               
 
    }
    this.camera.setParameters(params);
    try {
        this.camera.setPreviewDisplay(holder);
    } catch (IOException e) {
        e.printStackTrace();
    }
    this.camera.startPreview();
}

死机无数次之后总结出来的啊,发现程序写的一个不好强制结束了,摄像头都无法再次启用了,kill都不行,只能重新启动手机才好。重启一次还那么慢,谁知道有比较适合G2的row?

哦还有一个,预览画面90°的,2.X后可以用parameters.set(“rotation”, “90″),之前的话得写成parameters.set(“orientation”, “portrait”)。但是据说不是所有的机器都可以的……

Tags: , , Posted in Android by xishui.

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多