前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >短视频系统源码开发之摄像头预览的实现

短视频系统源码开发之摄像头预览的实现

作者头像
云豹科技程序员
修改2021-05-27 18:09:33
8170
修改2021-05-27 18:09:33
举报

短视频系统源码开发之摄像头预览实现思路: 在xml布局中定义一个TextureView,用于预览相机采集的音视频数据

SurfaceTextureListener的onSurfaceTextureAvailable回调中打开相机 成功打开相机后,设置相机参数。比如:对焦模式,预览大小,照片保存大小等等

设置相机预览时的旋转角度,然后调用startPreview()开始预览

关闭页面,释放相机资源

短视频系统源码开发之摄像头预览关键实现: Android 相机Camera类(老版本)的内部类中有个Parameters内部类,这个类包含了操作相机的很多参数。

例如:

控制闪光灯setFlashMode 控制旋转方向setRotation 控制图片大小setPictureSize 控制预览大小setPreviewSize 在这里我们打开摄像头然后预览画面到屏幕上再到拍照保存本地,这一个过程就涉及到camera的预览尺寸和照片尺寸

相应的api就是setPictureSize和setPreviewSize。

在这里一般都会首先获取相机支持的预览尺寸,通过API

parameters.getSupportedPreviewSizes()

得到然后结合想要预览的width,height得到最合适的Camera.Size

然后获取相机支持的图片尺寸,通过API

parameters.getSupportedPictureSizes()

计算最终的CameraSize算法如下

  /**
     * 计算最完美的Size
     * @param sizes
     * @param expectWidth
     * @param expectHeight
     * @return
     */
    private static Camera.Size calculatePerfectSize(List<Camera.Size> sizes, int expectWidth,
                                                    int expectHeight, CalculateType calculateType) {
        sortList(sizes); // 根据宽度进行排序
 
        // 根据当前期望的宽高判定
        List<Camera.Size> bigEnough = new ArrayList<>();
        List<Camera.Size> noBigEnough = new ArrayList<>();
        for (Camera.Size size : sizes) {
            if (size.height * expectWidth / expectHeight == size.width) {
                if (size.width > expectWidth && size.height > expectHeight) {
                    bigEnough.add(size);
                } else {
                    noBigEnough.add(size);
                }
            }
        }
        // 根据计算类型判断怎么如何计算尺寸
        Camera.Size perfectSize = null;
        switch (calculateType) {
            // 直接使用最小值
            case Min:
                // 不大于期望值的分辨率列表有可能为空或者只有一个的情况,
                // Collections.min会因越界报NoSuchElementException
                if (noBigEnough.size() > 1) {
                    perfectSize = Collections.min(noBigEnough, new CompareAreaSize());
                } else if (noBigEnough.size() == 1) {
                    perfectSize = noBigEnough.get(0);
                }
                break;
 
            // 直接使用最大值
            case Max:
                // 如果bigEnough只有一个元素,使用Collections.max就会因越界报NoSuchElementException
                // 因此,当只有一个元素时,直接使用该元素
                if (bigEnough.size() > 1) {
                    perfectSize = Collections.max(bigEnough, new CompareAreaSize());
                } else if (bigEnough.size() == 1) {
                    perfectSize = bigEnough.get(0);
                }
                break;
 
            // 小一点
            case Lower:
                // 优先查找比期望尺寸小一点的,否则找大一点的,接受范围在0.8左右
                if (noBigEnough.size() > 0) {
                    Camera.Size size = Collections.max(noBigEnough, new CompareAreaSize());
                    if (((float)size.width / expectWidth) >= 0.8
                            && ((float)size.height / expectHeight) > 0.8) {
                        perfectSize = size;
                    }
                } else if (bigEnough.size() > 0) {
                    Camera.Size size = Collections.min(bigEnough, new CompareAreaSize());
                    if (((float)expectWidth / size.width) >= 0.8
                            && ((float)(expectHeight / size.height)) >= 0.8) {
                        perfectSize = size;
                    }
                }
                break;
 
            // 大一点
            case Larger:
                // 优先查找比期望尺寸大一点的,否则找小一点的,接受范围在0.8左右
                if (bigEnough.size() > 0) {
                    Camera.Size size = Collections.min(bigEnough, new CompareAreaSize());
                    if (((float)expectWidth / size.width) >= 0.8
                            && ((float)(expectHeight / size.height)) >= 0.8) {
                        perfectSize = size;
                    }
                } else if (noBigEnough.size() > 0) {
                    Camera.Size size = Collections.max(noBigEnough, new CompareAreaSize());
                    if (((float)size.width / expectWidth) >= 0.8
                            && ((float)size.height / expectHeight) > 0.8) {
                        perfectSize = size;
                    }
                }
                break;
        }
        // 如果经过前面的步骤没找到合适的尺寸,则计算最接近expectWidth * expectHeight的值
        if (perfectSize == null) {
            Camera.Size result = sizes.get(0);
            boolean widthOrHeight = false; // 判断存在宽或高相等的Size
            // 辗转计算宽高最接近的值
            for (Camera.Size size : sizes) {
                // 如果宽高相等,则直接返回
                if (size.width == expectWidth && size.height == expectHeight
                        && ((float) size.height / (float) size.width) == CameraParam.getInstance().currentRatio) {
                    result = size;
                    break;
                }
                // 仅仅是宽度相等,计算高度最接近的size
                if (size.width == expectWidth) {
                    widthOrHeight = true;
                    if (Math.abs(result.height - expectHeight) > Math.abs(size.height - expectHeight)
                            && ((float) size.height / (float) size.width) == CameraParam.getInstance().currentRatio) {
                        result = size;
                        break;
                    }
                }
                // 高度相等,则计算宽度最接近的Size
                else if (size.height == expectHeight) {
                    widthOrHeight = true;
                    if (Math.abs(result.width - expectWidth) > Math.abs(size.width - expectWidth)
                            && ((float) size.height / (float) size.width) == CameraParam.getInstance().currentRatio) {
                        result = size;
                        break;
                    }
                }
                // 如果之前的查找不存在宽或高相等的情况,则计算宽度和高度都最接近的期望值的Size
                else if (!widthOrHeight) {
                    if (Math.abs(result.width - expectWidth) > Math.abs(size.width - expectWidth)
                            && Math.abs(result.height - expectHeight) > Math.abs(size.height - expectHeight)
                            && ((float) size.height / (float) size.width) == CameraParam.getInstance().currentRatio) {
                        result = size;
                    }
                }
            }
            perfectSize = result;
        }
 
        Log.d(TAG,"expectSize="+expectWidth+"x"+expectHeight+" size="+perfectSize.width + "x" +perfectSize.height);
        return perfectSize;
    }

