Temas de audio y video de Android (3) Recopile video en la cámara de Android, use SurfaceView y TextureView para obtener una vista previa de los datos de la cámara y obtenga la devolución de llamada de datos de NV21


TextureView se agregó después de Android 4.0. TextureView debe funcionar en un entorno en el que la aceleración de hardware esté activada, es decir, en el elemento de configuración Actividad en el archivo de configuración: android: hardwareAccelerated = "true" El atributo predeterminado es true, por lo que no es necesario escribirlo. Pero si está escrito como falso, la devolución de llamada onSurfaceTextureAvailable () no entrará.

La cámara recopila datos

  • Enciende la camara

      mCamera = Camera.open();
    
  • Configurar la interfaz de datos de vista previa de la cámara

    En general, hay dos formas de obtener una vista previa, una es llamar al método setPreviewDisplay para configurar SurfaceHolder, que está vinculado a SurfaceView, y la otra es llamar al método setPreviewTexture para configurar SurfaceTexture, que está vinculado a GLSurfaceView y TextureView

  • Obtener información de parámetros de Camera.Parameters

    Camera.Parameters parameters = mCamera.getParameters(); //获取摄像头参数 可以根据情况设置参数
    
  • Después de volver a configurar la información del parámetro agregado, llame a startPreview para comenzar a obtener una vista previa del efecto

    mCamera.startPreview();
    
  • Cámara de lanzamiento

      mCamera.release();
    

Tenga en cuenta que la cámara debe soltarse después de que se agote; de ​​lo contrario, no se podrá llamar a la cámara desde otros lugares.

Vista previa de los datos de la cámara con SurfaceView

 

    public class SurfaceViewActivity extends AppCompatActivity implements SurfaceHolder.Callback {

    private SurfaceView mSurfaceView;
    private Camera mCamera;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        requestWindowFeature(Window.FEATURE_NO_TITLE);// 去掉标题栏
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);// 设置全屏

        setContentView(R.layout.activity_surface_view);

        mSurfaceView = findViewById(R.id.surface_view);
        mSurfaceView.getHolder().addCallback(this);
        mSurfaceView.setKeepScreenOn(true);

        // 打开摄像头并将展示方向旋转90度
        camera = Camera.open();
        camera.setDisplayOrientation(90);
   
    }

   //------ Surface 预览 -------
    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
            //在控件创建的时候,进行相应的初始化工作
           try {
            mCamera.setPreviewDisplay(surfaceHolder);
            mCamera.startPreview();//开始预览

        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int w, int h) {
        //变化时,可以做相应操作
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
        mCamera.release();
    }
}

Use TextureView para obtener una vista previa de los datos de la cámara

 

public class TextureViewActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener{

