Ho scritto un codificatore di flusso H264 utilizzando l'API MediaCodec di Android. L'ho provato su una decina di dispositivi diversi con processori diversi e ha funzionato su tutti, tranne su quelli alimentati con Snapdragon 800 (Google Nexus 5 e Sony Xperia Z1). Su quei dispositivi ottengo SPS e PPS e il primo Keyframe, ma successivamente mEncoder.dequeueOutputBuffer (mBufferInfo, 0) restituisce solo MediaCodec.INFO_TRY_AGAIN_LATER. Ho già sperimentato diversi timeout, bitrate, risoluzioni e altre opzioni di configurazione, senza alcun risultato. Il risultato è sempre lo stesso.MediaCodec H264 Encoder non funziona su dispositivi Snapdragon 800
Io uso il seguente codice per inizializzare il codificatore:
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);
in cui il formato del colore selezionato è:
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;
}
}
e ottengo i dati facendo
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);
}
}
Grazie per il tuo aiuto!
Quanti frame di input vengono messi in coda nel punto in cui l'output si blocca? (Voglio assicurarmi che non stia semplicemente morendo di fame per input.) C'è qualcosa di sospetto in cerca di logcat? (I codec tendono a spruzzare Log.e, il che può rendere difficile dirlo). Quale formato di colore viene selezionato? (È il formato QCOM?) La dimensione della "cornice grezza" è esattamente uguale alla capacità del buffer di input? (Se no ... perché no?) – fadden
@fadden Non importa per quanto tempo lo lascio girare ma sembra sempre avere 5 frame nei buffer di input. Il suo output al momento della creazione è: 'I/OMXClient (11245): utilizzo di mux OMX lato client. I/ACodec (11245): setupVideoEncoder riuscito' Il formato colore selezionato è in entrambi i casi 'MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar' (Se interrogo tutti i formati ne ha solo due, il sopra e uno che ha una costante 2130708361 che si arresta se selezionato .) Il frame non elaborato e il buffer di input non sono gli stessi (la dimensione raw del frame è sempre minore e la capacità del buffer di input è sempre 282624) – lowtraxx
Cinque frame è tipico - sembra che non sia l'input di elaborazione, quindi nessun output. Suppongo che tu stia chiamando 'encoder.start()'? YUV420SemiPlanar è buono; 2130708361 è utilizzato solo per l'input di superficie. La dimensione di un buffer YUV420 dovrebbe essere "width * height * 1.5', o 460800 byte, quindi sono un po 'confuso riguardo la dimensione del buffer. Vedete il vostro messaggio "Media Format Changed" nel file di log e, in caso affermativo, cosa dice? – fadden