android camera系列(Camera1、Camera2、CameraX)的使用以及输出的图像格式

一、Camera

1.1、结合SurfaceView实现预览

1.1.1、布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".CameraActivity">
   <SurfaceView
       android:id="@+id/preview"
       android:layout_width="match_parent"
       android:layout_height="0dp"
       android:layout_weight="1"/>
</LinearLayout>

1.1.2、实现预览

mBinding.preview.getHolder().addCallback(new SurfaceHolder.Callback2() {
   
   
            @Override
            public void surfaceRedrawNeeded(@NonNull SurfaceHolder holder) {
   
   
                mCamera = Camera.open();
                try {
   
   
                    mCamera.setPreviewDisplay(holder);
                    mCamera.startPreview();
                } catch (IOException e) {
   
   
                    throw new RuntimeException(e);
                }
            }

            @Override
            public void surfaceCreated(@NonNull SurfaceHolder holder) {
   
   

            }

            @Override
            public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
   
   

            }

            @Override
            public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
   
   

            }
        });
  • Camera.open()
    打开摄像头
  • setPreviewDisplay
    设置预览展示的控件
  • startPreview
    开始预览
    在这里插入图片描述
    发现预览是横着的,需要使用setDisplayOrientation调整预览图像的方向
    在这里插入图片描述

1.1.3、获取摄像头的原始数据

mBinding.preview.getHolder().addCallback(new SurfaceHolder.Callback2() {
   
   
            @Override
            public void surfaceRedrawNeeded(@NonNull SurfaceHolder holder) {
   
   
                mCamera = Camera.open();
                try {
   
   
                    mCamera.setPreviewDisplay(holder);
                    mCamera.setDisplayOrientation(90);
                    mCamera.setPreviewCallback(new PreviewCallBack());
                    mCamera.startPreview();
                } catch (IOException e) {
   
   
                    throw new RuntimeException(e);
                }
            }

            @Override
            public void surfaceCreated(@NonNull SurfaceHolder holder) {
   
   

            }

            @Override
            public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
   
   

            }

            @Override
            public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
   
   

            }
        });
private class PreviewCallBack implements Camera.PreviewCallback {
   
   

        @Override
        public void onPreviewFrame(byte[] data, Camera camera) {
   
   
            try {
   
   
                bos.write(data);
                Camera.Size previewSize = camera.getParameters().getPreviewSize();
                Log.e(TAG, "onPreviewFrame:" + previewSize.width + "x" + previewSize.height);
                Log.e(TAG, "Image format:" + camera.getParameters().getPictureFormat());
            } catch (IOException e) {
   
   
                throw new RuntimeException(e);
            }
        }
    }

在这里插入图片描述
在这里插入图片描述

  • setPreviewCallback
    设置预览数据的回调
  • 2560*1440
    默认返回图像的分辨率
  • Image format:256
    默认返回图像的数据格式是JPEG的
    在这里插入图片描述

1.1.4、调整相机数据的分辨率和格式

Parameters
 private Camera.Size getBestPreviewSize(Camera.Parameters parameters, int desiredWidth, int desiredHeight) {
   
   
        List<Camera.Size> supportedSizes = parameters.getSupportedPreviewSizes();
        Camera.Size bestSize = null;
        int bestDiff = Integer.MAX_VALUE;

        for (Camera.Size size : supportedSizes) {
   
   
            int diff = Math.abs(size.width - desiredWidth) + Math.abs(size.height - desiredHeight);
            if (diff < bestDiff) {
   
   
                bestSize = size;
                bestDiff = diff;
            }
        }

        return bestSize;
    }
                    mCamera = Camera.open();
                try {
   
   
                    mCamera.setPreviewDisplay(holder);
                    Camera.Parameters parameters = mCamera.getParameters();
                    // 设置预览尺寸
                    int desiredWidth = 1280; // 设置所需的宽度
                    int desiredHeight = 720; // 设置所需的高度
                    Camera.Size bestSize = getBestPreviewSize(parameters, desiredWidth, desiredHeight);
                    parameters.setPreviewSize(bestSize.width, bestSize.height);
                    parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
                    //设置输出数据的格式为NV21
                    parameters.setPictureFormat(ImageFormat.NV21);
                    mCamera.setParameters(parameters);
                    mCamera.setPreviewCallback(new PreviewCallBack());
                    mCamera.setDisplayOrientation(90);
                    mCamera.startPreview();
                } catch (IOException e) {
   
   
                    throw new RuntimeException(e);
                }

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
ImageFormat里面还定义的有YUV_420_888(yuv420)、YV12,那我们可以使用吗?
在这里插入图片描述
在这里插入图片描述
运行后,应用直接抛出异常了,我用的真机是VIVO x20的,android版本是8.1的,所以,不支持设置输出的摄像头数据为YUV_420_888的。

1.1.5、保存摄像头输出的nv21格式的数据

package com.aniljing.androidcamera;

import android.content.Context;
import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.SurfaceHolder;

import com.aniljing.androidcamera.databinding.ActivityCameraBinding;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

