【发布时间】: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