preface

In Android audio and video development, online knowledge is too fragmented, self-learning is very difficult, but audio and video cattle Jhuster put forward “Android audio and video from the entry to improve – task list”. This is the third installment of the Android Audio and Video task list, and the corresponding contents to learn are: In Android platform using Camera API for video collection, respectively using SurfaceView, TextureView to preview Camera data, get NV21 data callback (for example, the NV21 data will be displayed in ImageView control after processing).


Audio and video task list

Audio and video task list: Click here to jump to view


A, directories,


(I) Steps of Camera data collection

(1) Open the camera

 mCamera = Camera.open();
Copy the code

(2) Set the preview data interface of the camera

Call setPreviewDisplay to set the SurfaceHolder, which is bound to the SurfaceView. The setPreviewTexture method is called to set SurfaceTexture, which is bound to TextureView

(3) Get camera. Parameters parameter information (if it is a simple preview, there is no need to set parameter information)

Camera.Parameters parameters = mCamera.getParameters(); // Get camera parameters

// You can set parameters as required
// Zoom the lens
parameters.setZoom();  
// Set the size of the preview photo
parameters.setPreviewSize(200.200);
// Sets the minimum and maximum number of frames per second to display when previewing photos
parameters.setPreviewFpsRange(4.10);
// Set the image format
parameters.setPictureFormat(ImageFormat.JPEG);
Image quality [0-100],100 is the highest
parameters.set("jpeg-quality".85);
// Set the size of the photo
parameters.setPictureSize(200.200);

mCamera.setParameters(parameters);
Copy the code

(4) after setting the added parameters back, call startPreview to startPreview

mCamera.startPreview();
Copy the code

(5) Release the camera

  mCamera.release();
Copy the code

Note that the Camera must be released after use, otherwise the Camera can not be called elsewhere.


(2) SurfaceView to preview Camera data

package com.lzacking.cameraapidemo;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;

import java.io.IOException;

public class CameraSurfaceViewActivity extends AppCompatActivity implements SurfaceHolder.Callback {

    private SurfaceView mSurfaceView;
    private Camera mCamera;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera_surface_view);

        mSurfaceView = findViewById(R.id.surfaceView);
        mSurfaceView.getHolder().addCallback(this);

        // Open the camera and turn the display direction 90 degrees
        mCamera = Camera.open(0);
        mCamera.setDisplayOrientation(90);
    }


    //------ SurfaceView preview -------
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // Initialize the control when it is created
        try {
            mCamera.setPreviewDisplay(holder);
            // Start preview
            mCamera.startPreview();
        } catch(IOException e) { e.printStackTrace(); }}@Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        // You can perform corresponding operations when changing
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // Release the cameramCamera.release(); }}Copy the code

(3) TextureView to preview Camera data

package com.lzacking.cameraapidemo;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.os.Bundle;
import android.view.TextureView;

import java.io.IOException;

public class CameraTextureViewActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener {

    private TextureView mTextureView;
    private Camera mCamera;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera_texture_view);

        mTextureView = findViewById(R.id.textureView);
        mTextureView.setSurfaceTextureListener(this);

        // Open the camera and turn the display direction 90 degrees
        mCamera = Camera.open(0);
        mCamera.setDisplayOrientation(90);
    }


    //------ TextureView preview -------
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        // Initialize the control when it is created
        try {
            mCamera.setPreviewTexture(surface);
            // Start preview
            mCamera.startPreview();
        } catch(IOException e) { e.printStackTrace(); }}@Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
        // You can perform corresponding operations when changing
    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        // Release the camera
        mCamera.release();
        return false;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {}}Copy the code

(4) Get NV21 data callback

The YUV format of Camera Preview Callback supported by Google in Android is NV21 and YV12. Android generally uses the YCbCr_420_SP format (NV21) by default.

