Android'in MediaCodec API'sini kullanarak bir H264 Akış Kodlayıcısı yazdım. Farklı işlemcilerle yaklaşık on farklı cihazda test ettim ve Snapdragon 800 güçlendirilmiş olanlar (Google Nexus 5 ve Sony Xperia Z1) haricinde hepsinde çalıştı. Bu cihazlarda SPS ve PPS ile ilk Keyframe'i elde ederim, ancak bundan sonra mEncoder.dequeueOutputBuffer (mBufferInfo, 0) sadece MediaCodec.INFO_TRY_AGAIN_LATER değerini döndürür. Zaten farklı zaman aşımları, bit hızları, kararlar ve diğer yapılandırma seçenekleriyle denemedim. Sonuç her zaman aynıdır.MediaCodec H264 Enkoder Snapdragon 800 cihazlarda çalışmaz
mBufferInfo = new MediaCodec.BufferInfo();
encoder = MediaCodec.createEncoderByType("video/avc");
MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", 640, 480);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 768000);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, mEncoderColorFormat);
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);
encoder.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
seçilen renk biçimidir:
Ben Encoder başlatmak için aşağıdaki kodu kullanabilirsiniz
MediaCodecInfo.CodecCapabilities capabilities = mCodecInfo.getCapabilitiesForType(MIME_TYPE);
for (int i = 0; i < capabilities.colorFormats.length && selectedColorFormat == 0; i++)
{
int format = capabilities.colorFormats[i];
switch (format) {
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
case MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar:
selectedColorFormat = format;
break;
default:
LogHandler.e(LOG_TAG, "Unsupported color format " + format);
break;
}
}
Ve
ByteBuffer[] inputBuffers = mEncoder.getInputBuffers();
ByteBuffer[] outputBuffers = mEncoder.getOutputBuffers();
int inputBufferIndex = mEncoder.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0)
{
// fill inputBuffers[inputBufferIndex] with valid data
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(rawFrame);
mEncoder.queueInputBuffer(inputBufferIndex, 0, rawFrame.length, 0, 0);
LogHandler.e(LOG_TAG, "Queue Buffer in " + inputBufferIndex);
}
while(true)
{
int outputBufferIndex = mEncoder.dequeueOutputBuffer(mBufferInfo, 0);
if (outputBufferIndex >= 0)
{
Log.d(LOG_TAG, "Queue Buffer out " + outputBufferIndex);
ByteBuffer buffer = outputBuffers[outputBufferIndex];
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0)
{
// Config Bytes means SPS and PPS
Log.d(LOG_TAG, "Got config bytes");
}
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0)
{
// Marks a Keyframe
Log.d(LOG_TAG, "Got Sync Frame");
}
if (mBufferInfo.size != 0)
{
// adjust the ByteBuffer values to match BufferInfo (not needed?)
buffer.position(mBufferInfo.offset);
buffer.limit(mBufferInfo.offset + mBufferInfo.size);
int nalUnitLength = 0;
while((nalUnitLength = parseNextNalUnit(buffer)) != 0)
{
switch(mVideoData[0] & 0x0f)
{
// SPS
case 0x07:
{
Log.d(LOG_TAG, "Got SPS");
break;
}
// PPS
case 0x08:
{
Log.d(LOG_TAG, "Got PPS");
break;
}
// Key Frame
case 0x05:
{
Log.d(LOG_TAG, "Got Keyframe");
}
//$FALL-THROUGH$
default:
{
// Process Data
break;
}
}
}
}
mEncoder.releaseOutputBuffer(outputBufferIndex, false);
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0)
{
// Stream is marked as done,
// break out of while
Log.d(LOG_TAG, "Marked EOS");
break;
}
}
else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED)
{
outputBuffers = mEncoder.getOutputBuffers();
Log.d(LOG_TAG, "Output Buffer changed " + outputBuffers);
}
else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED)
{
MediaFormat newFormat = mEncoder.getOutputFormat();
Log.d(LOG_TAG, "Media Format Changed " + newFormat);
}
else if(outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER)
{
// No Data, break out
break;
}
else
{
// Unexpected State, ignore it
Log.d(LOG_TAG, "Unexpected State " + outputBufferIndex);
}
}
Teşekkür yaparak veri almak yardımın için!
Çıkış durduğunda kaç tane giriş karesi sıraya alındı? (Sadece giriş için aç bırakılmadığından emin olmak istiyorum.) Logcat'ta şüpheli görünen bir şey var mı? (Codec'ler anlatmak zorlaştırabilir Log.e spreyleme eğilimindedir.) Hangi renk formatı seçilir? (QCOM formatı mı?) "Ham çerçevenizin" büyüklüğü, giriş arabelleğinin kapasitesi ile tam olarak aynı mı? (Değilse ... neden olmasın?) – fadden
@fadden Ne kadar süre çalışmasına izin verdiğim önemli değil ama her zaman giriş arabelleklerinde 5 kareye sahip gibi görünüyor. Yaratılış üzerine çıktı: 'I/OMXClient (11245): İstemci tarafı OMX mux kullanımı. I/ACodec (11245): setupVideoEncoder başarılı oldu The Seçilen renk formatı her iki durumda da 'MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar' (Tüm biçimleri sorguladığımda, yalnızca iki tane var, yukarıda bahsedilen ve seçiliyse kilitlenen sabit bir 2130708361 olan .) Ham çerçeve ve giriş arabelleği aynı değildir (ham kare boyutu her zaman daha küçüktür ve giriş arabelleği kapasitesi her zaman 282624'tür) – lowtraxx
Beş kare tipiktir - girişleri işlemiyor gibi değil, dolayısıyla çıkış yok. Ben encoder.start() 'diyorsun varsayalım? YUV420SemiPlanar iyidir; 2130708361 sadece Yüzey girişi için kullanılır. YUV420 arabelleğinin boyutu, genişlik * yükseklik * 1.5 'veya 460800 bayt olmalıdır, bu yüzden arabellek boyutunuz hakkında biraz kafam karışmış olur. Günlük dosyasında "Medya Formatı Değişti" mesajını görüyor musunuz, eğer öyleyse, ne diyor? – fadden