Android MediaCodec 似乎可以缓冲 H264 帧

Soc*_*ash 5 android opengl-es h.264 android-mediacodec

我正在手动读取 RTP/H264 流并将 H264 帧传递给 Android MediaCodec。我使用“markerBit”作为框架的边框。MediaCodec 与 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();
...
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

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

我怀疑演示时间戳可能会影响渲染。但我将其设置为 0。因此我假设帧应该毫无延迟地渲染。

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

VC.*_*One 2

来自 MediaCodec文档

Executing 状态具有三个子状态:Flushed、Running 和 End-of-Stream。编解码器立即start()进入 Flushed 子状态,其中保存所有缓冲区。一旦第一个输入缓冲区出队,编解码器就会进入“运行”子状态,并在此度过其生命的大部分时间。当您使用流结束标记对输入缓冲区进行排队时,编解码器将转换到子End-of-Stream 状态。在此状态下,编解码器不再接受更多输入缓冲区,但仍生成输出缓冲区,直到输出到达流末尾。在 Executing 状态下,您可以随时使用 flash() 返回到 Flushed 子状态。

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

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