Ich habe einen H264 Stream Encoder mit der MediaCodec API von Android geschrieben. Ich habe es auf ungefähr zehn verschiedenen Geräten mit verschiedenen Prozessoren getestet und es hat an allen von ihnen funktioniert, außer bei Snapdragon 800 (Google Nexus 5 und Sony Xperia Z1). Auf diesen Geräten bekomme ich SPS und PPS und das erste Keyframe, aber danach gibt mEncoder.dequeueOutputBuffer (mBufferInfo, 0) nur MediaCodec.INFO_TRY_AGAIN_LATER zurück. Ich habe bereits mit verschiedenen Timeouts, Bitraten, Auflösungen und anderen Konfigurationsoptionen experimentiert, ohne Erfolg. Das Ergebnis ist immer das Gleiche.MediaCodec H264 Encoder funktioniert nicht auf Snapdragon 800 Geräten
Ich verwende den folgenden Code, um den Encoder zu initialisieren:
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);
, wo das ausgewählte Farbformat ist:
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;
}
}
Und ich die Daten erhalten, indem
tun 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);
}
}
Dank für deine Hilfe!
Wie viele Eingaberahmen werden zu dem Zeitpunkt eingereiht, an dem der Ausgang blockiert? (Ich möchte sicherstellen, dass es nicht nur nach Eingabe hungert.) Gibt es etwas Verdächtiges im Logcat? (Codecs neigen dazu, Log.e zu sprühen, was es schwierig machen kann, zu sagen.) Welches Farbformat wird ausgewählt? (Ist es das QCOM-Format?) Ist die Größe Ihres "rohen Rahmens" genau so groß wie die Kapazität des Eingangspuffers? (Wenn nicht ... warum nicht?) – fadden
@fadden Es spielt keine Rolle, wie lange ich es laufen lasse, aber es scheint immer 5 Rahmen in den Eingabepuffern zu haben. Seine Ausgabe beim Erstellen lautet: 'I/OMXClient (11245): Verwendung des clientseitigen OMX mux. I/ACodec (11245): setupVideoEncoder erfolgreich Das ausgewählte Farbformat ist in beiden Fällen 'MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar' (Wenn ich alle Formate abfrage, hat es nur zwei, das oben genannte und eins, das eine Konstante 2130708361 hat, die abstürzt, falls ausgewählt .) Der Raw-Frame und der Input-Buffer sind nicht identisch (Raw-Frame-Größe ist immer kleiner und die Input-Buffer-Kapazität ist immer 282624) – lowtraxx
Fünf Frames ist typisch - klingt wie es keine Eingabe verarbeitet, daher keine Ausgabe. Ich nehme an, du nennst 'encoder.start()'? YUV420SemiPlanar ist gut; 2130708361 wird nur für den Oberflächeneingang verwendet. Die Größe eines YUV420-Puffers sollte "width * height * 1,5" oder 460800 Bytes sein, daher bin ich etwas verwirrt über Ihre Puffergröße. Wird die Meldung "Medienformat geändert" in der Protokolldatei angezeigt, und wenn ja, was heißt es? – fadden