专栏首页微卡智享Android利用SurfaceView显示Camera图像爬坑记(四)

Android利用SurfaceView显示Camera图像爬坑记(四)

前言

通过前面几篇,利用SurfaceView显示Camera的图像已经没什么问题了,接下来我们就要打磨一下细节,主要就是手机旋转的问题,考虑到我们会用横屏和竖屏的不同的情况。

横竖屏问题

用我们前面的DEMO后,因为默认的打开后就是竖屏,所以进入后图像没有问题,如下

但是我们切换到横屏后,图像就显示不对了,如下

原来的代码中我们也只是解决了默认竖屏的问题

在StartCamera中,设置了直接旋转90度(不过我们改为SurfaceTexture这样显示后,这个没什么用了)

然后生成图像的方法nv21ToBitmap中加入了旋转90度

实现思路

  1. 获取到手机当前的旋转角度
  2. 根据当前旋转的角度修改生成图像的角度
  3. 生成的图像后加入角度旋转

代码实现

首先把setDisplayOrientation这个屏蔽掉了,因为已经没用了

获取到手机当前的旋转角度

想要获取手机的当前旋转角度,就要用到WindowManager,所以我们要加入WindowManager的定义,还要加入一个默认的角度值。然后在构造函数中获取到当前windowmanager。

根据当前旋转的角度修改生成图像的角度

记得当时有三个重写的参数surfaceCreated,surfaceChanged和surfaceDestoryed,其中我们在surfaceCreated中加入了打开Camera,在surfaceDestoryed中加入了关闭Camera,而surfaceChanged中什么也没写,这次这个方法就被我们用到了。

    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
        int angel = windowManager.getDefaultDisplay().getRotation();
        switch (angel) {
            case Surface.ROTATION_0:
                rotatedegree=90;
                break;
            case Surface.ROTATION_90:
                rotatedegree=0;
                break;
            case Surface.ROTATION_180:
                rotatedegree=270;
                break;
            case Surface.ROTATION_270:
                rotatedegree=180;
                break;
            default:
                rotatedegree=0;
                break;
        }
    }

上面就是通过判断手机旋转角度,改变我们定义的retatedegree的值,这个是我根据旋转的情况自己算出来的,生成图像旋转的角度。

生成的图像后加入角度旋转

最后在我们nv21ToBitmap中的旋转角度改为我们已经定义的retatedegree

视频效果

基本到这,我们这个SurfaceView调Camera的类就完成了,这里我把整个类的代码放到这里,后面可能会在这上面加入通过OpenCV NDK的图像处理,这里做一个备份吧。

package dem.vac.surfaceviewdemo;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.graphics.YuvImage;
import android.hardware.Camera;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * 作者:Vaccae
 * 邮箱:3657447@qq.com
 * 创建时间:2019-06-12 15:42
 * 功能模块说明:
 */
public class VaccaeSurfaceView extends SurfaceView implements SurfaceHolder.Callback {

