Android MediaCodec Kodieren und dekodieren im asynchronen Modus

8

Ich versuche, ein Video aus einer Datei zu dekodieren und es mit MediaCodec in dem neuen Asynchronous Mode zu codieren, der in API Level 21 und höher unterstützt wird (Android OS 5.0 Lollipop).

Es gibt viele Beispiele dafür, dies im Synchronous-Modus auf Websites wie Big Flake , Google Grafika und Dutzende von Antworten auf StackOverflow, aber keiner von ihnen unterstützt den Asynchronous-Modus.

Ich muss das Video während des Vorgangs nicht anzeigen.

Ich glaube, dass das allgemeine Verfahren darin besteht, die Datei mit einem MediaExtractor als Eingabe für ein MediaCodec (Dekoder) zu lesen, damit die Ausgabe des Decoders in ein Surface rendern kann, das auch die gemeinsame Eingabe ist in ein MediaCodec (Encoder), und dann schließlich die Encoder-Ausgabedatei über ein MediaMuxer zu schreiben. Der Surface wird beim Setup des Encoders erstellt und mit dem Decoder geteilt.

Ich kann das Video in ein TextureView dekodieren, aber das Teilen von Surface mit dem Encoder anstelle des Bildschirms war nicht erfolgreich.

Ich habe MediaCodec.Callback() s für meine beiden Codecs eingerichtet. Ich glaube, ein Problem ist, dass ich nicht weiß, was ich in der Funktion onInputBufferAvailable() des Callbacks des Encoders tun soll. Ich weiß nicht, wie man Daten von Surface in den Encoder kopiert (was man wissen kann) - das sollte automatisch geschehen (wie bei der Decoder-Ausgabe mit codec.releaseOutputBuffer(outputBufferId, true); ). Dennoch glaube ich, dass onInputBufferAvailable einen Aufruf von codec.queueInputBuffer erfordert, um zu funktionieren. Ich weiß einfach nicht, wie man die Parameter einstellt, ohne Daten von etwas wie einem MediaExtractor zu erhalten, wie es auf der Decode-Seite verwendet wird.

Wenn Sie ein Beispiel haben, das eine Videodatei öffnet, diese entschlüsselt, sie mit den asynchronen MediaCodec -Rückrufen in eine andere Auflösung oder ein anderes Format codiert und sie dann als Datei speichert teilen Sie Ihren Beispielcode.

=== BEARBEITEN ===

Hier ist ein Arbeitsbeispiel im synchronen Modus dessen, was ich im asynchronen Modus zu tun versuche: ExtractDecodeEditEncodeMuxTest.java: Ссылка Dieses Beispiel funktioniert in meiner Anwendung

    
David Manpearl 09.03.2016, 06:34
quelle

2 Antworten

10

Ich glaube, Sie sollten nichts im onInputBufferAvailable() Callback des Encoders machen müssen - Sie sollten nicht encoder.queueInputBuffer() aufrufen. So wie Sie niemals encoder.dequeueInputBuffer() und encoder.queueInputBuffer() manuell aufrufen, wenn Sie die Surface-Eingabecodierung im synchronen Modus ausführen, sollten Sie dies auch nicht im asynchronen Modus tun.

Wenn Sie decoder.releaseOutputBuffer(outputBufferId, true); (sowohl im synchronen als auch im asynchronen Modus) aufrufen, löscht diese intern (mit dem von Ihnen bereitgestellten Surface ) einen Eingabepuffer von der Oberfläche, rendert die Ausgabe in diese und reiht sie in die Oberfläche ein (zum Encoder). Der einzige Unterschied zwischen dem synchronen und dem asynchronen Modus besteht darin, wie die Pufferereignisse in der öffentlichen API verfügbar gemacht werden. Bei Verwendung der Surface-Eingabe wird jedoch eine andere (interne) API verwendet, sodass der synchrone vs asynchrone Modus keine Rolle spielt das überhaupt.

Soweit ich weiß (obwohl ich es selbst nicht ausprobiert habe), sollten Sie den onInputBufferAvailable() -Rückruf einfach für den Encoder leer lassen.