最终还要确定旋转角度防止图像旋转,反转

获取旋转角度的方法是

  /**
     * 设置预览角度,setDisplayOrientation本身只能改变预览的角度
     * previewFrameCallback以及拍摄出来的照片是不会发生改变的,拍摄出来的照片角度依旧不正常的
     * 拍摄的照片需要自行处理
     * @param activity
     */
    private int calculateCameraPreviewOrientation(Activity activity) {
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(CameraParam.getInstance().cameraId, info);
        int rotation = activity.getWindowManager().getDefaultDisplay()
                .getRotation();
        int degrees = 0;
        switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 0;
                break;
            case Surface.ROTATION_90:
                degrees = 90;
                break;
            case Surface.ROTATION_180:
                degrees = 180;
                break;
            case Surface.ROTATION_270:
                degrees = 270;
                break;
        }
 
        int result;
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360;
            result = (360 - result) % 360;
        } else {
            result = (info.orientation - degrees + 360) % 360;
        }
        CameraParam.getInstance().orientation = result;
        return result;
    }

短视频系统源码开发之摄像头预览相关工作就完成了,在这里总结几点:

1.查看相机设置的previewSize和显示控件的大小比例是否一致

2.如果是拍照变形查看pictureSize和自己设置的图片宽高一致或者比例一致

3.查看是否是预览角度引起的

4.如果是自己通过OpengL渲染了图片然后显示到view上,有没有做图形变换,(涉及纹理坐标和顶点坐标的问题)

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档