Camera.Parameters parameters = camera.getParameters();
    parameters.setPreviewFormat(ImageFormat.NV21);
    camera.setParameters(parameters);

    // Listen for previewcallback via setPreviewCallback:
    camera.setPreviewCallback(new Camera.PreviewCallback() {
        @Override
        public void onPreviewFrame(byte[] bytes, Camera camera) {
            // The Bytes are in NV21 format}});Copy the code

Use ImageView to display NV21 data

mCamera.setPreviewCallback(new Camera.PreviewCallback() {
    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        // Process data, where the data is in NV21 format, and display the data on the ImageView control
        mPreviewSize = camera.getParameters().getPreviewSize();// Get the size for the format conversion
        
        / / remove hair YUVIMAGE
        YuvImage yuvimage = new YuvImage(
                data,
                ImageFormat.NV21,
                mPreviewSize.width,
                mPreviewSize.height,
                null);
        mBaos = new ByteArrayOutputStream();
        
        // YuvImage is converted to JPG format
        yuvimage.compressToJpeg(new Rect(0.0, mPreviewSize.width, mPreviewSize.height), 100, mBaos);// 80--JPG image quality [0-100],100 highest
        mImageBytes = mBaos.toByteArray();

        // Convert mImageBytes to bitmap
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPreferredConfig = Bitmap.Config.RGB_565;

        mBitmap = BitmapFactory.decodeByteArray(mImageBytes, 0, mImageBytes.length, options); mImageView.setImageBitmap(rotateBitmap(mBitmap, getDegree())); }});Copy the code

(v) Complete code

(1) Layout

Activity_main. XML:

<? 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"
    tools:context=".MainActivity"
    android:orientation="vertical"
    android:gravity="center">

    <Button
        android:id="@+id/btn_surfaceview"
        android:text="SurfaceView preview camera data"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/btn_textureview"
        android:text="TextureView camera data preview"
        android:textAllCaps="false"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"/>

    <Button
        android:id="@+id/btn_nv21data_callback"
        android:text="Call back NV21 data."
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"/>

</LinearLayout>
Copy the code

Activity_camera_surface_view. XML:

<? 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"
    tools:context=".CameraSurfaceViewActivity">

    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </SurfaceView>

</LinearLayout>
Copy the code

Activity_camera_texture_view. XML:

<? 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"
    tools:context=".CameraTextureViewActivity">

    <TextureView
        android:id="@+id/textureView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>
Copy the code

Activity_nv21data_callback. XML:

<? 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"
    tools:context=".NV21DataCallbackActivity"
    android:orientation="vertical">

    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="match_parent"
        android:layout_height="400dp" />

    <ImageView
        android:id="@+id/iv_imageview"
        android:layout_width="match_parent"
        android:layout_height="200dp">
    </ImageView>

</LinearLayout>
Copy the code

(2) Code

MainActivity.java

package com.lzacking.cameraapidemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button mSurfaceView;
    private Button mTextureView;
    private Button mNv21DataCallback;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // SurfaceView previews camera data
        mSurfaceView = findViewById(R.id.btn_surfaceview);
        mSurfaceView.setOnClickListener(this);

        // TextureView previews camera data
        mTextureView = findViewById(R.id.btn_textureview);
        mTextureView.setOnClickListener(this);

        // The obtained NV21 data is called back
        mNv21DataCallback = findViewById(R.id.btn_nv21data_callback);
        mNv21DataCallback.setOnClickListener(this);

        // Request permission
        if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) ! = PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(MainActivity.this.new String[]{Manifest.permission.CAMERA}, 1); }}@Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode) {
            case 1:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Log.i("info"."onRequestPermissionsResult: " + "Permission requested");
                } else {
                    Toast.makeText(this."You need to turn on camera permissions.", Toast.LENGTH_LONG).show();
                }
                break;
            default:}}@Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_surfaceview:
                Intent intentSurfaceView = new Intent(this, CameraSurfaceViewActivity.class);
                startActivity(intentSurfaceView);
                break;

            case R.id.btn_textureview:
                Intent intentTextureView = new Intent(this, CameraTextureViewActivity.class);
                startActivity(intentTextureView);
                break;

            case R.id.btn_nv21data_callback:
                Intent intentNV21DataCallback = new Intent(this, NV21DataCallbackActivity.class);
                startActivity(intentNV21DataCallback);
                break;

            default:
                break; }}}Copy the code

CameraSurfaceViewActivity.java

package com.lzacking.cameraapidemo;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;

import java.io.IOException;

public class CameraSurfaceViewActivity extends AppCompatActivity implements SurfaceHolder.Callback {

    private SurfaceView mSurfaceView;
    private Camera mCamera;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera_surface_view);

        mSurfaceView = findViewById(R.id.surfaceView);
        mSurfaceView.getHolder().addCallback(this);

        // Open the camera and turn the display direction 90 degrees
        mCamera = Camera.open(0);
        mCamera.setDisplayOrientation(90);
    }

    //------ SurfaceView preview -------
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // Initialize the control when it is created
        try {
            mCamera.setPreviewDisplay(holder);
            // Start preview
            mCamera.startPreview();
        } catch(IOException e) { e.printStackTrace(); }}@Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        // You can perform corresponding operations when changing
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // Release the cameramCamera.release(); }}Copy the code

CameraTextureViewActivity.java

package com.lzacking.cameraapidemo;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.os.Bundle;
import android.view.TextureView;

import java.io.IOException;

public class CameraTextureViewActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener {

    private TextureView mTextureView;
    private Camera mCamera;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera_texture_view);

        mTextureView = findViewById(R.id.textureView);
        mTextureView.setSurfaceTextureListener(this);

        // Open the camera and turn the display direction 90 degrees
        mCamera = Camera.open(0);
        mCamera.setDisplayOrientation(90);
    }

    //------ TextureView preview -------
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        // Initialize the control when it is created
        try {
            mCamera.setPreviewTexture(surface);
            // Start preview
            mCamera.startPreview();
        } catch(IOException e) { e.printStackTrace(); }}@Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
        // You can perform corresponding operations when changing
    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        // Release the camera
        mCamera.release();
        return false;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {}}Copy the code

