Vista texture

La classe TextureView è un oggetto vista che combina una vista con un SurfaceTexture.

Rendering con OpenGL ES

Un oggetto TextureView avvolge un SurfaceTexture, rispondendo ai callback e acquisendo nuovi buffer. Quando un TextureView acquisisce nuovi buffer, un TextureView emette una richiesta di invalidazione della vista e disegna utilizzando il contenuto del buffer più recente come origine dati, eseguendo il rendering dove e come lo stato di visualizzazione indica che dovrebbe.

OpenGL ES (GLES) può eseguire il rendering su TextureView passando SurfaceTexture alla chiamata di creazione EGL, ma questo crea un problema. Quando GLES esegue il rendering su TextureView, i produttori e i consumatori di BufferQueue si trovano nello stesso thread, il che può causare lo stallo o il fallimento della chiamata di scambio del buffer. Ad esempio, se un produttore invia diversi buffer in rapida successione dal thread dell'interfaccia utente, la chiamata di scambio buffer EGL deve rimuovere dalla coda un buffer da BufferQueue. Tuttavia, poiché il consumatore e il produttore si trovano sullo stesso thread, non ci saranno buffer disponibili e la chiamata di scambio si blocca o fallisce.

Per garantire che lo scambio del buffer non si blocchi, BufferQueue necessita sempre di un buffer disponibile per la rimozione dalla coda. Per implementare ciò, BufferQueue elimina il contenuto del buffer precedentemente acquisito quando un nuovo buffer viene accodato e pone restrizioni sui conteggi minimo e massimo dei buffer per impedire a un consumatore di consumare tutti i buffer contemporaneamente.

Scegliendo SurfaceView o TextureView

SurfaceView e TextureView ricoprono ruoli simili e sono entrambi cittadini della gerarchia della vista. Tuttavia, SurfaceView e TextureView hanno implementazioni diverse. Un SurfaceView accetta gli stessi parametri delle altre visualizzazioni, ma i contenuti di SurfaceView sono trasparenti quando vengono sottoposti a rendering.

Un TextureView ha una migliore gestione dell'alfa e della rotazione rispetto a un SurfaceView, ma un SurfaceView presenta vantaggi in termini di prestazioni durante la composizione di elementi dell'interfaccia utente sovrapposti ai video. Quando un client esegue il rendering con SurfaceView, SurfaceView fornisce al client un livello di composizione separato. SurfaceFlinger compone il livello separato come sovrapposizione hardware se supportato dal dispositivo. Quando un client esegue il rendering con TextureView, il toolkit dell'interfaccia utente compone il contenuto di TextureView nella gerarchia di visualizzazione con la GPU. Gli aggiornamenti al contenuto possono causare il ridisegno di altri elementi della vista, ad esempio, se le altre viste sono posizionate sopra un TextureView. Una volta completato il rendering della vista, SurfaceFlinger compone il livello dell'interfaccia utente dell'app e tutti gli altri livelli, in modo che ogni pixel visibile venga composto due volte.

Caso di studio: Play Video di Grafika

Play Video di Grafika include una coppia di lettori video, uno implementato con TextureView e uno implementato con SurfaceView. La parte di decodifica video dell'attività invia fotogrammi da MediaCodec a una superficie sia per TextureView che per SurfaceView. La più grande differenza tra le implementazioni sono i passaggi necessari per presentare le proporzioni corrette.

Il ridimensionamento di SurfaceView richiede un'implementazione personalizzata di FrameLayout. WindowManager deve inviare una nuova posizione della finestra e nuovi valori di dimensione a SurfaceFlinger. Il ridimensionamento di SurfaceTexture di TextureView richiede la configurazione di una matrice di trasformazione con TextureView#setTransform() .

Dopo aver presentato le proporzioni corrette, entrambe le implementazioni seguono lo stesso schema. Quando SurfaceView/TextureView crea la superficie, il codice dell'app abilita la riproduzione. Quando un utente tocca play , avvia un thread di decodifica video, con la superficie come destinazione di output. Successivamente, il codice dell'app non esegue alcuna operazione: la composizione e la visualizzazione vengono gestite da SurfaceFlinger (per SurfaceView) o da TextureView.

Caso di studio: doppia decodifica di Grafika

Double Decode di Grafika dimostra la manipolazione di SurfaceTexture all'interno di TextureView.

Double Decode di Grafika utilizza una coppia di oggetti TextureView per mostrare due video riprodotti fianco a fianco, simulando un'app di videoconferenza. Quando l'orientamento dello schermo cambia e l'attività riprende, i decoder MediaCodec non si fermano, simulando la riproduzione di un flusso video in tempo reale. Per migliorare l'efficienza, il cliente dovrebbe mantenere viva la superficie. La superficie è un handle per l'interfaccia del produttore nella BufferQueue di SurfaceTexture. Poiché TextureView gestisce SurfaceTexture, il client deve mantenere attiva SurfaceTexture per mantenere attiva la superficie.

Per mantenere viva la SurfaceTexture, Double Decode di Grafika ottiene i riferimenti alle SurfaceTexture dagli oggetti TextureView e li salva in un campo statico. Quindi, Double Decode di Grafika restituisce false da TextureView.SurfaceTextureListener#onSurfaceTextureDestroyed() per impedire la distruzione di SurfaceTexture. TextureView passa quindi un SurfaceTexture a onSurfaceTextureDestroyed() che può essere mantenuto durante la modifica della configurazione dell'attività, che il client passa al nuovo TextureView tramite setSurfaceTexture() .

Thread separati guidano ciascun decodificatore video. Mediaserver invia buffer con output decodificato ai SurfaceTextures, i consumatori BufferQueue. Gli oggetti TextureView eseguono il rendering e vengono eseguiti sul thread dell'interfaccia utente.

L'implementazione della Double Decode di Grafika con SurfaceView è più difficile dell'implementazione con TextureView perché gli oggetti SurfaceView distruggono le superfici durante i cambiamenti di orientamento. Inoltre, l'utilizzo degli oggetti SurfaceView aggiunge due livelli, il che non è l'ideale a causa delle limitazioni sul numero di sovrapposizioni disponibili sull'hardware.