MediaCodec mit dem Surface Input: Aufnahme im hintergrund
Arbeite ich an einem video-encoding-Anwendung, die ich will, um zu verhindern, dass aus beenden, wenn die hosting-Aktivität tritt in den hintergrund, oder der Bildschirm Zyklen ein - /ausschalten.
Die Architektur der mein encoder stammt von der ausgezeichneten CameraToMpegTest Beispiel, mit dem Zusatz, der die Anzeige von frames zu einem GLSurfaceView (siehe Github-links unten). Ich bin derzeit die Aufzeichnung im hintergrund mit einer zwei-Staaten-Lösung:
-
Wenn der hosting-Tätigkeit ist im Vordergrund, codieren Sie einen video-frame, das bei jedem Aufruf der
GLSurfaceView.Renderer
'sonDrawFrame
. Dies ermöglicht mir den Zugriff auf die GLSurfaceView ist die EGL Staat in bursts so nicht zu blockieren, andere events in der Warteschlange des renderer-thread. -
Wenn der hosting-Tätigkeit tritt in den hintergrund, halt die
onDrawFrame
Codierung und Encodierung von frames auf einer anderen hintergrund-thread in einer Schleife. Dieser Modus ist identisch mit dem CameraToMpegTest Beispiel.
Jedoch, wenn der Bildschirm ausgeschaltet ist, die GLSurfaceView ist EGLContext verloren und einen neuen Anruf zu onSurfaceCreated
Auftritt. In diesem Fall haben wir neu erstellen, die EGL-Fenster-Oberfläche verbunden mit MediaCodec input-Oberfläche. Leider ist diese 2. Aufruf eglCreateWindowSurface
produziert:
E/libEGL(18839): EGLNativeWindowType 0x7a931098 already connected to another API
Vor anrufen, ich lassen Sie alle EGL-Ressourcen, verbunden mit der Android-Oberfläche.
Gibt es eine Möglichkeit, tauschen Sie die EGLSurface verbunden mit MediaCodec input-Oberfläche?
Den vollständigen source von meinem test-Anwendung ist auf Github. Haupttätigkeit.
Update ich wandte die Lektionen, die hier gelernt in einem video-sdk für Android basierend auf den MediaCodec & MediaMuxer Klassen. Hoffe, es hilft!
MediaCodec
sollte nicht betroffen sein (oder sogar bewusst) seins in den hintergrund. Siehe z.B. diescreenrecord
Befehl Hinzugefügt, die in Android 4.4, die gerne läuft hinter den kulissen. Die Tatsache, dass es Codierung nichts bedeutet, es ist immer noch die input-Daten, somy Vermutung wäre, dass etwas ist, dieCamera
. Ich verstehe nicht, warum es verursachen würde, die VorschauSurface
leer zu sein, während die Vorschaubyte[]
hat echten Daten.- Aktualisiert meine Frage. Ich bin jetzt in der Lage, den übergang zum hintergrund der Aufnahme (keine GLSurfaceView display) und dann Vorder-Aufnahme (GLSurfaceView display) außer, wenn ein Bildschirm aus/ein Ereignis tritt in der Zwischenzeit...
- Ihnen fehlt ein
glSurfaceView.onPause()
in Ihrer Haupttätigkeit onPause(). Nicht sicher, ob das gleich, obwohl. Vielleicht kann ich mit ihm spielen ein bisschen, und morgen sehen, ob ich replizieren kann, das Verhalten. - Die Aktivität
onPause()
eine Methode aufruft, verwendetglSurfaceView.queueEvent()
Warteschlange eine operation erfolgen muss, der auf den renderer thread, bevorglSurfaceView.onPause()
hält es. Siehe hier - Bildschirm off/on bewirkt, dass die GLSurfaceView ist EGLContext verloren, wir haben also eine neue zu erstellen CodecInputSurface.mEGLSurface, dass die Aktien des neuen EGLContext. Jedoch Probleme beim trennen der MediaCodec input Oberfläche zu vermeiden
already connected to another API
Fehler aufeglCreateWindowSurface
. Sollte dies möglich sein? - Schnell dachte: developer.android.com/reference/android/opengl/...
- Ich habe
setPreserveEGLContextOnPause
settrue
. Funktioniert, bis der Bildschirm ausgeschaltet Konfigurationsänderung bewirkt, dass onDestroy(). Freigeben wollen, diese als Bibliothek, so dass nicht wollen, verlangen von Entwicklern Griff Konfigurationsänderungen manuell. Im schlimmsten Fall kann ich den swap in eine neue MediaCodec und update der Oberfläche. - Blick auf
~egl_surface_t
imframeworks/native/opengl/libs/EGL/egl_object.cpp
, der früheren EGL Oberfläche trennen soll aus dem MediaCodec Eingabe-Oberfläche, wenn es zerstört wird (eglDestroySurface + eglMakeCurrent(nichts)). Eine Sache, die Sie tun könnten, ist, prüftCodecInputSurface
Methoden... rufeneglGetCurrentContext
und sehen, ob es.equal()
zumEGLEncodeContext
um zu bestätigen, dass der richtige Kontext ist eine aktuelle, wenn Sie zerstören+eglMakeCurrent (nicht das "=="). Die Oberfläche wird nicht zerstört werden, wenn es noch aktuell in einem verwaisten Kontext. - Was sollte ich gesagt habe, ist: stellen Sie sicher, dass Sie in den richtigen thread, wenn Sie anrufen
eglMakeCurrent(nothing)
. Wenn der Kontext " +thread ist der aktuelle thread #1, dann rufteglMakeCurrent(nothing)
im thread #2 nicht loslassen den Faden und Fläche. Wenn der thread den aktuellen Kontext entspricht, was Sie in das Objekt, dann wissen Sie, Sie sind an der richtigen Stelle. (Es sei denn, Sie haben die gleichen aktuellen Kontext in zwei verschiedenen threads, in dem Fall wirst du in eine Welt von weh.) - FWIW, ich habe etwas ähnliches arbeiten über power-Zyklus auf einem Nexus 5. Mein encoder-thread bekommt eine Nachricht mit der neuen GLSurfaceView EGLSurface, und fordert den Gegenwert von releaseAllButSurface() + eglSetup() + SurfaceTextureManager.surfaceCreated(). Meine app geht nur um zu schlafen, während die Stromversorgung ausgeschaltet ist, so dass Sie am Ende mit einer langen pause in der Aufnahme, aber es wählt rechts, wenn die app fortgesetzt wird.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Hintergrund erste...
Beim Aufruf
eglCreateWindowSurface()
, die Android EGL-wrapper Anrufenative_window_api_connect()
auf dieSurface
Sie übergeben. Diese schließlich verwandelt sich in eineBufferQueue
Produzent Aufruf verbinden, was bedeutet, dass diese EGL Oberfläche ist nun die einzige Quelle der Grafiken Puffer für dieSurface
.Die EGL Oberfläche immer verbunden bleibt
Surface
bis die EGL Oberfläche ist zerstört. Wenn es ist, die Oberfläche Destruktor Anrufenative_window_api_disconnect()
trennen der EGL Oberfläche von derBufferQueue
. Die EGL Oberfläche ist Referenz-gezählt, mit der refcount inkrementiert, wenn die Oberfläche geleitet wird, umeglMakeCurrent()
so zerstört werden, müssen zwei Dinge passieren:eglDestroySurface()
genannt werden mussDem zweiten Element erfordert den Aufruf
eglMakeCurrent()
mit anderen EGL-Oberfläche (oderEGL_NO_SURFACE
), oder rufen SieeglReleaseThread()
, auf jedem thread, der hatte bisher die Oberfläche. Eine schnelle Möglichkeit, um zu bestätigen, dass dies getan wird, um die Protokollierung, bevor Sie Anrufe zueglMakeCurrent()
wenn die Oberfläche ist aus Strom und nicht-Strom, und vergleichen Sie die thread-IDs von der Anzeige der logcat-Ausgabe mitadb logcat -v threadtime
. Es kann auch hilfreich sein, zu verwenden, EGL Abfragen wieeglGetCurrentSurface(EGL_DRAW)
zu bestätigen, dass Sie tun, die un-Stroms in dem thread gemacht hat, dass die aktuelle Oberfläche.Wenn die EGL Oberfläche ist nicht zerstört, es wird nicht trennen Sie es von der
Surface
, und versucht, eine Verbindung einem neuen Produzenten (durch AufrufeglCreateWindowSurface
mit einem neuen EGL Oberfläche) wird zurückgewiesen mit der "bereits verbunden" - Meldung.Update: Meine Umsetzung ist nun in der Grafika-test-Projekt. Wenn Sie diese installieren, wählen Sie "Show + Kamera", die Aufnahme zu starten, schalten Sie das macht, und dann die Aufzeichnung beenden, sollten Sie einen kompletten Film mit einer langen pause in der Mitte. Sie können zurück wählen Sie "Play video", und wählen Sie "Kamera-test.mp4", um es anzuzeigen.
eglDestroySurface()
heißt und, ist es nicht mehr aktuell ist. Es wird einen bug im Zusammenhang mit diesem code -- github.com/google/grafika/issues/24 - aber das ist mehr über das, was ich tun konnte inonResume()
.