Napisałem H264 Encoder Stream pomocą MediaCodec API Androida. Przetestowałem go na około dziesięciu różnych urządzeniach z różnymi procesorami i działał na nich wszystkich, z wyjątkiem zasilanych Snapdragonem 800 (Google Nexus 5 i Sony Xperia Z1). Na tych urządzeniach uzyskać SPS i PPS oraz pierwszą klatkę kluczową, ale po tym mEncoder.dequeueOutputBuffer (mBufferInfo, 0) zwraca tylko MediaCodec.INFO_TRY_AGAIN_LATER. Eksperymentowałem już z różnymi limitami czasu, bitraty, rozdzielczości i innymi opcjami konfiguracyjnymi, bezskutecznie. Wynik jest zawsze taki sam.MediaCodec H264 Enkoder nie działa na Snapdragon 800 urządzeń
używam następujący kod do zainicjowania Encoder:
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);
wybranego formatu kolor jest:
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;
}
}
I uzyskać dane wykonując
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);
}
}
Thanks za pomoc!
Ile wejściowe klatki są w kolejce w momencie gdy stragany wyjściowe? (Chcę się upewnić, że nie jest to po prostu głód w celu wprowadzenia.) Czy jest coś podejrzanie wyglądającego w logcat? (Kodeki mają tendencję do rozpylania Log.e, co może utrudniać określenie.) Jaki format koloru jest wybierany? (Czy jest to format QCOM?) Czy rozmiar "surowej ramki" jest dokładnie taki sam jak pojemność bufora wejściowego? (Jeśli nie ... dlaczego nie?) – fadden
@fadden Nie ma znaczenia, jak długo pozwalam, aby działał, ale zawsze wydaje się mieć 5 ramek w buforze wejściowym. Jego wyjście podczas tworzenia to: 'I/OMXClient (11245): Używanie MUX-ów OMX po stronie klienta. I/ACodec (11245): setupVideoEncoder powiodło się "Wybrany format koloru jest w obu przypadkach" MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar' (Jeśli wyszukuję wszystkie formaty, ma tylko dwa, wyżej wymienione i taki, który ma stałą 2130708361, która ulega awarii, jeśli została wybrana .) Surowa ramka i bufor wejściowy nie są takie same (surowy rozmiar ramki jest zawsze mniejszy, a pojemność bufora wejściowego zawsze wynosi 282624). – lowtraxx
Pięć ramek jest typowe - brzmi, jakby to nie było przetwarzanie danych wejściowych, a więc brak danych wyjściowych. Zakładam, że nazywasz 'encoder.start()'? YUV420SemiPlanar jest dobry; 2130708361 jest używany tylko do wprowadzania powierzchni. Rozmiar bufora YUV420 powinien wynosić "szerokość * wysokość * 1,5" lub 460800 bajtów, więc jestem nieco zdezorientowany z powodu rozmiaru bufora. Czy widzisz komunikat "Format zmieniono format multimediów" w pliku dziennika, a jeśli tak, to co on mówi? – fadden