繁体   English   中英

在Android 4.1 / 4.2设备中将MediaCodec.getOutputFormat()用于编码器的问题

[英]Problems of using MediaCodec.getOutputFormat() for an encoder in Android 4.1/4.2 devices

我正在尝试使用MediaCodec将帧(通过摄像机或解码器)编码为视频。 在通过dequeueOutputBuffer()处理编码器输出时,我希望收到返回索引= 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,尽管它仍然可以输出编码后的基本流,但是复用器无法知道确切的输出格式。

我想问以下问题:

  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个Video AVC解码器。)是否需要设置其他可能影响演示时间的字段?

在Android 4.3中更改了MediaCodec编码器的行为,以适应MediaMuxer类的引入。 在Android 4.3中,您将始终从编码器收到INFO_OUTPUT_FORMAT_CHANGED 在以前的版本中,您不会。 (我已经更新了相关的常见问题解答条目 。)

无法向编码器查询MediaFormat

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

要正确初始化视频的ffmpeg混合器,请使用以下命令:

int outputBufferIndex = videoCodec.dequeueOutputBuffer(bufferInfo, -1);
if (MediaCodec.BUFFER_FLAG_CODEC_CONFIG == bufferInfo.flags) {
    ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
    headerData = new byte[bufferInfo.size];
    outputBuffer.get(headerData);

    // jni call
    WriteVideoHeader(headerData, headerData.length);

    videoCodec.releaseOutputBuffer(outputBufferIndex, false);   
}

在jni中,我使用如下代码:

jint Java_com_an_FileWriterEx_WriteVideoHeader(JNIEnv * env, jobject this, jbyteArray data, jint datasize)
{
    jboolean isCopy;
    jbyte* rawjBytes = (*env)->GetByteArrayElements(env, data, &isCopy);    
    WriteVideoHeaderInternal(env, m_pFormatCtx, m_pVideoStream, rawjBytes, datasize);   
    (*env)->ReleaseByteArrayElements(env, data, rawjBytes, 0);
    return 0;
}

jint WriteVideoHeaderInternal(JNIEnv * env, AVFormatContext* pFormatCtx, AVStream*     pVideoStream, jbyte* data, jint datasize)
{
  jboolean bNoError = JNI_TRUE;

  jbyte* pExtDataBuffer = av_malloc(datasize);
  if(!pExtDataBuffer) 
  {
    LOGI("av alloc error\n");
    bNoError = JNI_FALSE;
  }

  if (bNoError)
  {
    memcpy(pExtDataBuffer, data, datasize * sizeof(jbyte));

    pVideoStream->codec->extradata = pExtDataBuffer;
    pVideoStream->codec->extradata_size = datasize;
  } 
}

对于解析编解码器配置数据,假设最后8个字节是PPS数据是错误的。 必须根据起始代码和nal_unit_type解析数据。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM