【发布时间】:2018-07-16 11:04:47
【问题描述】:
我希望能够使用 MediaCodec 将位图写入视频。我希望视频是例如3 秒长和 30 fps。我的目标是 Android API 21。
我有一个绘图类:
public class ImageRenderer {
private static final String NO_FILTER_VERTEX_SHADER = "" +
"attribute vec4 position;\n" +
"attribute vec4 inputTextureCoordinate;\n" +
" \n" +
"varying vec2 textureCoordinate;\n" +
" \n" +
"void main()\n" +
"{\n" +
" gl_Position = position;\n" +
" textureCoordinate = inputTextureCoordinate.xy;\n" +
"}";
private static final String NO_FILTER_FRAGMENT_SHADER = "" +
"varying highp vec2 textureCoordinate;\n" +
" \n" +
"uniform sampler2D inputImageTexture;\n" +
" \n" +
"void main()\n" +
"{\n" +
" gl_FragColor = texture2D(inputImageTexture, textureCoordinate);\n" +
"}";
private int mGLProgId;
private int mGLAttribPosition;
private int mGLUniformTexture;
private int mGLAttribTextureCoordinate;
private static final int NO_IMAGE = -1;
private static final float CUBE[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f,
};
private int mGLTextureId = NO_IMAGE;
private final FloatBuffer mGLCubeBuffer;
private final FloatBuffer mGLTextureBuffer;
private Bitmap bitmap;
private static final float TEXTURE_NO_ROTATION[] = {
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
};
public ImageRenderer(Bitmap bitmap) {
this.bitmap = bitmap;
mGLCubeBuffer = ByteBuffer.allocateDirect(CUBE.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
mGLCubeBuffer.put(CUBE).position(0);
mGLTextureBuffer = ByteBuffer.allocateDirect(TEXTURE_NO_ROTATION.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
mGLTextureBuffer.put(TEXTURE_NO_ROTATION).position(0);
GLES20.glClearColor(0, 0, 0, 1);
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
mGLProgId = OpenGlUtils.loadProgram(NO_FILTER_VERTEX_SHADER, NO_FILTER_FRAGMENT_SHADER);
mGLAttribPosition = GLES20.glGetAttribLocation(mGLProgId, "position");
mGLUniformTexture = GLES20.glGetUniformLocation(mGLProgId, "inputImageTexture");
mGLAttribTextureCoordinate = GLES20.glGetAttribLocation(mGLProgId,
"inputTextureCoordinate");
GLES20.glViewport(0, 0, bitmap.getWidth(), bitmap.getHeight());
GLES20.glUseProgram(mGLProgId);
}
public void drawFrame() {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
// Draw bitmap
mGLTextureId = OpenGlUtils.loadTexture(bitmap, mGLTextureId, false);
GLES20.glUseProgram(mGLProgId);
mGLCubeBuffer.position(0);
GLES20.glVertexAttribPointer(mGLAttribPosition, 2, GLES20.GL_FLOAT, false, 0, mGLCubeBuffer);
GLES20.glEnableVertexAttribArray(mGLAttribPosition);
mGLTextureBuffer.position(0);
GLES20.glVertexAttribPointer(mGLAttribTextureCoordinate, 2, GLES20.GL_FLOAT, false, 0,
mGLTextureBuffer);
GLES20.glEnableVertexAttribArray(mGLAttribTextureCoordinate);
if (mGLTextureId != OpenGlUtils.NO_TEXTURE) {
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mGLTextureId);
GLES20.glUniform1i(mGLUniformTexture, 0);
}
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GLES20.glDisableVertexAttribArray(mGLAttribPosition);
GLES20.glDisableVertexAttribArray(mGLAttribTextureCoordinate);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
}
}
我还有一个InputSurface 连接到我的视频编码器和复用器。
在处理开始时,然后每次我成功复用一帧时,我都会调用:
inputSurface.makeCurrent();
imageRenderer.drawFrame();
inputSurface.setPresentationTime(presentationTimeNs);
inputSurface.swapBuffers();
inputSurface.releaseEGLContext();
其中inputSurface和imageRenderer是上述类的实例,而presentationTimeNs我是根据需要的帧率来计算的。
这通常确实有效,但感觉效率很低。我觉得我一遍又一遍地重绘位图,尽管我知道它没有改变。一开始我尝试只调用一次或两次drawFrame(),但随后在我所有的三星测试设备上输出的视频都会闪烁为黑色。
有没有更有效的方法可以将相同的位图一遍又一遍地绘制到我的编码器输入表面?
【问题讨论】:
标签: android opengl-es surfaceview android-mediacodec