我正在手动读取 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 theEnd-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/