public class CameraActivity extends AppCompatActivity {
   
   
    private final String TAG = CameraActivity.class.getSimpleName();
    private Context mContext;
    private ActivityCameraBinding mBinding;
    private Camera mCamera;
    private File mFile = new File(Environment.getExternalStorageDirectory(), "nv21.yuv");
    private BufferedOutputStream bos;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
   
   
        super.onCreate(savedInstanceState);
        mContext = this;
        mBinding = ActivityCameraBinding.inflate(getLayoutInflater());
        setContentView(mBinding.getRoot());
        try {
   
   
            if (mFile.exists()) {
   
   
                mFile.delete();
            }
            bos = new BufferedOutputStream(new FileOutputStream(mFile));
        } catch (FileNotFoundException e) {
   
   
            throw new RuntimeException(e);
        }
        mBinding.preview.getHolder().addCallback(new SurfaceHolder.Callback2() {
   
   
            @Override
            public void surfaceRedrawNeeded(@NonNull SurfaceHolder holder) {
   
   
                mCamera = Camera.open();
                try {
   
   
                    mCamera.setPreviewDisplay(holder);
                    Camera.Parameters parameters = mCamera.getParameters();
                    // 设置预览尺寸
                    int desiredWidth = 1280; // 设置所需的宽度
                    int desiredHeight = 720; // 设置所需的高度
                    Camera.Size bestSize = getBestPreviewSize(parameters, desiredWidth, desiredHeight);
                    parameters.setPreviewSize(bestSize.width, bestSize.height);
                    parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
                    parameters.setPictureFormat(ImageFormat.NV21);
                    mCamera.setParameters(parameters);
                    mCamera.setPreviewCallback(new PreviewCallBack());
                    mCamera.setDisplayOrientation(90);
                    mCamera.startPreview();
                } catch (IOException e) {
   
   
                    throw new RuntimeException(e);
                }
            }

            @Override
            public void surfaceCreated(@NonNull SurfaceHolder holder) {
   
   

            }

            @Override
            public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
   
   

            }

            @Override
            public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
   
   

            }
        });
    }

    private class PreviewCallBack implements Camera.PreviewCallback {
   
   

        @Override
        public void onPreviewFrame(byte[] data, Camera camera) {
   
   
            try {
   
   
                bos.write(data);
                Camera.Size previewSize = camera.getParameters().getPreviewSize();
                Log.e(TAG, "onPreviewFrame:" + previewSize.width + "x" + previewSize.height);
                Log.e(TAG,"Image format:"+camera.getParameters().getPictureFormat());
            } catch (IOException e) {
   
   
                throw new RuntimeException(e);
            }
        }
    }

    @Override
    protected void onDestroy() {
   
   
        super.onDestroy();
        mCamera.setPreviewCallback(null);
        mCamera.stopPreview();
        mCamera.release();
        mCamera = null;
        try {
   
   
            bos.flush();
            bos.close();
            bos = null;
        } catch (IOException e) {
   
   
            throw new RuntimeException(e);
        }
    }

    private Camera.Size getBestPreviewSize(Camera.Parameters parameters, int desiredWidth, int desiredHeight) {
   
   
        List<Camera.Size> supportedSizes = parameters.getSupportedPreviewSizes();
        Camera.Size bestSize = null;
        int bestDiff = Integer.MAX_VALUE;

        for (Camera.Size size : supportedSizes) {
   
   
            int diff = Math.abs(size.width - desiredWidth) + Math.abs(size.height - desiredHeight);
            if (diff < bestDiff) {
   
   
                bestSize = size;
                bestDiff = diff;
            }
        }

        return bestSize;
    }
}
  • 保存为yuv格式的文件
    在这里插入图片描述

  • 使用YuvEye工具打开nv21.yuv文件
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 可以看出,虽然预览已经可以竖屏显示视频了,但是我们实际保存的数据却还是横屏的,所以就需要我们手动的调整

  • 如果我们选择其他格式的,使用工具是否能够正常打开呢?
    在这里插入图片描述
    在这里插入图片描述
    我们会发现,柜子的颜色和使用正确的格式有很明显的差别的

  • 如果我们输入错误的分辨率,又是什么效果呢?
    在这里插入图片描述
    在这里插入图片描述

1.1.6、数据旋转

java方式:

private void nv21_rotate_to_90(byte[] nv21_data, byte[] nv21_rotated, int width, int height) {
   
   
        int y_size = width * height;
        int buffser_size = y_size * 3 / 2;

        // Rotate the Y luma
        int i = 0;
        int startPos = (height - 1) * width;
        for (int x = 0; x < width; x++) {
   
   
            int offset = startPos;
            for (int y = height - 1; y >= 0; y--) {
   
   
                nv21_rotated[i] = nv21_data[offset + x];
                i++;
                offset -= width;
            }
        }
        // Rotate the U and V color components
        i = buffser_size - 1;
        for (int x = width - 1; x > 0; x = x - 2) {
   
   
            int offset = y_size;
            for (int y = 0; y < height / 2; y++) {
   
   
                nv21_rotated[i] = nv21_data[offset + x];
                i--;
                nv21_rotated[</
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值