簡體   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