2016-05-11 9 views
2

Ich versuche, Effekte auf die Frames eines Videos mit der GPU anwenden und dann diese Frames in ein neues Ergebnis Video neu zu kodieren.MediaCodec gleichzeitige Codierung und Decodierung

Im Interesse der Leistung, die ich die folgende Strömung umgesetzt habe:

Es gibt 3 verschiedene Themen, die jeweils mit einem eigenen OpenGL-Kontext. Diese Kontexte sind so eingerichtet, dass sie Texturen untereinander teilen.

Thread 1 Extrakte Bilder vom Video und halten sie in den GPU-Speichern als Texturen, ähnlich wie this Beispiel.

Thread 2 verarbeitet die Texturen mit einer modifizierten Version von GPUImage, die auch Texturen im GPU-Speicher ausgibt.

Schließlich Gewinde 3 schreibt die vom Faden erhalten Texturen 2 in eine neue Videodatei ähnlich dem Verfahren, beschrieben here

Rahmenreihenfolge beibehalten wird Warteschlangen zwischen Threads 1 und 2 verwendet wird, und die Fäden 2 und 3. Texturen werden manuell aus dem Speicher gelöscht nach werden sie für die Verarbeitung/Schreiben verwendet.

Der ganze Sinn dieses Ablaufs besteht darin, jeden Prozess in der Hoffnung zu trennen, dass die endgültige Leistung die langsamste der drei Threads sein wird.

DAS PROBLEM:

Das letzte Video ist zu 90% schwarzer Rahmen, nur einige von ihnen richtig zu sein.

Ich habe die einzelnen Ergebnisse der Extraktion und Verarbeitung überprüft und sie funktionieren wie erwartet. Beachten Sie auch, dass die 3 Komponenten, die in den 3 Threads beschrieben werden, in einem einzigen Thread gut zusammenpassen.

Ich habe versucht, Thread 1 und Thread 3 zu synchronisieren, und nach dem Hinzufügen einer zusätzlichen 100ms Schlafzeit zu Thread 1 das Video funktioniert gut, mit vielleicht 1 oder 2 schwarzen Rahmen. Scheint mir so, als ob die zwei Instanzen von Decoder und Encoder nicht gleichzeitig arbeiten könnten.

Ich werde diesen Beitrag mit zusätzlichen Details bearbeiten.

Antwort

2

Das Teilen von Texturen zwischen OpenGL ES-Kontexten erfordert einige Sorgfalt. Die Art und Weise, wie es in Grafika's implementiert wird "Show + Capture-Kamera" Aktivität ist kaputt; Details siehe this issue. Das grundlegende Problem ist, dass Sie im Wesentlichen Speicherbarrieren ausstellen müssen, wenn die Textur aktualisiert wird; In der Praxis bedeutet das, dass auf der Herstellerseite glFinish() ausgegeben wird, und die Textur auf der Verbraucherseite neu gebunden wird, und dies alles in synchronized Blöcken zu tun.

Ihr Leben wird einfacher (und effizienter), wenn Sie alle GLES-Arbeiten an einem einzigen Thread erledigen können. Meiner Erfahrung nach ist es unklug, mehrere GLES-Kontexte gleichzeitig aktiv zu haben, und Sie ersparen sich dadurch etwas Mühe, indem Sie eine Alternative finden.

Sie wollen wahrscheinlich etwas mehr wie folgt aus:

  • Thread # 1 liest die Datei und führt Frames in einen MediaCodec Decoder. Der Decoder sendet die Ausgabe an eine SurfaceTexture Surface.
  • Thread # 2 hat den Kontext GLES. Es hat die SurfaceTexture erstellt, an die Thread 1 die Ausgabe sendet. Es verarbeitet die Bilder und rendert die Ausgabe auf der Oberfläche eines MediaCodec-Encoders.
  • Thread # 3, der den MediaCodec-Encoder erstellt hat, wartet auf den codierten Ausgang. Wenn die Ausgabe empfangen wird, wird sie auf die Festplatte geschrieben. Beachten Sie, dass die Verwendung von MediaMuxer anhalten kann; siehe this blog post für mehr.

In allen Fällen wird die einzige Kommunikation zwischen Threads (und unter der Haube, Prozesse) durch Surface durchgeführt. Die SurfaceTexture- und MediaCodec-Instanzen werden aus einem einzigen Thread erstellt und verwendet. nur der Producer-Endpunkt (die Surface) wird herumgereicht.

Ein potenzieller Problempunkt ist die Flusskontrolle - SurfaceTextures werden Frames löschen, wenn Sie sie zu schnell füttern. Die Kombination der Threads # 1 und # 2 kann je nach den Umständen sinnvoll sein.

+0

Hallo @fadden und danke für Ihre Antwort. Meine ursprüngliche Lösung würde einen einzigen Thread verwenden und gut funktionieren, war aber nicht schnell genug. Ich bin mir nicht sicher, ob ich die Bedeutung der SurfaceTexture verstehe. Zu diesem Zeitpunkt kopiere ich die resultierenden Decoder-Texturen in eine neue Textur, die ich mit GLES20.glGenTextures erstelle, und behalte sie, damit ich sie später benutzen kann. Auch, wie genau soll ich einen Kontext über drei Threads verwenden? Decodierung, Verarbeitung und Codierung erfordern einen aktuellen OpenGL-Kontext. – Rakatan

+0

SurfaceTexture nimmt alles, was auf einer Oberfläche gezeichnet ist, und wickelt es in eine GLES- "externe" Textur. Das ist es, was Ihr verlinktes Beispiel (ExtractMpegFramesTest) verwendet. Was war der Engpass bei der Verwendung eines einzelnen Threads? Was ist der langsamste Teil Ihrer Pipeline? Sie sollten nur GLES aus Thread # 2 im obigen Schema verwenden. – fadden

+0

Der Frame-Extraktionsabschnitt scheint der langsamste zu sein, deshalb dachte ich, dass ich ihm mehr Zeit geben könnte, während die Verarbeitung durchgeführt wird. Sie haben recht mit dem verlinkten Beispiel, aber um die Zeit zu sparen, kopiere ich die extrahierte Textur in eine neue von mir verwaltete Textur, um sie schneller in Phase 2 zu überführen. – Rakatan