BEARBEITEN: Also habe ich es selbst versucht und es ist (fast) so einfach wie oben beschrieben.

Wenn die Encoder-Eingangsoberfläche direkt als Ausgabe an den Decoder konfiguriert ist (ohne SurfaceTexture dazwischen), funktionieren die Dinge nur, wenn eine synchrone Decodier-Codier-Schleife in eine asynchrone umgewandelt wird.

Wenn Sie jedoch SurfaceTexture verwenden, können Sie auf einen kleinen Fehler stoßen. Es gibt ein Problem damit, wie man darauf wartet, dass Frames in Bezug auf den aufrufenden Thread in der SurfaceTexture ankommen, siehe Ссылка und Ссылка und Ссылка für Hinweise auf diese.

Das Problem, soweit ich es sehe, ist in awaitNewImage wie in Ссылка . Wenn der Callback onFrameAvailable im Hauptthread aufgerufen werden soll, haben wir ein Problem, wenn der Aufruf awaitNewImage auch im Hauptthread ausgeführt wird. Wenn die Callbacks onOutputBufferAvailable auch im Hauptthread aufgerufen werden und Sie awaitNewImage von dort aufrufen, haben wir ein Problem, da Sie am Ende auf einen Callback warten (mit einem wait() , der den gesamten Thread blockiert) kann nicht ausgeführt werden, bis die aktuelle Methode zurückkehrt.

Wir müssen also sicherstellen, dass die Callbacks onFrameAvailable in einem anderen Thread als dem Aufruf von awaitNewImage erscheinen. Eine ziemlich einfache Möglichkeit, dies zu tun, besteht darin, einen neuen separaten Thread zu erstellen, der nur die onFrameAvailable -Rückrufe bedient. Um dies zu tun, können Sie z.B. das:

%Vor%

Ich hoffe, dies ist genug, damit Sie Ihr Problem lösen können, lassen Sie mich wissen, wenn Sie mich brauchen, um eines der öffentlichen Beispiele zu bearbeiten, um dort asynchrone Rückrufe zu implementieren.

EDIT2: Da das GL-Rendering möglicherweise auch aus dem onOutputBufferAvailable -Rückruf erfolgt, könnte dies ein anderer Thread als der sein, der den EGL-Kontext eingerichtet hat. In diesem Fall muss der EGL-Kontext in dem Thread, der ihn eingerichtet hat, wie folgt freigegeben werden:

%Vor%

Und fügen Sie es vor dem Rendern wieder in den anderen Thread ein:

%Vor%

EDIT3: Wenn die Rückrufe des Codierers und des Decodierers auf demselben Thread empfangen werden, kann außerdem der Decodierer onOutputBufferAvailable , der das Rendern durchführt, verhindern, dass die Codierer-Rückrufe geliefert werden. Wenn sie nicht geliefert werden, kann das Rendering unendlich blockiert werden, da der Encoder die zurückgegebenen Ausgabepuffer nicht erhält. Dies kann behoben werden, indem sichergestellt wird, dass die Videodecoder-Callbacks stattdessen in einem anderen Thread empfangen werden, und dies vermeidet stattdessen das Problem mit dem onFrameAvailable -Rückruf.

Ich habe versucht, all das oben auf ExtractDecodeEditEncodeMuxTest zu implementieren, und habe es anscheinend gut in Ordnung gebracht, schaut euch Ссылка . Ich habe zunächst den unveränderten Test importiert, die Konvertierung in den asynchronen Modus durchgeführt und die trickreichen Details separat korrigiert, um die einzelnen Fixes im Commit-Protokoll zu betrachten.

    
mstorsjo 09.03.2016 07:28
quelle
1

Kann den Handler auch im MediaEncoder einstellen.

--- & gt; AudioEncoderCallback (aacSamplePreFrameSize), mHandler);

MyAudioCodecWrapper myMediaCodecWrapper;

%Vor%     
user8270308 09.01.2018 03:58
quelle