返回

视频H264解码原理与详细流程探讨

IOS

##视频H264解码详解(下)##

前言

本篇接着005 - 视频H264编码详解(中),主要做H264编解码流程中的最后2环 👇

  1. 继续封装解码工具类
  2. 拿到解码的流数据之后,渲染显示视频帧画面

一、初始化

初始化分为2个方法执行👇🏻

1. public static void init(Surface surface) {
2.     prepareGLComponents();
3.     createNativeWindow(surface);
4. }

1. private static void createNativeWindow(Surface surface) {
2.     // 1. 创建一个本地窗口
3.     mANativeWindow = surface.getNativeWindow();
4.     // 2. 绑定渲染器(缓冲区)与本地窗口
5.     mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
6.     // 3. 初始化渲染器配置
7.     int[] attrs = {
8.             EGL14.EGL_RED_SIZE, 8,
9.             EGL14.EGL_GREEN_SIZE, 8,
10.             EGL14.EGL_BLUE_SIZE, 8,
11.             EGL14.EGL_ALPHA_SIZE, 8,
12.             EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
13.             EGL14.EGL_SURFACE_TYPE, EGL14.EGL_WINDOW_BIT,
14.             EGL14.EGL_NONE
15.     };
16.     EGLConfig[] configs = new EGLConfig[1];
17.     int[] numConfigs = new int[1];
18.     EGL14.eglChooseConfig(mEGLDisplay, attrs, configs, 1, numConfigs);
19.     EGLConfig config = configs[0];
20.     // 4. 创建渲染器表面
21.     mEGLSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, config, mANativeWindow, null);
22.     // 5. 创建上下文
23.     EGLContext context = EGL14.eglCreateContext(mEGLDisplay, config, EGL14.EGL_NO_CONTEXT, null);
24.     // 6. 使我们创建的EGLContext成为当前线程的current context
25.     EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, context);
26. }

二、解码后显示视频画面

1. public static void render(int textureId, int width, int height) {
2.     GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
3.     GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
4.     GLES20.glViewport(0, 0, width, height);
5.     // GLES20.glUseProgram(mProgram);
6.     GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
7.     GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
8.     // glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
9.     // glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
10.     // glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
11.     // glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
12.     GLES20.glUniform1i(mSamplerLoc, 0);
13.     GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
14.     GLES20.eglSwapBuffers(mEGLDisplay, mEGLSurface);
15. }
  • 上述方法render
    • 1.~3. 清除帧缓冲区并设置视口大小。
    • 4. 激活纹理并绑定纹理对象。
    • 5. 使用我们创建的着色器程序。
    • 6.~12. 配置纹理参数并激活纹理单元。
    • 13. 绘制图像。
    • 14. 交换帧缓冲区。

三、解码数据并显示视频

1. private void decodeData() {
2.     byte[] h264 = new byte[1024 * 1024];
3.     while (!mIsExit) {
4.         int len = mInputStream.read(h264, 0, h264.length);
5.         if (len > 0) {
6.             byte[] temp = new byte[len];
7.             System.arraycopy(h264, 0, temp, 0, len);
8.             nativeDecodeData(temp);
9.         } else {
10.             mIsExit = true;
11.         }
12.     }
13.     nativeRelease();
14. }
  • 上述方法decodeData
    • 1. 创建一个用于存储H.264数据缓冲区的数组。
    • 2. 循环读取输入流中的数据。
    • 3. 如果读取到的数据长度大于0,则将数据复制到临时数组中。
    • 4. 调用本机方法nativeDecodeData解码数据。
    • 5. 如果读取到的数据长度为0,则退出循环。
    • 6. 调用本机方法nativeRelease释放资源。

总结

至此,我们已经完成了H.264解码器的实现,它可以将H.264数据解码成视频帧,然后将这些视频帧渲染到屏幕上。