mediaCodec例子03-摄像头采集

430 阅读2分钟

流程

  • 1.启动摄像头采集数据到surface中
  • 2.通过采集的回调拿到每帧数据bytes
  • 3.配置编码器,编码器数据类型为yuv420,设置码率,帧率,I帧间隔
  • 4.启动编码器,编码需要设置pts,将每帧数据bytes放到到编码器的ByteBuffer
  • 5.获取编码器输出

完整例子

CameraActivity

public class CameraActivity extends AppCompatActivity {

    public boolean checkPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(
                Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(new String[]{
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    Manifest.permission.CAMERA
            }, 1);

        }
        return false;
    }

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

        checkPermission();
    }
}

LocalSurfaceView


public class LocalSurfaceView extends SurfaceView implements SurfaceHolder.Callback , Camera.PreviewCallback {

    CameraH264Encoder h264Encode;
    private Camera.Size size;
    private Camera mCamera;
    //预览宽高    yuv  有多大   是1  不是2
    byte[] buffer;

    public LocalSurfaceView(Context context) {
        this(context,null);
    }

    public LocalSurfaceView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public LocalSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr,0);
    }

    public LocalSurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        getHolder().addCallback(this);
    }

    //摄像头由于在设计上是竖着放的,而本身是横着的,因此摄像头的数据需要旋转90度。
    private void startPreview() {
        mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
        Camera.Parameters parameters = mCamera.getParameters();
        size = parameters.getPreviewSize();
        try {
            mCamera.setPreviewDisplay(getHolder());
            mCamera.setDisplayOrientation(90);
            buffer = new byte[size.width * size.height * 3 / 2];
            mCamera.addCallbackBuffer(buffer);
            mCamera.setPreviewCallbackWithBuffer(this);
            mCamera.startPreview();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        if (h264Encode == null) {
            this.h264Encode = new CameraH264Encoder(size.width, size.height);
            h264Encode.startLive();
        }
        h264Encode.encodeFrame(data);
        mCamera.addCallbackBuffer(data);
    }

    @Override
    public void surfaceCreated(@NonNull SurfaceHolder holder) {
        startPreview();
    }

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

    }

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

    }
}

CameraH264Encoder

public class CameraH264Encoder {
    MediaCodec mediaCodec;
    int index;
    int width;
    int height;
    public CameraH264Encoder(int width, int height) {
        this.width = width;
        this.height = height;
    }

    public void startLive() {
        try {
            mediaCodec = MediaCodec.createEncoderByType("video/avc");
            MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", width, height);
            mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, width * height);
            mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 15);
            mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 2); //IDR帧刷新时间
            //这里注意,数据类型是从camera出来的yuv数据,与之前录屏不一样。录屏选的类型是surface
            mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
            mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            mediaCodec.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void encodeFrame(byte[] input) {
        int inputBufferIndex = mediaCodec.dequeueInputBuffer(10000);
        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
        if (inputBufferIndex >= 0) {
            ByteBuffer inputBuffer =   mediaCodec.getInputBuffer(inputBufferIndex);

            inputBuffer.clear();
            inputBuffer.put(input);
            //编码的时候,需要告之编码器pts
            mediaCodec.queueInputBuffer(inputBufferIndex, 0, input.length, computPts(), 0);
            index++;
        }

        int outputBufferIndex =   mediaCodec.dequeueOutputBuffer(bufferInfo,100000);
        if (outputBufferIndex >= 0) {
            ByteBuffer  outputBuffer= mediaCodec.getOutputBuffer(outputBufferIndex);
            byte[] data = new byte[bufferInfo.size];
            outputBuffer.get(data);
            FileUtils.writeBytes(data);
            FileUtils.writeContent(data);
            mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
        }
    }

    public int computPts() {
        return 1000000 / 15 * index;
    }
}