OpenGLES渲染画面通过MediaCodec录制

录制原理

  • 预览

通过fbo处理视频数据,通过samplerExternalOES纹理来创建SurfaceTexture,这样的话摄像头数据就和fbo相关联,具体可以看OpenGLES通过SurfaceTexture预览摄像头画面

  • 录制

通过MediaCodec创建一个surface,然后通过创建一个新的egl环境共享预览的EglContext和这个surface绑定,渲染fbo绑定的纹理,即可录制。
egl环境配置:
Android配置EGL环境
Android自定义GLSurfaceView

流程如下图所示:
录制原理

MediaCodec录制主要代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93

private MediaMuxer mMediaMuxer;
private MediaCodec.BufferInfo mBuffInfo;
private MediaCodec mVideoEncodec;
private int width, height;


//初始化
public void initEncoder(EGLContext eglContext,String savePath,String mineType,int width,int height){
this.width = width;
this.height = height;
this.mEGLContext = eglContext;
initMediaEncoder(savePath,mineType,width,height);
}

private void initMediaEncoder(String savePath, String mineType, int width, int height) {
try {
mMediaMuxer = new MediaMuxer(savePath,MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
initVideoEncoder(mineType,width,height);
} catch (IOException e) {
e.printStackTrace();
}
}

private void initVideoEncoder(String mineType, int width, int height) {
try {
mVideoEncodec= MediaCodec.createEncoderByType(mineType);

MediaFormat videoFormat = MediaFormat.createVideoFormat(mineType,width,height);
videoFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE,30);//30帧
videoFormat.setInteger(MediaFormat.KEY_BIT_RATE,width*height*4);//RGBA
videoFormat.setInteger(MediaFormat.KEY_BIT_RATE,width*height*4);//RGBA
//设置压缩等级 默认是baseline
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
videoFormat.setInteger(MediaFormat.KEY_PROFILE,MediaCodecInfo.CodecProfileLevel.AVCProfileMain);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
videoFormat.setInteger(MediaFormat.KEY_LEVEL, MediaCodecInfo.CodecProfileLevel.AVCLevel3);
}
}

mVideoEncodec.configure(videoFormat,null,null,MediaCodec.CONFIGURE_FLAG_ENCODE);

mBuffInfo = new MediaCodec.BufferInfo();
mSurface = mVideoEncodec.createInputSurface();
} catch (IOException e) {
e.printStackTrace();
mVideoEncodec=null;
mBuffInfo=null;
mSurface=null;
}
}


//开始录制
public void startRecode(){
videoEncodec.start();
int outputBufferIndex = videoEncodec.dequeueOutputBuffer(videoBufferinfo, 0);
if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
videoTrackIndex = mediaMuxer.addTrack(videoEncodec.getOutputFormat());
mediaMuxer.start();
}else {
while (outputBufferIndex>=0){
ByteBuffer outputBuffer= videoEncodec.getOutputBuffers()[outputBufferIndex];
outputBuffer.position(videoBufferinfo.offset);
outputBuffer.limit(videoBufferinfo.offset + videoBufferinfo.size);

//设置时间戳
if(pts==0){
pts = videoBufferinfo.presentationTimeUs;
}
videoBufferinfo.presentationTimeUs = videoBufferinfo.presentationTimeUs - pts;
//写入数据
mediaMuxer.writeSampleData(videoTrackIndex,outputBuffer,videoBufferinfo);
if(encoderWeakReference.get().onMediaInfoListener!=null){
encoderWeakReference.get().onMediaInfoListener.onMediaTime((int) (videoBufferinfo.presentationTimeUs/1000000));
}
videoEncodec.releaseOutputBuffer(outputBufferIndex,false);
outputBufferIndex = videoEncodec.dequeueOutputBuffer(videoBufferinfo, 0);
}
}
}

//停止录制
public void stopRecode(){
videoEncodec.stop();
videoEncodec.release();
videoEncodec =null;

mediaMuxer.stop();
mediaMuxer.release();
mediaMuxer = null;
}

具体示例请看:
https://github.com/ChinaZeng/SurfaceRecodeDemo

-------------The End-------------