我正在尝试使用 MediaCodec 将帧(通过相机或解码器)编码为视频。通过dequeueOutputBuffer()处理编码器输出时,我期望收到返回index = MediaCodec.INFO_OUTPUT_FORMAT_CHANGED,所以我可以调用getOutputFormat()获取编码器输出格式作为当前使用的ffmpeg复用器的输入。
我已经测试了一些 Android 版本 4.1~4.3 的平板/电话设备。它们都具有至少一个硬件视频 AVC 编码器并用于测试。在 4.3 版本的设备上,编码器在按预期写入编码数据之前给出 MediaCodec.INFO_OUTPUT_FORMAT_CHANGED,并且从 getOutputFormat() 返回的输出格式可以被复用器正确使用。在 4.2.2 或更低版本的设备上,编码器从不提供 MediaCodec.INFO_OUTPUT_FORMAT_CHANGED,但它仍然可以输出编码的基本流,但复用器无法知道确切的输出格式。
我想问以下问题:
- 编码器的行为(在输出编码数据之前是否提供 MediaCodec.INFO_OUTPUT_FORMAT_CHANGED)是否取决于 Android API 级别或单个设备上的芯片?
- 如果编码器在 MediaCodec.INFO_OUTPUT_FORMAT_CHANGED 出现之前写入数据,有没有什么办法可以得到编码数据的输出格式?
- 编码器仍然在编码数据之前在设备上输出编解码器配置数据(带有标志 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,以及一个value = 1869968451的“what”字段。(我不明白“what”字段。它似乎是一个常数,不是必需的。谁能告诉我它的含义?)
如果我将编解码器配置数据解析为 csd-1 字段(最后 8 个字节)和 csd-0 字段(剩余字节),则复用器似乎可以正常工作并输出可在所有测试设备上播放的视频。(但我想问:这个 8 字节的假设是否正确,还是有更可靠的方法来解析数据?)
但是,我遇到了另一个问题,如果我再次通过 Android MediaCodec 解码视频,则 dequeueOutputBuffer() 获得的 BufferInfo.presentationTimeUs 值对于大多数解码帧都是 0。只有最后几帧有正确的时间。MediaExtractor.getSampleTime() 获得的采样时间是正确的,并且正是我为编码器/复用器设置的值,但解码的帧时间不是。此问题仅发生在 4.2.2 或更低版本的设备上。
奇怪的是帧时间不正确,但视频可以在设备上以正确的速度播放。(我测试的大多数 4.2.2 或更低版本的设备只有 1 个 Video AVC 解码器。)我是否需要设置可能影响演示时间的其他字段?