是否可以/如何直接将MediaCodec解码帧馈送到MediaCodec编码器?

时间:2015-04-21 13:16:31

标签: android video video-processing mediacodec grafika

我的目标是从多个视频文件中拼接视频片段。片段由任意开始时间和结束时间定义。最初我想使用像mp4parser这样的库来做它但它只能在同步(IFRAME)点切割流,而我需要更高的精度。

我的方案是从文件中提取编码流 - >解码 - >编码 - >将结果复制到mp4文件中。现在一般代码工作,但结果视频是白噪声。在Nexus-S和Galaxy-S3上测试过。我的代码是几个例子的组合:

  • 根据MoviePlayer.java
  • 读取以前录制的文件
  • Decode-Encode:DecodeEditEncodeTest.java
  • Mux视频流进入mp4 - 又一个例子,这里不相关

我想简化示例,因为我不需要在中间处理帧。我试图将缓冲区从解码器输出馈送到编码器输入,而不在中间使用Surface。整个过程在代码运行完成并产生可播放的视频文件的意义上起作用。但是文件的内容是白噪声。

这是从解码器到编码器的帧的代码片段。有什么问题以及如何使其发挥作用?

...
} else { // decoderStatus >= 0
    if (VERBOSE) Log.d(TAG, "surface decoder given buffer "
                            + decoderStatus + " (size=" + info.size + ")");
    // The ByteBuffers are null references, but we still get a nonzero
    // size for the decoded data.
    boolean doRender = (info.size != 0);
    // As soon as we call releaseOutputBuffer, the buffer will be forwarded
    // to SurfaceTexture to convert to a texture.  The API doesn't
    // guarantee that the texture will be available before the call
    // returns, so we need to wait for the onFrameAvailable callback to
    // fire.  If we don't wait, we risk rendering from the previous frame.
    //   decoder.releaseOutputBuffer(decoderStatus, doRender);
    if (doRender) {
    // This waits for the image and renders it after it arrives.
//                  if (VERBOSE) Log.d(TAG, "awaiting frame");
//                          outputSurface.awaitNewImage();
//                          outputSurface.drawImage();
//                          // Send it to the encoder.
//                              inputSurface.setPresentationTime(info.presentationTimeUs * 1000);
//                          if (VERBOSE) Log.d(TAG, "swapBuffers");
//                          inputSurface.swapBuffers();

            encoderStatus = encoder.dequeueInputBuffer(-1);

            if (encoderStatus >= 0) {
                                encoderInputBuffers[encoderStatus].clear();

                                decoderOutputBuffers[decoderStatus].position(info.offset);
                                decoderOutputBuffers[decoderStatus].limit(info.offset + info.size);

                                encoderInputBuffers[encoderStatus].put(decoderOutputBuffers[decoderStatus]);
                                encoder.queueInputBuffer(encoderStatus, 0, info.size, info.presentationTimeUs*1000, 0);
                }
            }

                        decoder.releaseOutputBuffer(decoderStatus, false);
...

1 个答案:

答案 0 :(得分:4)

使用Surface比使用ByteBuffer要好得多。它更快,更便携。曲面是缓冲区的队列,而不仅仅是像素数据的帧缓冲区;解码的视频帧由句柄传递。如果你使用ByteBuffers,视频数据必须被复制几次,这会减慢你的速度。

创建MediaCodec编码器,获取输入表面,并将其作为输出表面传递给解码器。

如果您需要使用API​​ 16/17,那么您会遇到ByteBuffers。如果你搜索周围的你可以找到古怪的高通格式的反向工程转换器,但请记住,在API 18之前没有CTS测试,所以没有保证。