基于Surface的视频编解码与OpenGL ES渲染

来源:互联网 发布:mac点不开 app store 编辑:程序博客网 时间:2024/05/28 18:44

http://blog.csdn.net/gh_home/article/details/52399959


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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

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();  }
  • 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
  • 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

2. 初始化EGL,配置OpenGL的环境

这一步的相关概念可以参照 http://www.cnitblog.com/zouzheng/archive/2011/05/30/74326.html 
主要是配置以下几个内容

数据类型取值EGLDisplay(系统显示 ID 或句柄)EGLConfig(Surface 的 EGL 配置)EGLSurface(系统窗口或 frame buffer 句柄)EGLContext(OpenGL ES 图形上下文)

在这里我们需要建立两个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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

保证了两个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"
  • 1
  • 1

这一部分的程序主要是在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)
  • 1
  • 1

这个使得我们通知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
  • 1
  • 2
  • 1
  • 2

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留有交互接口,因此说它是一个中间层


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 手机浏览器老是自动打开软件怎么办 打开手机浏览器为什么是英文怎么办 ie浏览器删除掉了怎么办 手机360浏览器卸载不掉怎么办 大学素质拓展学分不够怎么办 专升本学分不够怎么办 电脑连接无线网络网关禁用怎么办 背部毛孔粗大有黑头怎么办 毛孔变粗大长痘怎么办 皮肤粗糙暗黄毛孔大怎么办 毛孔粗大还有痘印怎么办 高一的不爱上数学怎么办 监狱系统需要体测怀孕怎么办 货运从业资格证年审过期了怎么办 科三线路记不住怎么办? 汽车大灯里面起雾水怎么办 二级重伤对方法庭拒绝赔偿怎么办? 在麦当劳工作收到假钞怎么办 东西湖小学分配太远怎么办 农行k令过期了怎么办 穿军训的鞋捂坏了怎么办 联通手机号销户话费怎么办 建行员工所持有的原始股怎么办 孕妇喝了午时茶怎么办 苹果6id被锁了怎么办 苹果手机app密码忘了怎么办 好哥们借钱手上没钱怎么办 武汉ca证书u盾怎么办 判了刑发现还有漏案没判怎么办 高铁旅客漏乘怎么办 水库里面要养殖小龙虾最好怎么办 点读机的笔丢了怎么办 皮肤晒的很黑怎么办 电脑wifi连接受限制怎么办 高中孩子班管理松怎么办 脱式计算有余数怎么办 手机被别人绑定微信怎么办 饿了么入职查不到学历怎么办 高中没考上家人让打工怎么办 一建证书丢了怎么办 自考大专证书丢了怎么办