NV21DataCallbackActivity

package com.lzacking.cameraapidemo;

import androidx.appcompat.app.AppCompatActivity;

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.os.Bundle;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.ImageView;

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

public class NV21DataCallbackActivity extends AppCompatActivity implements SurfaceHolder.Callback {

    private Camera mCamera;
    private SurfaceView mSurfaceView;

    private Camera.Size mPreviewSize; // Preview the size
    private ByteArrayOutputStream mBaos;
    private byte[] mImageBytes;
    private Bitmap mBitmap;
    private ImageView mImageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_nv21data_callback);
        mImageView = findViewById(R.id.iv_imageview);

        mSurfaceView = findViewById(R.id.surfaceView);
        mSurfaceView.getHolder().addCallback(this);

        // Open the camera and turn the display direction 90 degrees
        mCamera = Camera.open(0);
        mCamera.setDisplayOrientation(90);
    }


    @Override
    public void surfaceCreated(SurfaceHolder holder) {}// Window changes
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        doChange(holder);
    }


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

    // When our program starts running, the surFaceView displays the content obtained by the current camera, and the obtained NV21 data is displayed on the ImageView control
    private void doChange(SurfaceHolder holder) {
        try {
            mCamera.setPreviewDisplay(holder);// Set the camera preview screen
            // Set the rotation Angle of the surfaceView
            mCamera.setDisplayOrientation(getDegree());

            if(mCamera ! =null ) {
                try {
                    Camera.Parameters parameters = mCamera.getParameters(); // Get camera parameters

                    // You can set parameters as required
                    // Zoom the lens
                    // parameters.setZoom();

                    // Set the size of the preview photo
                    // parameters.setPreviewSize(200, 200);

                    // Sets the minimum and maximum number of frames per second to display when previewing photos
                    // parameters.setPreviewFpsRange(4, 10);

                    // Set the image format
                    // parameters.setPictureFormat(ImageFormat.JPEG);

                    Image quality [0-100],100 is the highest
                    // parameters.set("jpeg-quality", 85);

                    // Set the size of the photo
                    // parameters.setPictureSize(200, 200);

                    // Set the image format of the preview image
                    parameters.setPreviewFormat(ImageFormat.NV21);

                    mCamera.setParameters(parameters);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            mCamera.setPreviewCallback(new Camera.PreviewCallback() {
                @Override
                public void onPreviewFrame(byte[] data, Camera camera) {
                    // Process data, where the data is in NV21 format, and display the data on the ImageView control
                    mPreviewSize = camera.getParameters().getPreviewSize();// Get the size for the format conversion
                    / / remove hair YUVIMAGE
                    YuvImage yuvimage = new YuvImage(
                            data,
                            ImageFormat.NV21,
                            mPreviewSize.width,
                            mPreviewSize.height,
                            null);
                    mBaos = new ByteArrayOutputStream();
                    // YuvImage is converted to JPG format
                    yuvimage.compressToJpeg(new Rect(0.0, mPreviewSize.width, mPreviewSize.height), 100, mBaos);// 80--JPG image quality [0-100],100 highest
                    mImageBytes = mBaos.toByteArray();

                    // Convert mImageBytes to bitmap
                    BitmapFactory.Options options = new BitmapFactory.Options();
                    options.inPreferredConfig = Bitmap.Config.RGB_565;

                    mBitmap = BitmapFactory.decodeByteArray(mImageBytes, 0, mImageBytes.length, options); mImageView.setImageBitmap(rotateBitmap(mBitmap, getDegree())); }}); mCamera.startPreview();// Start preview

        } catch(IOException e) { e.printStackTrace(); }}private int getDegree(a) {
        // Get the current screen rotation Angle
        int rotating = this.getWindowManager().getDefaultDisplay().getRotation();
        int degree = 0;/ / degree
        // Set the surfaceView display Angle according to the rotation Angle of the phone
        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;
    }


    /** * select transform *@paramOrigin original *@paramDegree Degree of rotation, positive or negative *@returnRotated image */
    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);
        // Rotate around the place
        Bitmap newBM = Bitmap.createBitmap(origin, 0.0, width, height, matrix, false);
        if (newBM.equals(origin)) {
            return newBM;
        }
        origin.recycle();
        returnnewBM; }}Copy the code

(3) Permission

<uses-permission android:name="android.permission.CAMERA" />
Copy the code

Source code: Android audio and video development foundation (three) : The Android platform using Camera API for video collection, and preview Camera data, get NV21 data callback