2016-07-05 33 views
1

Ich lese manuell einen RTP/H264-Stream und übergebe die H264-Frames an den Android MediaCodec. Ich benutze den "markerBit" als Rahmen für die Frames. Der MediaCodec ist an eine OpenGL-Textur (SurfaceTexture) gebunden. Im Allgemeinen funktioniert alles gut. Der Decoder scheint jedoch Frames zu puffern. Wenn ich einen Frame in den Decoder lege, wird er nicht sofort auf die Textur gerendert. Nachdem ich 2-3 Frames mehr in den Decoder gesteckt habe, wird der erste Frame in die Textur gerendert.Android MediaCodec scheint H264-Frames zu puffern

Ich implementiere gegen Android 4.4.4.

private static final int INFINITE_TIMEOUT = -1; 
private static final int TIMEOUT_OUTPUT_BUFFER_MEDIA_CODEC = 1000; 
... 
int bufferIndex = codec.dequeueInputBuffer(INFINITE_TIMEOUT); 
if (bufferIndex < 0) { 
    throw new RuntimeException("Error"); 
} 

ByteBuffer inputBuffer = inputBuffers[bufferIndex]; 
inputBuffer.clear(); 

// Copy H264 data to inputBuffer 
h264Frame.fill(inputBuffer); 

codec.queueInputBuffer(bufferIndex, 0, inputBuffer.position(), 0, 0); 
drainOutputBuffers(); 
... 

und

private boolean drainOutputBuffers() { 
MediaCodec.BufferInfo buffInfo = new MediaCodec.BufferInfo(); 

int outputBufferIndex = codec.dequeueOutputBuffer(buffInfo, TIMEOUT_OUTPUT_BUFFER_MEDIA_CODEC); 

if (outputBufferIndex >= 0) { 
    codec.releaseOutputBuffer(outputBufferIndex, true); 
    return true; 
} 

switch (outputBufferIndex) { 
    case MediaCodec.INFO_TRY_AGAIN_LATER: 
    LOG.debug("Could not dequeue output buffer. Try again later"); 
    break; 
    case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: 
    LOG.warn("The output format has changed."); 
    break; 
    case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: 
    LOG.warn("The output buffers has changed."); 
    break; 
    default: 
    LOG.warn("The output buffer index was negative: {}", outputBufferIndex); 
} 
return false; 
} 

Auf der Rendering-Seite ich den „onFrameAvailable“ Rückruf verwenden, um zu überprüfen, ob ich die Textur auf dem OPENGL Thema habe zu aktualisieren. Das Flag, das ich für die Überprüfung verwende, wird durch eine Sperre geschützt (synchronisiert).

Ich vermute, dass der Präsentationszeitstempel das Rendering beeinflussen kann. Aber ich setze es auf 0. Daher nehme ich an, dass der Rahmen ohne Verzögerung gerendert werden soll.

Ich möchte das Bild auf die Textur gerendert haben, ohne zusätzliche Frames zu setzen.

+1

Ich glaube nicht, dass es die Natur für h264 Decoder ist, ist es wahrscheinlich ein Fehler in MediaCodec. Wenn ich dies auf einem PC mit ffmpeg mache, gibt es keine Verzögerung. – user3667089

Antwort

1

Vom MediaCodec documentation

Vollstreckungsstaat hat drei Unterzustände: Flushed, Rennen und End-of-Strom. Unmittelbar nach start() ist der Codec im Flushed Sub-State, wo er alle Puffer enthält. Sobald der erste Eingangspuffer aus der Warteschlange genommen wird, wechselt der Codec in den Unterzustand Running, wo er die meiste Zeit verbraucht. Wenn Sie einen Eingangspuffer mit dem End-of-Stream-Marker in die Warteschlange stellen, wechselt der Codec in den Unterzustand End-of-Stream . In diesem Zustand akzeptiert der Codec nicht mehr weitere Eingaben Puffer, aber erzeugt immer noch Ausgabepuffer, bis das Ende des Datenstroms auf dem Ausgang erreicht ist. Sie können jederzeit mit dem Befehl flush() im Ausführungsstatus in den Unterzustand Flushed unter zurückkehren.

Sie müssen einen Eingangspuffer mit dem Marker end-of-stream in die Warteschlange stellen. Tun Sie dies mit dem ersten Frame, den Sie dem Decoder zuführen (stellen Sie sicher, dass es sich um einen Keyframe handelt).

Dieser Punkt soll dem Decoder mitteilen, dass er keine Frames mehr erwartet und daher sofort mit der Wiedergabe beginnt. Ansonsten ist es normal, 3 oder 4 Frames zu füttern, bevor Sie etwas sehen. Dies ist eine Erwartung aller MPEG-Decoder und ist nicht Android-bezogen.

+0

Wenn ich ein End-of-Stream-Flag zusammen mit dem ersten Schlüsselbild sende, funktioniert der Mediacodec nicht mehr. – Soccertrash

+1

In diesem ** [link] (https://developer.android.com/reference/android/media/MediaCodec.html) ** Ich habe gepostet, scrolle runter zu "States" und lies das sorgfältig durch. Überlegen Sie sich auch 'flush()' auf dem Decoder. Der Punkt ist: jeder Rahmen ist Teil einer ** G ** Gruppe ** O ** f ** P ** Bilder (zB: ** I ** wird gefolgt von ** P ** & ** B ** Rahmen). P & B benötigt immer ** I ** (Keyframe), so dass es als Referenz beiseite gelegt wird, während Sie 3 oder 4 (P oder B) Frames senden, bevor Sie etwas sehen. Der Punkt von '' end of stream'' und '' flush' "ist gleichbedeutend mit" es gibt nichts nach meinem Keyframe, zeige es jetzt an ". –

+1

Ich meinte, lesen Sie den Link und wenden Sie an, was die Dokumentation sagt. Ihr Problem liegt darin, dass der MPEG-Decoder einen Keyframe (Vollbild) als Referenz für einige P- und B-Frames enthält (diese haben weniger Bildinformationen, so dass sie fehlende Teile aus dem Keyframe erhalten). Entferne den Stream, wenn das hilft. Die Lösung ist, dem System irgendwie zu sagen, dass nach dem einen Frame, den Sie zur Decodierung gesendet haben, der Stream nun vorbei ist und so die Daten zum Decoder (Anzeigebild) weiterleitet ... –

-1

Der Mediacodec-Decoder puffert 6-7 Frames vor der Ausgabe des ersten decodierten Ausgangsframes. Es scheint im mediacodec Fehler zu machen. Dies wird ein Problem in der Streaming-Anwendung sein. Bis jetzt zeigt mein Debugging Decodierung H264 mit Mediacodec 6-7 Frames Verzögerung beim Start des Streams.