录屏流程
流程总共分为三部分:
- 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();
}
}
}