Android MediaCodec 似乎可以缓冲 H264 帧

标签 android opengl-es h.264 android-mediacodec

我正在手动读取 RTP/H264 流并将 H264 帧传递给 Android MediaCodec。我使用“markerBit”作为框架的边框。 MediaCodec 绑定(bind)到 OpenGL 纹理 (SurfaceTexture)。 一般来说,一切正常。但是解码器似乎可以缓冲帧。如果我在解码器中放置一个帧,它不会立即渲染到纹理。在我将 2-3 帧更多地放入解码器后,第一帧被渲染到纹理。

我正在针对 Android 4.4.4 实现。

private static final int INFINITE_TIMEOUT = -1;
private static final int TIMEOUT_OUTPUT_BUFFER_MEDIA_CODEC = 1000;
...
int bufferIndex = codec.dequeueInputBuffer(INFINITE_TIMEOUT);
if (bufferIndex < 0) {
  throw new RuntimeException("Error");
}

ByteBuffer inputBuffer = inputBuffers[bufferIndex];
inputBuffer.clear();

// Copy H264 data to inputBuffer
h264Frame.fill(inputBuffer);

codec.queueInputBuffer(bufferIndex, 0, inputBuffer.position(), 0, 0);
drainOutputBuffers();
...

private boolean drainOutputBuffers() {
MediaCodec.BufferInfo buffInfo = new MediaCodec.BufferInfo();

int outputBufferIndex = codec.dequeueOutputBuffer(buffInfo, TIMEOUT_OUTPUT_BUFFER_MEDIA_CODEC);

if (outputBufferIndex >= 0) {
  codec.releaseOutputBuffer(outputBufferIndex, true);
  return true;
}

switch (outputBufferIndex) {
  case MediaCodec.INFO_TRY_AGAIN_LATER:
    LOG.debug("Could not dequeue output buffer. Try again later");
    break;
  case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
    LOG.warn("The output format has changed.");
    break;
  case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
    LOG.warn("The output buffers has changed.");
    break;
  default:
    LOG.warn("The output buffer index was negative: {}", outputBufferIndex);
}
return false;
}

在渲染端,我使用“onFrameAvailable”回调来检查是否必须更新 openGl 线程上的纹理。我用于检查的标志由锁(同步)保护。

我怀疑演示时间戳可能会影响渲染。但是我将它设置为 0。因此我假设应该立即渲染帧。

我想将帧渲染到纹理,而不必放置额外的帧。

最佳答案

来自 MediaCodec documentation

The Executing state has three sub-states: Flushed, Running and End-of-Stream. Immediately after start() the codec is in the Flushed sub-state, where it holds all the buffers. As soon as the first input buffer is dequeued, the codec moves to the Running sub-state, where it spends most of its life. When you queue an input buffer with the end-of-stream marker, the codec transitions to the End-of-Stream sub-state. In this state the codec no longer accepts further input buffers, but still generates output buffers until the end-of-stream is reached on the output. You can move back to the Flushed sub-state at any time while in the Executing state using flush().

您需要“使用 end-of-stream 标记对输入缓冲区进行排队”。对您提供给解码器的第一帧执行此操作(确保它是关键帧)。

这一点是为了告诉解码器不要再期待帧,因此立即开始播放。否则在看到任何东西之前输入 3 或 4 帧是正常的。这是所有 MPEG 解码器的期望,与 Android 无关。

关于Android MediaCodec 似乎可以缓冲 H264 帧,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38199344/

相关文章:

android - OpenGL ES 2.0 顶点错误垃圾收集器 (Android)

opengl-es - 矢量旋转 (OpenGL ES)

ffmpeg - MP4 视频只有在 Jwplayer 上完全加载后才能播放

c - FFmpeg av_hwdevice_ctx_create 返回 ENOMEM

ios - iOS 6 中的视频工具箱

java - VideoView "is loaded"监听器?

android - 如何将textpadding放在android的webview中?

android - 从 GPU 纹理保存视频帧

android - 创建矢量 Assets 时出现错误 "currentColor"

android - 破折号路径效果使屏幕变慢