    TextureView textureView;
    Camera camera;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_texture_view);
        textureView = findViewById(R.id.texture_view);
        textureView.setSurfaceTextureListener(this);

        // 打开摄像头并将展示方向旋转90度
        camera = Camera.open();
        camera.setDisplayOrientation(90);
    }

   //------ Texture 预览 -------
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) {
        try {
            camera.setPreviewTexture(surfaceTexture);
            camera.startPreview();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) {

    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
        camera.release();//释放相机
        return false;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {

    }
}

Obtenga la devolución de llamada de datos de NV21

Hay dos formatos YUV comunes para la devolución de llamada de vista previa de la cámara admitidos por Google en Android: uno es NV21 y el otro es YV12. Android generalmente usa el formato YCbCr_420_SP (NV21) de forma predeterminada. El formato de la devolución de llamada de datos se puede configurar. como sigue:

 

Camera.Parameters parameters = camera.getParameters();
    parameters.setPreviewFormat(ImageFormat.NV21);
    camera.setParameters(parameters);
    通过setPreviewCallback方法监听预览的回调:
    camera.setPreviewCallback(new Camera.PreviewCallback() {
        @Override
        public void onPreviewFrame(byte[] bytes, Camera camera) {
            //这里面的Bytes的数据就是NV21格式的数据。
        }
    });
    

A continuación, use ImageView para mostrar los datos obtenidos en NV21.

 

mCamera.setPreviewCallback(new Camera.PreviewCallback() {
                @Override
                public void onPreviewFrame(byte[] data, Camera camera) {
                    //处理data
                    mPreviewSize = camera.getParameters().getPreviewSize();//获取尺寸,格式转换的时候要用到
                    //取发YUVIMAGE
                    YuvImage yuvimage = new YuvImage(
                            data,
                            ImageFormat.NV21,
                            mPreviewSize.width,
                            mPreviewSize.height,
                            null);
                    mBaos = new ByteArrayOutputStream();
                    //yuvimage 转换成jpg格式
                    yuvimage.compressToJpeg(new Rect(0, 0, mPreviewSize.width, mPreviewSize.height), 100, mBaos);// 80--JPG图片的质量[0-100],100最高
                    mImageBytes = mBaos.toByteArray();

                    //将mImageBytes转换成bitmap
                    BitmapFactory.Options options = new BitmapFactory.Options();
                    options.inPreferredConfig = Bitmap.Config.RGB_565;

                    mBitmap = BitmapFactory.decodeByteArray(mImageBytes, 0, mImageBytes.length, options);
                    icon.setImageBitmap(mBitmap);
//                    icon.setImageBitmap(rotateBitmap(mBitmap,getDegree()));
                }
            });

Código completo

 

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.hardware.Camera;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;

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

public class SurfaceViewActivity extends AppCompatActivity implements SurfaceHolder.Callback {

    private SurfaceView mSurfaceView;
    private Camera mCamera;

    private ImageView icon;
    private ByteArrayOutputStream mBaos;
    private byte[] mImageBytes;
    private Bitmap mBitmap;
    private Camera.Size mPreviewSize;//预览尺寸大小

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        requestWindowFeature(Window.FEATURE_NO_TITLE);// 去掉标题栏
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);// 设置全屏

        setContentView(R.layout.activity_surface_view);

        mSurfaceView = findViewById(R.id.surface_view);
        mSurfaceView.getHolder().addCallback(this);
        mSurfaceView.setKeepScreenOn(true);
        icon = findViewById(R.id.icon);

    }

