是否possible/how直接将MediaCodec解码后的帧提供给MediaCodec编码器?
Is it possible/how to feed MediaCodec decoded frames to MediaCodec encoder directly?
我的目标是拼接几个视频文件的视频片段。片段由任意开始时间和结束时间定义。最初我想使用像 mp4parser 这样的库来实现它,但它只能在同步 (IFRAME) 点切割流,而我需要更高的精度。
我的方案是从文件中提取编码流 -> 解码 -> 编码 -> 将结果多路复用到 mp4 文件中。现在通常代码可以工作,但生成的视频是白噪声。在 Nexus-S 和 Galaxy-S3 上测试。我的代码是几个例子的组合:
- 根据MoviePlayer.java
读取以前记录的文件
- 解码-编码: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);
...
使用 Surface 比使用 ByteBuffer 好得多。它更快,更便携。表面是缓冲区队列,而不仅仅是像素数据的帧缓冲区;解码后的视频帧通过句柄传递。如果您使用 ByteBuffers,视频数据必须复制几次,这会减慢您的速度。
创建 MediaCodec 编码器,获取输入表面,并将其作为输出表面传递给解码器。
如果您需要使用 API 16/17,您只能使用 ByteBuffers。如果您四处搜索,您可以找到用于古怪的 qualcomm 格式的逆向工程转换器,但请记住,直到 API 18 才进行 CTS 测试,因此无法保证。
我的目标是拼接几个视频文件的视频片段。片段由任意开始时间和结束时间定义。最初我想使用像 mp4parser 这样的库来实现它,但它只能在同步 (IFRAME) 点切割流,而我需要更高的精度。
我的方案是从文件中提取编码流 -> 解码 -> 编码 -> 将结果多路复用到 mp4 文件中。现在通常代码可以工作,但生成的视频是白噪声。在 Nexus-S 和 Galaxy-S3 上测试。我的代码是几个例子的组合:
- 根据MoviePlayer.java 读取以前记录的文件
- 解码-编码: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);
...
使用 Surface 比使用 ByteBuffer 好得多。它更快,更便携。表面是缓冲区队列,而不仅仅是像素数据的帧缓冲区;解码后的视频帧通过句柄传递。如果您使用 ByteBuffers,视频数据必须复制几次,这会减慢您的速度。
创建 MediaCodec 编码器,获取输入表面,并将其作为输出表面传递给解码器。
如果您需要使用 API 16/17,您只能使用 ByteBuffers。如果您四处搜索,您可以找到用于古怪的 qualcomm 格式的逆向工程转换器,但请记住,直到 API 18 才进行 CTS 测试,因此无法保证。