mediaCodec例子02-录屏例子

876 阅读1分钟

录屏流程

流程总共分为三部分:

  • 1.mediaCodec编码器提供surface Surface surface = mediaCodec.createInputSurface();构建codec的时候,要指定数据来源格式为MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface
  • 2.开启录屏,录屏应用将数据写入到surface中,mediaProjection.createVirtualDisplay(xx,xx,xx,surface,xx)
  • 3.codec获取outputBuffer,拷贝编码后的数据,释放buffer

完整例子

H264EncodeActivity

public class H264EncodeActivity extends AppCompatActivity {

    MediaProjectionManager projectionManager;
    MediaProjection mediaProjection;

    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_h264_encode);

        checkPermission();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode != RESULT_OK || requestCode != 1) return;

        mediaProjection = projectionManager.getMediaProjection(resultCode , data);
        H264Encoder h264Encoder = new H264Encoder(mediaProjection);
        h264Encoder.start();
    }

    public void start(View view) {
        projectionManager = (MediaProjectionManager)getSystemService(MEDIA_PROJECTION_SERVICE);
        Intent captureIntent = projectionManager.createScreenCaptureIntent();
        startActivityForResult(captureIntent, 1);
    }
}



H264Encoder

public class H264Encoder extends Thread{

    MediaProjection mediaProjection;
    MediaCodec mediaCodec;
    private int width;
    private int height;

    public H264Encoder(MediaProjection mediaProjection) {
        this.mediaProjection = mediaProjection;
        this.width = 640;
        this.height = 1920;
        configEncodeCodec();
    }

    //帧率 , I帧间隔 , 码率 , 数据格式
    // mediaCodec负责提供surface给mediaProjection使用
    private void configEncodeCodec() {

        MediaFormat mediaFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height);
        mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE , 20);
        mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 30);
        mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, width * height);
        //指定编码的数据格式是由surface决定的。
        mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT , MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);

        try{
            mediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
            mediaCodec.configure(mediaFormat , null , null , MediaCodec.CONFIGURE_FLAG_ENCODE);
            Surface surface = mediaCodec.createInputSurface();
            //到此已经完成录屏到写入codec提供的surface的过程
            mediaProjection.createVirtualDisplay("project-encoder" ,width,height , 2 ,
                    DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC , surface , null , null);
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    @Override
    public void run() {
        super.run();
        mediaCodec.start();
        try{
            MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
            while (true){
                int outIndex = mediaCodec.dequeueOutputBuffer(info , 10000);
                byte[] bytes = new byte[info.size];
                if (outIndex >= 0){
                    ByteBuffer byteBuffer = mediaCodec.getOutputBuffer(outIndex);
                    byteBuffer.get(bytes);
                    FileUtils.writeBytes(bytes);
                    FileUtils.writeContent(bytes);
                    //使用过的ByteBuffer必须释放,否则最后会提示没有可用的buffer , 这里的render=false,就是因为这里不需要渲染。跟编码过程不一样。
                    mediaCodec.releaseOutputBuffer(outIndex , false);
                }

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