//    //------ Surface 预览 -------
    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
            //在控件创建的时候,进行相应的初始化工作
        mCamera = Camera.open();
    }


    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int w, int h) {
        doChange(surfaceHolder);
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
        mCamera.setPreviewCallback(null);
        mCamera.stopPreview();
        mCamera.release();
    }
    //当我们的程序开始运行,即使我们没有开始录制视频,我们的surFaceView中也要显示当前摄像头显示的内容
    private void doChange(SurfaceHolder holder) {
        try {
            mCamera.setPreviewDisplay(holder);//设置摄像机的预览界面  一般都是与SurfaceView#SurfaceHolder进行绑定
            //设置surfaceView旋转的角度,系统默认的录制是横向的画面
            mCamera.setDisplayOrientation(getDegree());

            if (mCamera != null ){
                try{
                    Camera.Parameters parameters = mCamera.getParameters(); //获取摄像头参数 
//                    可以根据情况设置参数
//                    parameters.setZoom();  //镜头缩放
                    // 设置预览照片的大小
//                    parameters.setPreviewSize(200, 200);
                    // 设置预览照片时每秒显示多少帧的最小值和最大值
//                    parameters.setPreviewFpsRange(4, 10);
                    // 设置图片格式
//                    parameters.setPictureFormat(ImageFormat.JPEG);
                    // 设置JPG照片的质量  图片的质量[0-100],100最高
//                    parameters.set("jpeg-quality", 85);
                    // 设置照片的大小
//                    parameters.setPictureSize(200, 200);
                    mCamera.setParameters(parameters);
                }
                catch (Exception e) {
                    e.printStackTrace();
                    Log.e("lu","Camera设置的参数错误:"+e.getMessage());
                }
            }

            mCamera.setPreviewCallback(new Camera.PreviewCallback() {
                @Override
                public void onPreviewFrame(byte[] data, Camera camera) {
                    //处理data
                    mPreviewSize = camera.getParameters().getPreviewSize();//获取尺寸,格式转换的时候要用到
                    //取发YUVIMAGE
                    YuvImage yuvimage = new YuvImage(
                            data,
                            ImageFormat.NV21,
                            mPreviewSize.width,
                            mPreviewSize.height,
                            null);
                    mBaos = new ByteArrayOutputStream();
                    //yuvimage 转换成jpg格式
                    yuvimage.compressToJpeg(new Rect(0, 0, mPreviewSize.width, mPreviewSize.height), 100, mBaos);// 80--JPG图片的质量[0-100],100最高
                    mImageBytes = mBaos.toByteArray();

                    //将mImageBytes转换成bitmap
                    BitmapFactory.Options options = new BitmapFactory.Options();
                    options.inPreferredConfig = Bitmap.Config.RGB_565;

                    mBitmap = BitmapFactory.decodeByteArray(mImageBytes, 0, mImageBytes.length, options);
//                    icon.setImageBitmap(mBitmap);
                    icon.setImageBitmap(rotateBitmap(mBitmap,getDegree()));
                }
            });
            mCamera.startPreview();//开始预览

        } catch (IOException e) {
            e.printStackTrace();
            Log.e("lu","Camera预览变化错误:"+e.getMessage());
        }
    }

    private int getDegree() {
        //获取当前屏幕旋转的角度
        int rotating = this.getWindowManager().getDefaultDisplay().getRotation();
        int degree = 0;//度数
        //根据手机旋转的角度,来设置surfaceView的显示的角度
        switch (rotating) {
            case Surface.ROTATION_0:
                degree = 90;
                break;
            case Surface.ROTATION_90:
                degree = 0;
                break;
            case Surface.ROTATION_180:
                degree = 270;
                break;
            case Surface.ROTATION_270:
                degree = 180;
                break;
        }
        return degree;
    }


    /**
     * 选择变换
     * @param origin 原图
     * @param degree  旋转角度,可正可负
     * @return 旋转后的图片
     */
    private Bitmap rotateBitmap(Bitmap origin, float degree) {
        if (origin == null) {
            return null;
        }
        int width = origin.getWidth();
        int height = origin.getHeight();
        Matrix matrix = new Matrix();
        matrix.setRotate(degree);
        // 围绕原地进行旋转
        Bitmap newBM = Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false);
        if (newBM.equals(origin)) {
            return newBM;
        }
        origin.recycle();
        return newBM;
    }

}

para resumir

  • Debido a que el sistema solo puede encender una cámara al mismo tiempo de manera predeterminada, ya sea la cámara frontal o la cámara trasera, debe soltarse cuando no esté en uso.
  • La cámara en Android distingue las cámaras delantera y trasera, por lo que aquí hay una función de cambio entre las cámaras delantera y trasera (la función de cambio no está escrita arriba)
  • Preste atención al ángulo de rotación del lienzo de vista previa.
  • La cámara debe asegurarse de que SurfaceTexture de TextureView esté activa al ejecutar startPreview. Si la devolución de llamada onSurfaceTextureAvailable () no aparece debido a algunas razones de rendimiento, la vista previa no se abrirá. Si esto sucede, realice las operaciones open y startPreview en la devolución de llamada onSurfaceTextureAvailable () para asegurarse de que sea infalible.
  • Comparación de los controles de visualización SurfaceView y TextureView para fotogramas de vídeo

Supongo que te gusta

Origin blog.csdn.net/xfb1989/article/details/113353740
Recomendado
Clasificación