【问题标题】:Decoding an RTP stream with H264 data on Android Using MediaCodec使用 MediaCodec 在 Android 上使用 H264 数据解码 RTP 流
【发布时间】:2017-06-03 02:02:43
【问题描述】:

我目前正在尝试从 RTP 流中解析 H264 数据,然后将其发送到 MediaCodec 以在适用于 Android 的 SurfaceView 上呈现。

但是,我不确定如何:

  • 从 RTP 数据包正确构建 H264 切片
  • 将 H264 切片组装成切片后,将其发送到媒体编解码器

我还没有看到任何以简洁明了的方式实现的示例,而且我还没有发现 MediaCodec 文档有任何帮助。

有人在这个领域有经验吗?

void videoCodec(ByteBuffer input, int flags) {

    bufferInfo.set(0, 0, 0, flags);

    int inputBufferId = codec.dequeueInputBuffer(10000);

    if (inputBufferId >= 0) {

        //put data
        ByteBuffer inputData = inputBuffers[inputBufferId];

        inputData.clear();
        inputData.put(input);

        //queue it up
        codec.queueInputBuffer(inputBufferId, 0, input.limit(), 0, flags);
    }

    int outputBufferId = codec.dequeueOutputBuffer(bufferInfo, 10000);

    if (outputBufferId >= 0) {
        // outputBuffers[outputBufferId] is ready to be processed or rendered.
        Timber.e("Rendering Data with Index of: %s", outputBufferId);
        codec.releaseOutputBuffer(outputBufferId, true);

    } else if (outputBufferId == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
        outputBuffers = codec.getOutputBuffers();
    } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
        // Subsequent data will conform to new format.
        //format = codec.getOutputFormat();
    }
}

 MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080);
                    codec = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
                    codec.configure(format, surfaceVideo.getHolder().getSurface(), null, 0);
                    codec.start();


                    inputBuffers = codec.getInputBuffers();
                    outputBuffers = codec.getOutputBuffers();

      while (streaming) {

          //receive RTP Packet
          h264Parser(rtpPacket.getPayload());

      }

h264Parser 看起来像这样:

void h264Parser(byte[] payload) {

    int packetType = (byte) payload[0] & (byte) 0x1F;
    boolean startBit = (payload[1] & 0x80) != 0;
    boolean endBit = (payload[1] & 0x40) != 0;
    int flags = 0;

    switch (packetType) {
        case 7:
            pps = new ByteArrayOutputStream();
            pps.write(prefix);
            pps.write(payload);
            break;
        case 8:
            if (pps.size() > 0) {
               pps.write(payload);
               hasPps = true;
               flags = MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
               payload = pps.toByteArray();
               //Send packet to decoder
               videoCodec(ByteBuffer.wrap(payload), flags);
            break;
        case 28:

            if (hasPps) {
                if (startBit) {
                    baos = new ByteArrayOutputStream();
                    baos.write(prefix);
                    baos.write(payload);
                } else if (endBit) {
                        if(baos != null) {
                            baos.write(payload);
                            flags = MediaCodec.BUFFER_FLAG_KEY_FRAME;
                            payload = baos.toByteArray();
                            //Send packet to decoder
                            videoCodec(ByteBuffer.wrap(payload), flags);
                            hasPps = false;
                } else {
                        if(baos != null ) {
                            baos.write(payload);
                        }
                }
            }

            break;
        case 1:
            break;
        default:
    }

【问题讨论】:

标签: android h.264 rtp android-mediacodec live-video


【解决方案1】:

据我所知,MediaCodec 使用完整的访问单元,而不仅仅是切片(有人纠正我的错误)

因此,您必须构建一个完整的访问单元形式的 RTP 并将其提供给解码器(遗憾的是,我没有 RTP 经验,无法帮助您构建一个)。

您将访问单元发送到解码器,如下所示:

出列输入缓冲区

int inputBufferIndex = decoder.dequeueInputBuffer(TIMEOUT_USEC); ByteBuffer inputBuffer = videoDecoderInputBuffers[videoInputBufIndex];

用您的访问单元填充它

inputBuffer.put(acessUnit); inputBuffer.flip();

将缓冲区排队等待解码

decoder.queueInputBuffer(inputBufferIndex,0,inputBuffer.limit(), 0, FLAGS);

希望对你有帮助

【讨论】:

  • 谢谢,克里斯!这很有帮助。我现在正在挖掘。
  • 我不断得到 -1 的 outputBufferId。我怀疑这意味着没有可用的输出缓冲区出列。有什么想法吗?
  • 没错,如果您提供无法处理的媒体编解码器输入,情况也是如此。请记住,您必须将 SPS 和 PPS 馈送到解码器。您可以通过传递给解码器 (format.setByteBuffer("csd-0", ByteBuffer.wrap(header_sps)); format.setByteBuffer("csd-1", ByteBuffer.wrap(header_pps));) 的 MediaFormat 或使用标志 BUFFER_FLAG_CODEC_CONFIG 将它们作为第一个缓冲区发送
  • 我很好奇inputBuffer.flip()。解码器需要一定的字节序吗?此外,我还看到了在将更多数据放入缓冲区之前清除缓冲区的实现。
  • 你得到一个已经空的缓冲区(可能是特定于设备的,你可以检查位置和限制以进行验证)但清除永远不会受到伤害,因为它只会将位置设置为 0 并限制容量。 inputBuffer.flip() 仅准备要读取的缓冲区,因为它将位置限制和位置设置为 0
猜你喜欢
  • 2014-01-04
  • 1970-01-01
  • 2018-10-17
  • 2018-09-16
  • 1970-01-01
  • 2017-02-17
  • 2017-12-16
  • 2013-09-22
  • 2014-02-06
相关资源
最近更新 更多