    //Camera相关
    //设置摄像头ID,我们默认为后置
    private int mCameraIndex=Camera.CameraInfo.CAMERA_FACING_BACK;
    //定义摄像机
    private Camera camera;
    //定义Camera的回调方法
    private Camera.PreviewCallback previewCallback=new Camera.PreviewCallback() {
        @Override
        public void onPreviewFrame(byte[] bytes, Camera camera) {
            synchronized (this) {
                int width=camera.getParameters().getPreviewSize().width;
                int height=camera.getParameters().getPreviewSize().height;
                Log.d("frame", "width:"+width+" height:"+height);
                Canvas canvas=holder.lockCanvas();
                if (canvas != null) {
                    Log.i("frame", "canvas width:" + canvas.getWidth() + " height:" + canvas.getHeight());
                    canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR);
                    Bitmap cacheBitmap=nv21ToBitmap(bytes, width, height);

                    RectF rectF=new RectF(0, 0, canvas.getWidth(), canvas.getHeight());
                    canvas.drawBitmap(cacheBitmap, null, rectF, null);

                    holder.unlockCanvasAndPost(canvas);
                }
            }
        }
    };

    //定义SurfaceHolder
    private SurfaceHolder holder;
    //定义SurfaceTexture;
    private SurfaceTexture surfaceTexture;

    //WindowManager 用于获取摄像机方向
    private WindowManager windowManager;
    //当前相机角度
    private int rotatedegree=0;


    //构造函数
    public VaccaeSurfaceView(Context context) {
        super(context);

        //获取WindowManager
        windowManager=(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

        //实例化Surfacetexture
        surfaceTexture=new SurfaceTexture(2);
        //获取Holder
        holder=getHolder();
        //加入SurfaceHolder.Callback在类中implements
        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        //保持屏幕常亮
        holder.setKeepScreenOn(true);
    }

    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
        //开启摄像机
        startCamera(mCameraIndex);
    }

    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
        int angel = windowManager.getDefaultDisplay().getRotation();
        switch (angel) {
            case Surface.ROTATION_0:
                rotatedegree=90;
                break;
            case Surface.ROTATION_90:
                rotatedegree=0;
                break;
            case Surface.ROTATION_180:
                rotatedegree=270;
                break;
            case Surface.ROTATION_270:
                rotatedegree=180;
                break;
            default:
                rotatedegree=0;
                break;
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
        //关闭摄像机
        stopCamera();
    }

    //region 开启关闭Camera
    //开启摄像机
    private void startCamera(int mCameraIndex) {
        // 初始化并打开摄像头
        if (camera == null) {
            try {
                camera=Camera.open(mCameraIndex);
            } catch (Exception e) {
                return;
            }
            //获取Camera参数
            Camera.Parameters params=camera.getParameters();
            if (params.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
                // 自动对焦
                params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
            }
            params.setPreviewFormat(ImageFormat.NV21); // 设置预览图片格式
            params.setPictureFormat(ImageFormat.JPEG); // 设置拍照图片格式

            camera.setParameters(params);

            try {
                camera.setPreviewCallback(previewCallback);
                camera.setPreviewTexture(surfaceTexture);
//                camera.setPreviewDisplay(holder);
                //旋转90度
//                camera.setDisplayOrientation(90);
                camera.startPreview();
            } catch (Exception ex) {
                ex.printStackTrace();
                camera.release();
                camera=null;
            }
        }
    }

    //关闭摄像机
    private void stopCamera() {
        if (camera != null) {
            camera.setPreviewCallback(null);
            camera.stopPreview();
            camera.release();
            camera=null;
        }

    }
    //endregion

    private Bitmap nv21ToBitmap(byte[] nv21, int width, int height) {
        Bitmap bitmap=null;
        try {
            YuvImage image=new YuvImage(nv21, ImageFormat.NV21, width, height, null);
            ByteArrayOutputStream stream=new ByteArrayOutputStream();
            image.compressToJpeg(new Rect(0, 0, width, height), 80, stream);
            //将rawImage转换成bitmap
            BitmapFactory.Options options=new BitmapFactory.Options();
            options.inPreferredConfig=Bitmap.Config.ARGB_8888;
            bitmap=BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size(), options);

            //加入图像旋转
            Matrix m=new Matrix();
            m.postRotate(rotatedegree);
            bitmap=Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
                    m, true);

            stream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bitmap;
    }



}

-END-

本文分享自微信公众号 - 微卡智享(VaccaeShare),作者:Vaccae

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-07-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Android利用SurfaceView显示Camera图像爬坑记(一)

    前面文章《学习《实时Opencv处理后的Camera图像Android NDK方法》笔记》我们利用NDK处理OpenCV的图像,并在里面下载了一个DEMO进行处...

    Vaccae
  • 分享|C#解析深浅拷贝

    前面我们学习完了设计模式,在其中我们有了解到原型模式。这里涉及到了克隆自身对象。那么也就是对对象进行拷贝。这里就涉及到了这么一个概念。深浅拷贝、何为深拷贝何为浅...

    Vaccae
  • C++ OpenCV特征提取之自定义角点检测器(一)

    我们在前面学习了《C++ OpenCV特征提取之Harris角点检测》和《C++ OpenCV特征提取之Shi-Tomasi角点检测》,今天我们再来学习一下自定...

    Vaccae
  • 栈的定义、分类

            1.函数调用:一个函数内部调用另外一个函数。例A函数内部调用了B函数,B函数内部又调用了C函数,则A函数地址会先进栈,其次是B,最后是C,所以C...

    爱学习的孙小白
  • LeetCode312. 戳气球

     方法:动态规划,定义二维数组coins,coinsa表示把第a个气球和第b个气球之间(不包括a和b)的气球戳烂,最大能得到的分值

    mathor
  • C++随笔(二)用指针强制访问private的值

    private本来是私有变量,外部无法访问的,但是抖个机灵,我们用指向类的指针和在类里面不断偏移我们的指针地址来访问私有成员变量的值。

    Pulsar-V
  • Leetcode 547. Friend Circles

    版权声明:博客文章都是作者辛苦整理的,转载请注明出处,谢谢! https://blog.csdn....

    Tyan
  • 10:矩阵转置

    10:矩阵转置 总时间限制: 1000ms 内存限制: 65536kB描述 输入一个n行m列的矩阵A,输出它的转置AT。 输入第一行包含两个整数n和m,...

    attack
  • 1893. [国家集训队2011]等差子序列(bitset)

    ★★   输入文件:nt2011_sequence.in   输出文件:nt2011_sequence.out 简单对比 时间限制:0.3 s   内存限制:5...

    attack
  • BBS论坛(八)

    zhang_derek

扫码关注云+社区

领取腾讯云代金券

玩转腾讯云 有奖征文活动