2013-11-26 19 views
10

Sto lavorando a un'applicazione di codifica video che voglio impedire di arrestare quando l'attività di hosting entra nello sfondo, o lo schermo si attiva/disattiva.MediaCodec con Surface Input: registrazione in background

L'architettura del mio encoder è derivata dall'eccellente esempio CameraToMpegTest, con l'aggiunta della visualizzazione di fotogrammi della telecamera su un GLSurfaceView (vedere i collegamenti Github di seguito). Attualmente sto eseguendo sfondo la registrazione con una soluzione a due stati:

  • Quando l'hosting di attività è in primo piano, codificare un frame video su ogni chiamata a 's il GLSurfaceView.Renderer. Ciò mi consente di accedere allo stato EGL di GLSurfaceView a raffica in modo da non bloccare altri eventi in coda al thread di rendering.

  • Quando l'attività di hosting passa allo sfondo, interrompere la codifica e codificare fotogrammi su un altro thread in background all'interno di un ciclo. Questa modalità è identica all'esempio di CameraToMpegTest.

Tuttavia se lo schermo è spento EGLContext del GLSurfaceView è perduto e una nuova chiamata a onSurfaceCreated verifica. In questo caso dobbiamo ricreare la superficie della finestra EGL collegata alla superficie di input di MediaCodec. Purtroppo questo secondo invito a eglCreateWindowSurface produce:

E/libEGL(18839): EGLNativeWindowType 0x7a931098 already connected to another API 

prima di chiamare, io release all EGL resources connected to the Android Surface.

C'è un modo per scambiare la superficie EGLSurface collegata alla superficie di ingresso di MediaCodec?

La fonte completa della mia applicazione di prova è Github. Main Activity.

Aggiornamento ho applicato le lezioni apprese qui in una video sdk for Android basata sulle classi MediaMuxer MediaCodec &. Spero che sia d'aiuto!

+1

'MediaCodec' non dovrebbe essere influenzato (o nemmeno a conoscenza) di essere in background. Vedi per es. il comando 'screenrecord' aggiunto in Android 4.4, che corre felicemente dietro le quinte. Il fatto che stia codificando * qualsiasi cosa * significa che continua a ricevere dati di input, suppongo che qualcosa stia influenzando 'Camera'. Non capisco perché causerebbe l'anteprima 'Surface' da vuoto mentre l'anteprima' byte [] 'ha dati reali. – fadden

+0

Aggiornata la mia domanda. Ora sono in grado di passare alla registrazione in background (senza display GLSurfaceView) e quindi alla registrazione in primo piano (display GLSurfaceView) ** eccetto ** quando si verifica un evento di spegnimento/accensione nello schermo ... – dbro

+0

Manca un 'glSurfaceView .onPause() 'nella tua attività principale onPause(). Non sono sicuro se ciò avrà importanza. Potrei essere in grado di giocarci un po 'domani e vedere se riesco a replicare il comportamento. – fadden

risposta

9

fondo prima ...

Quando si chiama eglCreateWindowSurface(), i Android EGL wrapper chiamate native_window_api_connect() sul Surface avete passato. Questo alla fine si trasforma in un produttore BufferQueue collegare chiamata, il che significa che questa superficie EGL è ora l'unico fonte di buffer grafici per lo Surface.

La superficie EGL rimane collegata allo Surface finché la superficie EGL non viene distrutta. Quando lo è, il surface destructor chiama native_window_api_disconnect() per scollegare la superficie EGL dal BufferQueue.La superficie EGL è riferimento conteggiata, con il refcount incrementato quando la superficie viene passato eglMakeCurrent() modo da distruggere due cose deve accadere:

  1. eglDestroySurface() deve essere chiamato
  2. superficie EGL non deve essere "di" in ogni filo

il secondo elemento richiede chiamando eglMakeCurrent() con un'altra superficie EGL (o EGL_NO_SURFACE), o chiamando eglReleaseThread(), su ogni filo che aveva usato in precedenza la superficie. Un modo rapido per confermare l'operazione consiste nell'aggiungere la registrazione prima delle chiamate a eglMakeCurrent() quando la superficie viene resa corrente e non corrente e confrontare gli ID di filettatura visualizzando l'output del logcat con adb logcat -v threadtime. Potrebbe anche essere utile usare query EGL come eglGetCurrentSurface(EGL_DRAW) per confermare che stai facendo la non-corrente nel thread che ha reso la superficie corrente.

Se la superficie EGL non viene distrutta, non si disconnetterà dallo Surface e tenterà di connettere un nuovo produttore (chiamando eglCreateWindowSurface con una nuova superficie EGL) verrà rifiutata con il messaggio "già connesso".

Aggiornamento: La mia implementazione è ora disponibile nello Grafika test project. Se lo installi, seleziona "Mostra + cattura fotocamera", avvia la registrazione, attiva la potenza e quindi interrompe la registrazione, dovresti avere un film completo con una lunga pausa nel mezzo. Puoi tornare indietro, selezionare "Riproduci video" e scegliere "camera-test.mp4" per vederlo.

+1

Eureka! Grafika è bellissima. Vedere tutti i pezzi mobili opportunamente modulati rende il mio odore artigianale di pastelli e colla. – dbro

+0

@fadden In ContinuousCaptureActivity.java (https://github.com/google/grafika/blob/master/src/com/android/grafika/ContinuousCaptureActivity.java), quando onPause, si rilascia prima mDisplaySurface, quindi si rilascia mEglCore. MDisplaySurface.release() alla fine chiamerà EGL14.eglDestroySurface, ma in quel momento non hai confermato il problema "corrente". L'eglDestroySurface riuscirà? – dragonfly

+0

Non sono sicuro di cosa intendi con "non ha confermato il problema" attuale ". Come indicato nella risposta, le risorse sono conteggiate con riferimento; essendo corrente aggiunge un riferimento aggiuntivo. Quindi la superficie viene scartata quando 'eglDestroySurface()' viene chiamato * e * non è più attuale. C'è * un * bug relativo a questo codice - https://github.com/google/grafika/issues/24 - ma questo è più di ciò che non ho fatto in 'onResume()'. – fadden

Problemi correlati