所用到的东西,
FBO:离屏渲染,得到视频数据,这里用FBO是为了以后扩展特效预留;
MediaMuxer:封装音视频输出成MP4
AudioRecord:录制音频;
MediaCode:编解码,把音频编码成AAC ,把视频编码成AVC也就是H264
整个流程图如下:
自定义GLSurfaceView,因为GLSurfaceView继承SurfaceView 把一些GL的东西做了,不用再做,
然后把SurfaceTexture给Camera,
mCamera.setPreviewTexture(surfaceTexture);
这个是离屏渲染,OpenGL 不能直接拿到相机的数据,通过绑定纹理的方式去预览,拿到每帧渲染数据;通过Renderer onDrawFrame接口回调,这个时候就进行预览绘制,和录制的离屏绘制,然后把离屏绘制的纹理id给录制类进行处理;
// 这个是手动刷新毎帧 mSurfaceTexture.updateTexImage(); // 预览相机的处理 mSurfaceTexture.getTransformMatrix(mtx); mCameraFilter.setMatrix(mtx); int textureId = mCameraFilter.onDrawFrame(mTextureID[0]); // 绘制 mScreenFilter.onDrawFrame(textureId);
然后再去单独处理录制的,
录制的我使用Android自带的OpenGL es,所以需要EGL;然后通过MedicCodec 去缓冲区拿数据并编码,这里需要在同一线程,
/** * 4.配置EGL 环境 */ HandlerThread handlerThread = new HandlerThread("MyMediaRecorder"); handlerThread.start(); Looper looper = handlerThread.getLooper(); mHandler = new Handler(looper); mHandler.post(new Runnable() { @Override public void run() { mEGL = new MyEGL(mEGLContext, mInputSurface, mContext, mWidth, mHeight); mMediaCodec.start(); isStart = true; } });
/** * 录制 真正执行编码的函数 * * @param textureId * @param timestamp 时间戳 */ public void encodeFrame(final int textureId, final long timestamp) { if (!isStart) { return; } if (mHandler != null) { mHandler.post(new Runnable() { @Override public void run() { if (mEGL != null) { mEGL.draw(textureId, timestamp); } getEncodeedData(false); } }); } }
视频编码后的数据放到Mp4RecorderCalbackRenderer里的队列中,然后让MediaMuxer去封装,为什么要放队列?因为还有音频需要一起处理;
使用AudioRecord 录音然后循环取数据,
while (mIsRecording){ int len = mAudioRecord.read(mBuffer,0,mBuffer.length); if (len>0){ if (mAacEncoder== null){ return; } mAacEncoder.queueData(mBuffer); } }
然后编码给Mp4RecorderCalbackRenderer 队列中,然后让MediaMuxer封装;
MediaMuxer是使用时间戳来同步音视频;
详细的代码:Seven: Android音视频