在Android上解码h264字节流

8

我希望在Android上解码和显示一段原始的H264视频字节流,因此我目前使用MediaCodec / Format类。我通过Udp从服务器获取帧数据。但是,很不幸,目前什么也没有显示。

这是我目前的进展。

初始化MediaCodec类:

codec = MediaCodec.createDecoderByType("video/avc");

MediaFormat format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, "video/avc");
format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 100000);
format.setInteger(MediaFormat.KEY_WIDTH, 800);
format.setInteger(MediaFormat.KEY_HEIGHT, 600);
format.setInteger("max-width", 800);
format.setInteger("max-height", 600);
format.setInteger("push-blank-buffers-on-shutdown", 1);

codec.configure(format, surface, null, 0);

解码器的使用方法:

int inIndex = codec.dequeueInputBuffer(10000);
if(inIndex >= 0)
{
                ByteBuffer inputBuffer = codecInputBuffers[inIndex];
                inputBuffer.clear();
                inputBuffer.put(frameData);
                codec.queueInputBuffer(inIndex, 0, frameSize, 33, 0);
}

int outIndex = codec.dequeueOutputBuffer(null, 10000);

switch(outIndex)
{
  case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
    codecOutputBuffers = codec.getOutputBuffers();
    System.out.println("OB Changed");
    break;
  case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
        System.out.println("OF Changed");
        break;
  case MediaCodec.INFO_TRY_AGAIN_LATER:
    System.out.println("l8r");
    break;
  default:
    ByteBuffer buffer = codecOutputBuffers[outIndex];
    codec.releaseOutputBuffer(outIndex, true);
}

我正在测试这段代码的设备是谷歌Nexus 5。当我运行它时,outIndex 始终等于 MediaCodec.INFO_TRY_AGAIN_LATER
我之前已经写过一个笔记本客户端,它可以正常工作,所以我想服务器发送的h264流应该没问题。
感谢你的帮助。
编辑: 如果有人遇到同样的问题,fadden提出的修改建议(1)可以解决此问题。我已经更正了上面的代码。现在它可以工作了。我示例中的另一个错误是不能传递null给.dequeueOutputBuffers(...);。你必须执行类似以下操作:
        BufferInfo buffInfo = new MediaCodec.BufferInfo();
        int outIndex = codec.dequeueOutputBuffer(buffInfo, 10000);

即使你对此不感兴趣。;)

1
你能分享一下已经修正过的代码吗? - dvrm
1个回答

7

我看到几个问题...

(1) 你试图替换输入缓冲区数组中的一个缓冲区。但是,MediaCodec 的工作方式并不是这样的——框架提供缓冲区,而你将数据复制到其中。这样做的想法是,通过允许框架进行分配,它可以潜在地避免稍后复制数据。

你需要从 decoder.getInputBuffers() 获取输入缓冲区数组,并使用它们。每次都要确保使用 clear() 清除 ByteBuffer,以重置位置和限制。

(2) 你正在编写一条数据包,并期望输出一帧数据。实际上,在生成第一帧之前,你可能需要提供多个数据缓冲区。请参见 this post 中的示例。在某些配置文件中,编码器允许重新排序帧,因此即使解码器开始运行,你也不能只提供一帧并等待解码后的数据从另一侧弹出。

(3) AVC解码器需要SPS/PPS数据,您可以通过带有BUFFER_FLAG_CODEC_CONFIG标志的缓冲区提供此数据,或者使用MediaFormat#setByteBuffer()将数据添加到MediaFormat中的“csd-0”/“csd-1”键。这两种方法的示例可在EncodeDecodeTest中找到。 bigflake上有许多AVC解码示例,但数据源是MediaCodec编码器,因此它们通常免费获得第三点。 此帖子可能对您有用。

为了显示这些帧,您可以在Grafika中看到不同的方法(通常使用.mp4文件,因此编码/解码实现并不那么相关)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接