录制原理
- 预览
通过fbo
处理视频数据,通过samplerExternalOES
纹理来创建SurfaceTexture
,这样的话摄像头数据就和fbo
相关联,具体可以看OpenGLES通过SurfaceTexture预览摄像头画面
- 录制
通过MediaCodec
创建一个surface
,然后通过创建一个新的egl
环境共享预览的EglContext
和这个surface
绑定,渲染fbo
绑定的纹理,即可录制。
egl
环境配置:
Android配置EGL环境
Android自定义GLSurfaceView
流程如下图所示:
MediaCodec录制主要代码
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;
}