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

2021-05-27 18:09:33 浏览数 (1)

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

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

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

关闭页面,释放相机资源

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

例如:

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

相应的api就是setPictureSize和setPreviewSize。

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

代码语言:javascript复制
parameters.getSupportedPreviewSizes()

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

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

代码语言:javascript复制
parameters.getSupportedPictureSizes()

计算最终的CameraSize算法如下

代码语言:javascript复制
  /**
     * 计算最完美的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;
    }

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

获取旋转角度的方法是

代码语言:javascript复制
  /**
     * 设置预览角度,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上,有没有做图形变换,(涉及纹理坐标和顶点坐标的问题)

0 人点赞