android - 在 Android 4.1/4.2 设备中使用 MediaCodec.getOutputFormat() 作为编码器的问题

标签 android ffmpeg encoder android-mediacodec muxer

我正在尝试使用 MediaCodec 将帧(通过相机或解码器)编码为视频。 当通过 dequeueOutputBuffer() 处理编码器输出时,我期望收到返回索引 = MediaCodec.INFO_OUTPUT_FORMAT_CHANGED,因此我可以调用 getOutputFormat() 来获取编码器输出格式作为当前使用的 ffmpeg muxer 的输入。

我测试了一些Android版本4.1~4.3的pad/phone设备。它们都至少有一个硬件视频AVC编码器并用于测试。在版本 4.3 的设备上,编码器在按预期写入编码数据之前会给出 MediaCodec.INFO_OUTPUT_FORMAT_CHANGED,并且从 getOutputFormat() 返回的输出格式可以被复用器正确使用。在 4.2.2 或更低版本的设备上,编码器永远不会给出 MediaCodec.INFO_OUTPUT_FORMAT_CHANGED,同时它仍然可以输出编码的基本流,但复用器无法知道确切的输出格式。

我想问以下问题:

  1. 编码器的行为(在输出编码数据之前是否给出 MediaCodec.INFO_OUTPUT_FORMAT_CHANGED)是否取决于 Android API 级别或各个设备上的芯片?
  2. 如果编码器在MediaCodec.INFO_OUTPUT_FORMAT_CHANGED出现之前写入数据,有没有办法获取编码数据的输出格式?
  3. 编码器仍然在编码数据之前在设备上输出编解码器配置数据(带有标志 MediaCodec.BUFFER_FLAG_CODEC_CONFIG)。它主要用于配置解码器,但是我可以通过编解码器配置数据导出输出格式吗?

我尝试了这些解决方案来获取输出格式但失败了:

  • 在整个编码过程中频繁调用 getOutputFormat()。但是,它们都会抛出 IllegalStateException,但不会出现 MediaCodec.INFO_OUTPUT_FORMAT_CHANGED。
  • 在开始时使用初始 MediaFormat 来配置编码器,如示例:

    m_init_encode_format = MediaFormat.createVideoFormat(m_encode_video_mime, m_frame_width, m_frame_height);
    int encode_bit_rate = 3000000;
    int encode_frame_rate = 15;
    int encode_iframe_interval = 2;
    
    m_init_encode_format.setInteger(MediaFormat.KEY_COLOR_FORMAT, m_encode_color_format);
    m_init_encode_format.setInteger(MediaFormat.KEY_BIT_RATE, encode_bit_rate);
    m_init_encode_format.setInteger(MediaFormat.KEY_FRAME_RATE, encode_frame_rate);
    m_init_encode_format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, encode_iframe_interval);
    
    m_encoder = MediaCodec.createByCodecName(m_video_encoder_codec_info.getName());
    m_encoder.configure(m_init_encode_format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    
    // Assume m_init_encode_format is the output format of the encoder
    

    但是它失败了,因为编码器的输出格式仍然比初始格式“更改”。

请帮助我了解编码器的行为,以及如果缺少所需的 MediaCodec.INFO_OUTPUT_FORMAT_CHANGED,是否有任何解决方案来查询输出格式。


通过比较输出格式和编解码器配置数据,缺少的字段为 csd-0、csd-1 和值为 1869968451 的“what”字段。 (我不明白“what”字段。它似乎是一个常量,不是必需的。谁能告诉我它的含义吗?)

如果我将编解码器配置数据解析为 csd-1 字段(最后 8 个字节)和 csd-0 字段(剩余字节),则复用器似乎可以正常工作并输出可在所有测试设备上播放的视频。 (但我想问:这个8字节的假设是否正确,或者有更可靠的方法来解析数据?)

但是,我遇到了另一个问题,如果我再次使用 Android MediaCodec 解码视频,则 dequeueOutputBuffer() 获得的 BufferInfo.presentationTimeUs 值对于大多数解码帧来说都是 0。只有最后几帧的时间是正确的。 MediaExtractor.getSampleTime() 获取的采样时间是正确的,并且与我为编码器/复用器设置的值完全相同,但解码的帧时间不是。此问题仅发生在 4.2.2 或更低版本的设备上。

奇怪的是,帧时间不正确,但视频可以在设备上以正确的速度播放。 (我测试过的大多数 4.2.2 或更低版本的设备只有 1 个视频 AVC 解码器。)我是否需要设置可能影响演示时间的其他字段?

最佳答案

Android 4.3 中更改了 MediaCodec 编码器的行为,以适应 MediaMuxer 类的引入。在 Android 4.3 中,您将始终从编码器接收 INFO_OUTPUT_FORMAT_CHANGED。在以前的版本中,您不会。 (我已经更新了 relevant FAQ entry 。)

无法查询编码器的 MediaFormat

我没有使用过基于 ffmpeg 的复用器,所以我不确定它需要什么信息。如果它正在寻找 csd-0/csd-1 键,您可以从 CODEC_CONFIG 数据包中提取它们(我认为您必须解析 SPS/PPS 值并将它们放在单独的键中) 。在 4.3 设备上检查 MediaFormat 的内容将显示缺少哪些字段。

关于android - 在 Android 4.1/4.2 设备中使用 MediaCodec.getOutputFormat() 作为编码器的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22324990/

相关文章:

C++ FFmpeg 创建 mp4 文件

android - 如何将 apk 分发给有限的用户。而且他们不能重新分发给其他人?

android insmod init_module 失败(所需 key 不可用)

android - 我们什么时候知道 Activity 的 View 何时被布局

c++ - ffmpeg解码器似乎丢帧

javascript - Filtergraph描述中的流说明符 '' [0][1]concat=a=1 :n=1:v=1[s0] matches no streams

video - RTP 的 H264 关键帧问题

ubuntu - 为 ubuntu 16.04 安装 FFMPEG 的所有编码器?

android - 要求用户在更新时卸载以前的 Android 应用程序版本