基于Surface的视频编解码与OpenGL ES渲染
来源:互联网 发布:内存整型数据是啥 编辑:程序博客网 时间:2024/05/19 18:17
1. 概述
这篇文章所做的事情是这样的:
1. 从一个.mp4文件中解码视频流到surface上
2. 利用OpenGL ES渲染改变视频流中每一帧的内容
3. 将改变后的视频流重新编码输出到一个新的.mp4文件
所有代码可在此处下载:https://github.com/GH-HOME/DecodeEncodeMP4
2. 数据流
图像的数据流按照以下方式传递。
3. 工作流
1. 初始化MediaEncoder(视频编码)、MediaDecoder(视频解码)、MediaMux(生成MP4文件合成音频)、MediaExtractor(分割视频与音频)。在这一步中,encoder和decoder需要分别绑定一个surface。
核心代码如下:
1)初始化编码器与MediaMux
MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, WIDTH, HEIGHT); format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE); format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE); format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL); encoder = null; try { encoder = MediaCodec.createEncoderByType(MIME_TYPE); encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); encodesurface=encoder.createInputSurface(); encoder.start(); mMuxer = new MediaMuxer(outputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); }catch (IOException ioe) { throw new RuntimeException("failed init encoder", ioe); } mTrackIndex = -1; mMuxerStarted = false;
2)初始化解码器与MediaExtractor
try { File inputFile = new File(FILES_DIR, INPUT_FILE); // must be an absolute path if (!inputFile.canRead()) { throw new FileNotFoundException("Unable to read " + inputFile); } extractor = new MediaExtractor(); extractor.setDataSource(inputFile.toString()); DecodetrackIndex = selectTrack(extractor); if (DecodetrackIndex < 0) { throw new RuntimeException("No video track found in " + inputFile); } extractor.selectTrack(DecodetrackIndex); MediaFormat format = extractor.getTrackFormat(DecodetrackIndex); if (VERBOSE) { Log.d(TAG, "Video size is " + format.getInteger(MediaFormat.KEY_WIDTH) + "x" + format.getInteger(MediaFormat.KEY_HEIGHT)); } outputSurface = new CodecOutputSurface(saveWidth, saveHeight,encodersurface); String mime = format.getString(MediaFormat.KEY_MIME); decoder = MediaCodec.createDecoderByType(mime); decoder.configure(format, outputSurface.getSurface(), null, 0); decoder.start(); }catch (IOException e) { e.printStackTrace(); }
2. 初始化EGL,配置OpenGL的环境
这一步的相关概念可以参照 http://www.cnitblog.com/zouzheng/archive/2011/05/30/74326.html
主要是配置以下几个内容
在这里我们需要建立两个EGLContext ,一个用于控制接收从mp4文件中传过来的数据,一个用于控制将EGLSurface上的数据经过OPENGL ES渲染传递到encoder中的surface。在初始化第二个EGLContext 的时候需要将其与第一个EGLContext 绑定,这样两者可以共享一个Texture ID(也就是实际的图像数据)。
这部分代码较多:主要是CodecOutputSurface类中的eglsetup函数
关键代码是这一句
mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT, attrib_list, 0); mEGLContextEncoder = EGL14.eglCreateContext(mEGLDisplay, configEncoder, mEGLContext, attrib_list, 0);
保证了两个EGLContext共享Texture id
3. OPENGL渲染的准备工作
这一步主要是分以下步骤:
1. 写shader language的程序,包含VERTEX_SHADER和FRAGMENT_SHADER
2. 编译链接以及加载上述程序
3. 获取VERTEX_SHADER以及FRAGMENT_SHADER中的变量的句柄,创建Texture ID
在参考的Big Flake示例代码中发现在shader language中可以直接去渲染YUV420编码的数据,需要加以下标志声明
"#extension GL_OES_EGL_image_external : require\n"
这一部分的程序主要是在CodecOutputSurface类中的setup函数以及STextureRender类中的一些成员函数
4. 绘图程序的运行过程
在初始化编解码器后,将解码器对应的surface和一个SurfaceTexture绑定起来,同时SurfaceTexture的另外一边与OPRNGL ES中初始化建立的一个Texture ID绑定。这样就建立了一条由解码的mp4数据到OPENGL ES的Texture的数据流。 其中SurfaceTexture充当中介,在上述工作准备好后,开启SurfaceTexture内容侦听,即回调函数onFrameAvailable。 一旦SurfaceTexture内容发生变化(有新的编码数据流流入),系统会自动调用onFrameAvailable表明SurfaceTexture中有可用数据,之后我们调用SurfaceTexture的成员函数updateTexImage将当前的图像流传递到OPENGL ES中的texture。
在掉用GLES20绘图函数之前需要先调用
EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)
这个使得我们通知GPU以及OPENGL ES在执行绘图指令的时候,是在当前mEGLContext这个上下文绘制在mEGLSurface上的。所以我们在最后绘图的时候需要makecurrent到与Encoder绑定的surface对应的那个EGLSurface上:之前我们需要这样绑定这两个量
EGLSurface mEGLSurfaceEncoder = EGL14.eglCreateWindowSurface(mEGLDisplay, configEncoder, surface, surfaceAttribs2, 0); //creates an EGL window surface and returns its handle
4. 总结
对于OpenGL ES与编解码的结合主要可以参考以下两个网站
http://bigflake.com/mediacodec/
https://github.com/google/grafika
个人觉得grafika的模块化写的更好一点。总体来说,这个流程就是Encoder和Decoder把数据流弄到surface上,然后与OpenGL中的Texture id绑定,之后调用OPENGL ES的绘图指令渲染的过程,其中EGL就是给OPENGL ES方便移植做中间层的,它在程序中的作用就是为OPENGL ES做了初始化的工作,同时它也与mediacodec留有交互接口,因此说它是一个中间层
- 基于Surface的视频编解码与OpenGL ES渲染
- 基于Surface的视频编解码与OpenGL ES渲染
- Android使用MediaCodec解码视频并用OpenGL ES进行渲染的思路
- iOS中OpenGL-ES渲染YUV视频
- 基于ffmpeg的简单音视频编解码的例子
- 基于ffmpeg的简单音视频编解码的例子
- 基于FFMPEG 的跨平台视频编解码研究
- 嵌入式Linux下基于FFmpeg的视频硬件编解码
- 嵌入式Linux下基于FFmpeg的视频硬件编解码
- 基于FFmpeg的远程视频监控系统编解码
- 嵌入式Linux下基于FFmpeg的视频硬件编解码
- [OpenGL ES 02]OpenGL ES渲染管线与着色器
- [OpenGL ES 02]OpenGL ES渲染管线与着色器
- [OpenGL ES 02]OpenGL ES渲染管线与着色器
- [OpenGL ES 02]OpenGL ES渲染管线与着色器
- [OpenGL ES 02]OpenGL ES渲染管线与着色器
- [OpenGL ES 02]OpenGL ES渲染管线与着色器
- [OpenGL ES 02]OpenGL ES渲染管线与着色器
- 解决android gradle的问题
- 动态添加复选框
- SQL Server T-SQL高级查询
- 单机最大tcp连接数
- xstream 注解
- 基于Surface的视频编解码与OpenGL ES渲染
- PHP设计模式系列 - 策略模式
- Binary Tree Maximum Path Sum
- 开源app后台开源框架集合 java
- leetcode:greedy:Candy(135)
- 选中页面出现的值
- 两部电脑如何使用同一个GITHUB项目包
- 【法律】中华人民共和国劳动合同法
- android 添加linearlayout 边框