![](/img/trans.png)
[英]How to get the colorFormats supported by MediaCodec on android 4.1 and 4.2?
[英]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,盡管它仍然可以輸出編碼后的基本流,但是復用器無法知道確切的輸出格式。
我想問以下問題:
我嘗試了以下解決方案以獲取輸出格式,但失敗了:
使用最初的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.