【发布时间】:2020-04-29 19:13:31
【问题描述】:
首先,感谢您花时间阅读本文。当我面临 h264 帧的解码问题时,我需要一些帮助或见解。我发布这个问题是因为与此相关的大多数其他相关帖子都没有提供明确的步骤。我已经共享了具有主要逻辑的解码器线程的代码。我从网络获取 sps、pps 和数据,因此当接收到 sps 和 pps 并馈送相应的数组时,我将这个 mConfigured 标志设为真。如果需要,我可以提供更多信息。
PS - 请原谅编码标准,因为它仍在开发中。
decoder.dequeueOutputBuffer >> 总是返回 -1
//接收到新数据包时从网络调用的以下函数。
public void decodeAndPlayVideoFrame(byte[] encodedData, int frameType) {
if (frameType == 1) {
SPS = encodedData;
} else if (frameType == 2) {
PPS = encodedData;
} else if (frameType == 0) {
Log.d("EncodeDecode", "enqueueing frame no: " + (frameID));
try {
Frame frame = new Frame(frameID);
int totalDataLength = encodedData.length + SPS.length + PPS.length;
frame.frameData = new byte[totalDataLength];
System.arraycopy(SPS, 0, frame.frameData, 0, SPS.length);
System.arraycopy(PPS, 0, frame.frameData, SPS.length, PPS.length);
System.arraycopy(encodedData, 0, frame.frameData, SPS.length + PPS.length, encodedData.length);
queue.add(frame);
frameID++;
} catch (NullPointerException e) {
Log.e("EncodeDecode", "frame is null");
e.printStackTrace();
} catch (IllegalArgumentException e) {
Log.e("EncodeDecode", "problem inserting in the queue");
e.printStackTrace();
} catch (IllegalStateException e) {
Log.e("EncodeDecode", "problem inserting in the queue");
e.printStackTrace();
}
Log.d("EncodeDecode", "frame enqueued. queue size now: " + queue.size());
}
}
// 播放器线程在屏幕启动时启动并处理送入队列的数据包。
private class PlayerThread extends Thread {
//private MediaExtractor extractor;
private MediaCodec decoder;
private Surface surface;
private boolean mConfigured;
public PlayerThread(Surface surface) {
this.surface = surface;
}
private void initCodec() throws IOException {
MediaFormat mediaFormat = null;
decoder = MediaCodec.createDecoderByType("video/avc");
mediaFormat = MediaFormat.createVideoFormat("video/avc",
320,
240);
try {
decoder.configure(mediaFormat,
surface,
null,
0);
frameID = 0;
mConfigured = true;
decoder.start();
Log.d("EncodeDecode", "DECODER_THREAD:: decoder.start() called");
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (SPS == null || PPS == null || SPS.length == 0 || PPS.length == 0) {
try {
Log.d("EncodeDecode", "DECODER_THREAD:: sps,pps not ready yet");
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (!mConfigured) {
try {
initCodec();
} catch (IOException e) {
e.printStackTrace();
}
}
int i = 0;
ByteBuffer[] inputBuffers = decoder.getInputBuffers();
ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
while (!Thread.interrupted()) {
Frame currentFrame = null;
try {
Log.d("EncodeDecode", "DECODER_THREAD:: calling queue.take(), if there is no frame in the queue it will wait");
currentFrame = queue.take();
} catch (InterruptedException e) {
Log.e("EncodeDecode", "DECODER_THREAD:: interrupted while PlayerThread was waiting for the next frame");
e.printStackTrace();
}
if (currentFrame == null)
Log.e("EncodeDecode", "DECODER_THREAD:: null frame dequeued");
else
Log.d("EncodeDecode", "DECODER_THREAD:: " + currentFrame.id + " no frame dequeued");
if (currentFrame != null && currentFrame.frameData != null && currentFrame.frameData.length != 0) {
Log.d("EncodeDecode", "DECODER_THREAD:: decoding frame no: " + i + " , dataLength = " + currentFrame.frameData.length);
int inIndex = decoder.dequeueInputBuffer(-1);
if (inIndex >= 0) {
Log.d("EncodeDecode", "DECODER_THREAD:: sample size: " + currentFrame.frameData.length + "->" + currentFrame.frameData[0] + "to" + currentFrame.frameData[currentFrame.frameData.length-1]);
ByteBuffer buffer = inputBuffers[inIndex];
buffer.clear();
buffer.put(currentFrame.frameData);
decoder.queueInputBuffer(inIndex, 0, currentFrame.frameData.length, 0, 0);
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
int outIndex = decoder.dequeueOutputBuffer(info, 0);
switch (outIndex) {
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
Log.e("EncodeDecode", "DECODER_THREAD:: INFO_OUTPUT_BUFFERS_CHANGED");
outputBuffers = decoder.getOutputBuffers();
break;
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
Log.e("EncodeDecode", "DECODER_THREAD:: New format " + decoder.getOutputFormat());
break;
case MediaCodec.INFO_TRY_AGAIN_LATER:
Log.e("EncodeDecode", "DECODER_THREAD:: dequeueOutputBuffer timed out!");
break;
default:
Log.d("EncodeDecode", "DECODER_THREAD:: decoded SUCCESSFULLY!!!");
ByteBuffer outbuffer = outputBuffers[outIndex];
decoder.releaseOutputBuffer(outIndex, true);
break;
}
try {
Thread.sleep(250);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}
}
decoder.stop();
decoder.release();
mConfigured = false;
}
}
【问题讨论】:
-
也许看this question,它非常相似,我会从问你同样的问题开始。但是——我也注意到你似乎没有在这里配置“csd-0”/“csd-1”;就编解码器而言,您还没有对 SPS/PPS 做任何事情。没有它就行不通。
-
感谢您的回复。我已经更新了代码 sn-p 以包含将 sps、pps 附加到数据数组的函数,所以您认为仍然需要将 csd-0/csd-1 添加到 mediaformat 中。你说什么?
-
根据文档,“此类数据必须在调用 queueInputBuffer() 时使用标志 BUFFER_FLAG_CODEC_CONFIG 进行标记”。那个代码在哪里?
-
如果您只是阅读@E.Abdel 的评论,如果一个设置 csd-0、csd-1 和 mediaformat,则 BUFFER_FLAG_CODEC_CONFIG 不是强制性的。
-
对。你会注意到我在第一条评论中说了非常相似的话。需要明确的是,您有两个选择:1.)您可以使用“csd-0”/“csd-1”在
MediaFormat上配置 SPS/PPS,或者 2.)您可以使用queueInputBuffer()提交它BUFFER_FLAG_CODEC_CONFIG。在查看您发布的代码时,您还没有完成任何一项。但是,您已将数据添加到queue,这表明您正在尝试选项 2。您显然没有使用选项 1,因为您没有尝试将 SPS/PPS 添加到MediaFormatin任何方式.
标签: android video-streaming h.264 android-mediacodec