【问题标题】:Render Bitmap frames to Surface for encoding将位图帧渲染到 Surface 以进行编码
【发布时间】:2014-08-19 15:28:11
【问题描述】:

我的目标是接收一个 M4V 视频文件,将视频片段解码为 PNG 帧,修改这些帧,然后重新编码修剪后的视频(也为 M4V)。

工作流程是这样的:[Input Video] -> Export Frames -> Modify Frames -> Encode Frames -> [Output Video]

对于解码过程,我一直在参考bigflake 示例。使用ExtractMpegFramesTest 示例代码,我能够从.m4v 文件生成Bitmap 帧并将帧导出为PNG 文件。

现在我正在尝试重新编码过程,使用EncodeAndMuxTest 示例尝试创建另一组用于编码的类。

我遇到的问题是,示例代码似乎在 OpenGL 中生成原始帧。我有一系列Bitmaps,我想编码/渲染到CodecInputSurface 对象。几乎与解码过程相反。

大部分示例代码都很好,看来我只需要修改generateSurfaceFrame() 以使用OpenGL 将Bitmap 渲染为Surface

这是我到目前为止的代码:

// Member variables (see EncodeAndMuxTest example)
private MediaCodec encoder;
private CodeInputSurface inputSurface;
private MediaMuxer muxer;
private int trackIndex;
private boolean hasMuxerStarted;
private MediaCodec.BufferInfo bufferInfo;

// This is called for each frame to be rendered into the video file
private void encodeFrame(Bitmap bitmap)
{
    int textureId = 0;

    try
    {
        textureId = loadTexture(bitmap);

        // render the texture here?
    }
    finally
    {
        unloadTexture(textureId);
    }
}

// Loads a texture into OpenGL
private int loadTexture(Bitmap bitmap)
{
    final int[] textures = new int[1];
    GLES20.glGenTextures(1, textures, 0);

    int textureWidth = bitmap.getWidth();
    int textureHeight = bitmap.getHeight();

    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
    GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);

    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
            GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
            GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
            GLES20.GL_CLAMP_TO_EDGE);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
            GLES20.GL_CLAMP_TO_EDGE);

    return textures[0];
}

// Unloads a texture from OpenGL
private void unloadTexture(int textureId)
{
    final int[] textures = new int[1];
    textures[0] = textureId;

    GLES20.glDeleteTextures(1, textures, 0);
}

我觉得我应该能够使用 ExtractMpegFramesTest 示例中的STextureRender 来实现类似的效果,但它只是不适合我。

另一件事是性能,我真的在努力获得高效的编码。我将编码 90-450 帧的视频(3-15 秒 @ 30fps),所以这应该只需要几秒钟。

【问题讨论】:

  • 您可以使用 glTexImage() 将位图像素转换为 GLES 纹理,然后渲染纹理……这似乎是您目前所做的。什么不工作? Grafika (github.com/google/grafika) 中的一些代码可能有用。您正在执行什么样的修改?如果它很简单,您可能可以使用 GLES 片段着色器来完成它,这会更快(参见例如 Grafika 中的“显示 + 捕获”活动)。
  • Woo @fadden,感谢您的评论!我正在执行的操作是修剪(从电影中选择片段)、缩放/平移(缩放 + 变换)、调整帧大小以添加信箱(用于文本)以及在信箱区域渲染文本。这些步骤中的每一个都需要用户交互,例如捏合缩放、拖动平移以及在相应的顶部/底部区域输入文本。我用Canvas/Paint/Bitmap 对象来做这一切,但着色器会很整洁。我对 OpenGL 有基本的了解,但着色器语言对我来说是新的。
  • 另外,我想这与其说它没有工作不如我对做什么有心理障碍。从理论上讲,我应该能够向它渲染一个 GL_TRIANGLE_STRIP 四边形,类似于 STextureRender 在 ExtractMpegFramesTest 示例中所做的。我确实看过 Grafika 示例代码,但我不确定在哪里可以找到示例,稍后我会再看更多,看看我能想出什么。
  • “来自相机的纹理”活动使用来自相机预览的输入做一些类似的事情——定位、缩放等等。从 GL 渲染文本有点痛苦;最简单的方法是将文本渲染为位图并将位图转换为纹理。您可以在 Android Breakout (code.google.com/p/android-breakout/source/browse/src/com/…) 中查看示例。
  • 谢谢!我一定会看看这一切。尽管我尽可能地获得最佳性能,但 SD 卡的 IO 负载似乎是一个主要瓶颈(每帧导出约 500 毫秒)。 Bitmap 操作不需要很长时间,我只做“少数”帧。我曾考虑将位图保留在内存中并在必要时回收旧的,但使用近百 MB 的内存确实让我感到畏缩,即使在 Nexus 5 级别的旗舰设备上也是如此。

