在Android上使用cv::Mat和MediaCodec进行视频编码

3

我正在使用一台安卓4.3设备,使用cv::Mat编码视频。

我查看了grafika hacksBigFrake samples, 我已经测试过它们并且可以工作。

我在c++中有我的cv::Mat,并且使用JNI可以将缓冲区或缓冲区指针发送到Java,在那里我已经准备好了编码器:

///////////////////////// Configure encoder
    // QVGA at 2Mbps
    mWidth = 320;
    mHeight = 240;
    mBitRate = 2000000;
    //////////////////////////////////////

    MediaCodecInfo codecInfo = selectCodec(MIME_TYPE);
    if (codecInfo == null) 
    {
        // Don't fail CTS if they don't have an AVC codec (not here, anyway).
        Log.e(LOG_TAG, "Unable to find an appropriate codec for " + MIME_TYPE);
    }
    if (VERBOSE) Log.d(LOG_TAG, "found codec: " + codecInfo.getName());

    mBufferInfo = new MediaCodec.BufferInfo();

    MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);

    // Set some properties.  Failing to specify some of these can cause the MediaCodec
    // configure() call to throw an unhelpful exception.

    format.setInteger(MediaFormat.KEY_BIT_RATE, mBitRate);
    format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
    format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
  //  format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
  //  format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 200);
    format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); // PROBLEM: Color_formatSurface is the only want who works!!!!!
    if (VERBOSE) Log.d(LOG_TAG, "format: " + format);

    // Create a MediaCodec encoder, and configure it with our format. 
    //
    mEncoder = MediaCodec.createEncoderByType(MIME_TYPE);
    mEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    //mInputSurface = new CodecInputSurface(mEncoder.createInputSurface()); // I don't want to use a surface
    mEncoder.start();
    inputBuffers = mEncoder.getInputBuffers();
    outputBuffers = mEncoder.getOutputBuffers();

问题在于,MediaFormat.KEY_COLOR_FORMAT 只支持 COLOR_FormatSurface 或 COLOR_FormatYUV420SemiPlanar,但我的数据是 RGB 格式的,没有表面元数据或者 YUV。我已经尝试使用了三种可用的编解码器:
//private static final String MIME_TYPE = "video/avc";    // H.264 Advanced Video Coding
//private static final String MIME_TYPE = "video/mp4v-es";    // Mp4
private static final String MIME_TYPE = "video/3gpp";    // 3gpp

1)这种情况下,颜色格式是否会成为问题?我的意思是,是否可以直接使用,我必须使用PixelBuffer来伪造COLOR_FormatSurface,或者将我的数据转换为YUV?

2)从cv :: Mat.data指针复制数据到MediaCodec缓冲区的最有效方法是什么?

更新1:

  • 在提取CV:Mat之前,我有一个FBO OpenGL。我认为另一种解决方案是在InputSurface中呈现FBO,以直接使用MediaCodec对视频进行编码,而无需共享上下文。 但我没有找到将FBO从OpenGL上下文复制到CodecInputSurface的参考。

2
Grafika的“Record GL app”演示了通过从FBO复制到Surface进行记录(以及其他方法)。除非您需要支持API 16/17,否则应避免使用ByteBuffer输入到MediaCodec,因为它需要YUV数据并且速度较慢。您可以使用glTexImage2D将RGB数据上传到纹理; Grafika中有一个基准测试(512x512 RGBA),表明它在当前设备上非常快。 - fadden
2个回答

4

FBO渲染到编解码器输入表面非常简单。您需要使用GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,framebuffer);绑定frameBuffer的输出纹理ID到mediacodec上下文中,即首先使用makeCurrent。

GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);

并使用正常的复制着色器,类似于媒体编解码器中已有的内容,但不需要OES纹理,而是使用2D纹理:例如

private static final String VERTEXT_SHADER =
        "uniform mat4 uOrientationM;\n" +
            "uniform mat4 uTransformM;\n" +
            "attribute vec2 aPosition;\n" +
            "varying vec2 vTextureCoord;\n" +
            "void main() {\n" +
            "gl_Position = vec4(aPosition, 0.0, 1.0);\n" +
            "vTextureCoord = (uTransformM * ((uOrientationM * gl_Position + 1.0) * 0.5)).xy;" +
            "}";

    private static final String FRAGMENT_SHADER =
        "precision mediump float;\n" +
            "uniform sampler2D sTexture;\n" +
            "varying vec2 vTextureCoord;\n" +
            "void main() {\n" +
            "gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
            "}"; 

您可以在英特尔INDE Android样例中找到与复制和FBO初始化相关的实用解决方案。 https://software.intel.com/en-us/articles/intel-inde-media-pack-for-android-tutorials


2
我认为有两种方法:
  1. 将RGB缓冲区转换为NV12(或YV12),配置编码器以接受NV12(YV12),并将NV12缓冲区复制到编码器的缓冲区 - 我不确定它是否适用于所有设备,因为不同设备上的颜色格式支持不一致,更多信息请参见fadden在Q5\A5中的页面:http://bigflake.com/mediacodec/

  2. 从RGB缓冲区创建位图,通过GLUtils.texImage2D()将其放入由createInputSurface()创建的表面\纹理中 - 对性能不确定


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接