Android MediaCodec 如何精确剪辑音频帧

4
我正在开发能够精确裁剪Android视频文件的能力。转码是通过MediaExtractorMediaCodecMediaMuxer实现的。我需要帮助截取任意音频帧,以便与它们的视频帧对应。
我相信必须在解码器输出缓冲区中裁剪音频帧,因为这是可编辑的未压缩音频数据的逻辑位置。
对于输入/输出裁剪,我正在计算原始音频缓冲区的必要偏移和大小调整,以将其装入可用的结尾帧中,并使用以下代码提交数据:
MediaCodec.BufferInfo info = pendingAudioDecoderOutputBufferInfos.poll();
...
ByteBuffer decoderOutputBuffer = audioDecoder.getOutputBuffer(decoderIndex).duplicate();
decoderOutputBuffer.position(info.offset);
decoderOutputBuffer.limit(info.offset + info.size);
encoderInputBuffer.position(0);
encoderInputBuffer.put(decoderOutputBuffer);
info.flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
audioEncoder.queueInputBuffer(encoderIndex, info.offset, info.size, presentationTime, info.flags);
audioDecoder.releaseOutputBuffer(decoderIndex, false);

我的问题是,数据调整似乎只影响复制到输出音频缓冲区的数据,而不是缩短写入MediaMuxer的音频帧。输出视频要么在剪辑末尾出现几毫秒的丢失音频,要么如果我写入太多数据,则完全从剪辑末尾删除音频帧。
如何正确地裁剪音频帧?

如果我理解正确的话,当您调用queueInputBuffer时使用info.offset似乎存在差异。编码器输入缓冲区难道不是从0到info.size而不是从info.offset到(info.offset + info.size)吗?也许您经历的时间差正好是0到偏移量之间的数据量? - Dave
@Dave 我相信你是正确的。decoderOutputBufferencoderInputBufferaudioEncoder.queueInputBuffer没有保证会使用相同的sizeoffset值。我尝试了几种组合。我也相信我只是减小了数据的大小,但并没有减小容器的大小。我开始考虑解决方案可能涉及到类似于MediaCodec.BUFFER_FLAG_CODEC_CONFIG的配置更改。 - David Manpearl
1个回答

4
这里有几个要点:
  • 正如Dave所指出的,你应该向audioEncoder.queueInputBuffer传递0而不是info.offset - 当你使用decoderOutputBuffer.position(info.offset);设置缓冲区位置时,你已经考虑到了解码器输出缓冲区的偏移量。但也许你已经更新了它。

  • 我不确定MediaCodec音频编码器是否允许您以任意大小的块传递音频数据,或者您是否需要一次发送完整的音频帧。我认为它可能会接受它 - 那么你就没问题了。如果不行,你需要自己缓冲音频,并在有完整帧时将其传递给编码器(如果你在开始时裁剪了一些)。

  • 请记住,音频也是基于帧的(对于AAC来说,它是1024个样本帧,除非您使用低延迟变体或HE-AAC),因此对于44 kHz,您只能具有23毫秒粒度的音频持续时间。如果您想让音频在正确数量的样本后精确结束,则需要使用容器信令来指示这一点。我不确定MediaCodec音频编码器是否会刷新最后半帧,或者是否需要手动在结尾处传递额外的零来获取最后几个样本,如果您未对齐帧大小。不过也许这并不需要。

  • 编码AAC音频会在音频流中引入一些延迟;解码后,你会在解码流的开始有一些预热样本(这些样本的确切数量取决于编码器 - 对于AAC-LC在Android中的软件编码器,它可能是2048个样本,但也可能不同)。对于2048个样本的情况,它正好与2帧音频相吻合,但它也可能是一个不是整数帧的东西。我不认为MediaCodec会发出精确的延迟量信号。如果你从编码器中删除前两个输出包(如果延迟为2048个样本),你将避免额外的延迟,但前几帧的实际解码音频将不是完全正确的。(必须使用预热包能够正确表示你的流开始的任何样本,否则它将在2048个样本内更多或更少地收敛于你的预期音频。)


谢谢。您今年早些时候还回答了我的一个问题,并在这里为我提供了巨大的帮助:https://dev59.com/QVsV5IYBdhLWcg3wsgyh#35885471。我同意@Dave和您关于偏移量的看法。 - David Manpearl
@mstorsjo @David Manpearl 你们好,我使用MediaCodec将原始PCM数据编码为AAC原始数据,并将其解码回来。我正在尝试修复错误“音频不会立即处理,直到输入足够的数据”。例如,用户录制了“你好,我的名字是kidfrom”,只有“你好,我的名字”会被立即处理,3分钟后用户录制了“你来自哪里?”,再次只有“你来自哪里”会被立即处理。另一个用户听到的第一件事是“你好,我的名字是”,3分钟后是“kidfrom,你来自哪里”。这很奇怪。你们能帮我吗? - Jason Rich Darmawan
无论如何,我看了你的第二点,尝试将android.media.AudioRecord bufferSizeInBytes设置为2048,这是我认为帧大小的MediaCodec.BufferInfo.size值。但它并没有修复这个错误。 - Jason Rich Darmawan

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