标签: android opengl-es android-mediacodec


【解决方案1】:

您可以尝试英特尔 INDE 媒体包,它允许修改帧、剪切片段、连接文件等等。框架修改的几个示例效果:颜色修改、文本覆盖等,您可以轻松修改或添加新效果。它有一个很好的示例集和教程如何构建和运行应用程序:https://software.intel.com/en-us/articles/intel-inde-media-pack-for-android-tutorials-running-samples

帧修改是基于 gl 着色器的,例如对于 Sepia:

@Override
protected String getFragmentShader() {
    return "#extension GL_OES_EGL_image_external : require\n" +
            "precision mediump float;\n" +
            "varying vec2 vTextureCoord;\n" +
            "uniform mat3 uWeightsMatrix;\n" +
            "uniform samplerExternalOES sTexture;\n" +
            "void main() {\n" +
            "  vec4 color = texture2D(sTexture, vTextureCoord);\n" +
            "  vec3 color_new = min(uWeightsMatrix * color.rgb, 1.0);\n" +
            "  gl_FragColor = vec4(color_new.rgb, color.a);\n" +
            "}\n";

}

uWeightsMatrix 通过 getAttributeLocation 和 glUniformMatrix3fv 设置为着色器

protected float[] getWeights() {
    return new float[]{
            805.0f / 2048.0f, 715.0f / 2048.0f, 557.0f / 2048.0f,
            1575.0f / 2048.0f, 1405.0f / 2048.0f, 1097.0f / 2048.0f,
            387.0f / 2048.0f, 344.0f / 2048.0f, 268.0f / 2048.0f
    };
}

【讨论】:

    【解决方案2】:

    请注意,原始 STextureRender 从 SurfaceTexture 渲染外部纹理。如果您希望它渲染您的纹理(从位图创建),您必须进行以下更改:

    1. 将目标参数GLES11Ext.GL_TEXTURE_EXTERNAL_OES改为GLES20.GL_TEXTURE_2D

    2. 将着色器定义中的这一行 "uniform samplerExternalOES sTexture;\n" 更改为 “uniform sampler2D sTexture;\n”

    3. drawFrame中删除st.getTransformMatrix(mSTMatrix);

    这个解决方案对我有用。

    【讨论】:

      【解决方案3】:

      我能够将位图帧渲染到表面以进行编码。 我使用 MediaCodec + MediaMuxer 使用 InputSurface 对位图帧进行编码,并使用 OpenGL 渲染位图。

      看起来你所缺少的只是打开 gl 命令来渲染纹理

      为了解决这个问题,我还添加了一些额外的 open gl 命令来使用顶点着色器渲染纹理。

      有关使用 open gl https://github.com/rsri/Pic2Fro/blob/b4fe69b44343dab2515c3fd6e769f3370bf31312/app/src/main/java/com/pic2fro/pic2fro/util/Util.java 渲染纹理的更多详细信息,请参阅此项目和 util 类

      在您的 sn-p 中的 GLUtils.texImage2D(..) 之后使用纹理句柄和适当的宽度和高度调用 renderTexture(..) 将修复所有位图渲染问题。

      也可以在这里查看我的相关答案 https://stackoverflow.com/a/49331295/7602598

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-12-17
        • 1970-01-01
        • 1970-01-01
        • 2015-12-03
        • 2014-12-03
        • 1970-01-01
        相关资源
        最近更新 更多