La classe TextureView è un oggetto View che combina una vista con una SurfaceTexture.
Rendering con OpenGL ES
Un oggetto TextureView racchiude una SurfaceTexture, risponde ai callback e acquisisce nuovi buffer. Quando TextureView acquisisce nuovi buffer, emette una richiesta di invalidazione della visualizzazione e disegna utilizzando i contenuti del buffer più recente come origine dati, eseguendo il rendering dove e come indicato dallo stato della visualizzazione.
OpenGL ES (GLES) può eseguire il rendering su una TextureView passando la SurfaceTexture alla chiamata di creazione EGL, ma questo crea un problema. Quando GLES eseguire il rendering su una TextureView, i produttori e i consumatori di BufferQueue si trovano nello stesso thread, il che può causare l'interruzione 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 un buffer dalla coda BufferQueue. Tuttavia, poiché il consumer e il producer si trovano nello stesso thread, non saranno disponibili buffer e la chiamata di scambio si bloccherà o non andrà a buon fine.
Per assicurarsi che lo scambio del buffer non si blocchi, BufferQueue ha sempre bisogno di un buffer disponibile per essere dequeued. Per implementare questa funzionalità, BufferQueue ignora i contenuti del buffer acquisito in precedenza quando viene messo in coda un nuovo buffer e applica limitazioni al numero minimo e massimo di buffer per impedire a un consumatore di consumare tutti i buffer contemporaneamente.
Scegliere SurfaceView o TextureView
SurfaceView e TextureView svolgono ruoli simili e fanno entrambi parte della gerarchia delle visualizzazioni. Tuttavia, SurfaceView e TextureView hanno implementazioni diverse. Una visualizzazione SurfaceView accetta gli stessi parametri delle altre visualizzazioni, ma i contenuti di SurfaceView sono trasparenti quando vengono visualizzati.
TextureView gestisce meglio l'alfa e la rotazione rispetto a SurfaceView, ma quest'ultimo offre vantaggi in termini di prestazioni durante la composizione degli elementi dell'interfaccia utente sovrapposti ai video. Quando un client esegue il rendering con un SurfaceView, quest'ultimo fornisce al client un livello di composizione separato. SurfaceFlinger compone il livello distinto come overlay hardware, se supportato dal dispositivo. Quando un client esegue il rendering con una TextureView, il toolkit UI compone i contenuti della TextureView nella gerarchia delle visualizzazioni con la GPU. Gli aggiornamenti dei contenuti possono causare il ricalcolo di altri elementi della visualizzazione, ad esempio se le altre visualizzazioni sono posizionate sopra una TextureView. Al termine del rendering della visualizzazione, SurfaceFlinger compone il livello UI dell'app e tutti gli altri livelli, in modo che ogni pixel visibile venga composto due volte.
Case study: video di Let's Play di Grafika
Play Video di Grafika include una coppia di video player, uno implementato con TextureView e uno con SurfaceView. La parte di decodifica video dell'attività invia frame da MediaCodec a una superficie sia per TextureView sia per SurfaceView. La differenza più grande tra le implementazioni è costituita dai passaggi necessari per presentare le proporzioni corrette.
La scalabilità di SurfaceView richiede un'implementazione personalizzata di FrameLayout.
WindowManager deve inviare una nuova posizione della finestra e nuovi valori delle dimensioni a
SurfaceFlinger. La scalabilità della SurfaceTexture di un 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 attiva la riproduzione. Quando un utente tocca Riproduci, viene avviato un thread di decodifica video con la superficie come destinazione di output. Dopodiché, il codice dell'app non fa nulla: la composizione e la visualizzazione sono gestite da SurfaceFlinger (per SurfaceView) o da TextureView.
Case study: decodifica doppia di Grafika
La funzionalità Double Decode di Grafika mostra la manipolazione di SurfaceTexture all'interno di una TextureView.
La funzionalità di decodifica doppia di Grafika utilizza una coppia di oggetti TextureView per mostrare due video affiancati, simulando un'app di videoconferenza. Quando l'orientamento dello schermo cambia e l'attività si riavvia, i decodificatori MediaCodec non si fermano, simulando la riproduzione di uno stream video in tempo reale. Per migliorare l'efficienza, il cliente deve mantenere attiva la piattaforma. La superficie è un handle all'interfaccia del produttore nella BufferQueue di SurfaceTexture. Poiché TextureView gestisce la SurfaceTexture, il client deve mantenere attiva la SurfaceTexture per mantenere attiva la superficie.
Per mantenere attivo SurfaceTexture, la decodifica doppia di Grafika ottiene i riferimenti ai SurfaceTexture dagli oggetti TextureView e li salva in un campo statico.
Quindi, la decodifica doppia di Grafika restituisce false
da
TextureView.SurfaceTextureListener#onSurfaceTextureDestroyed()
per
evitare 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()
.
Ogni decodificatore video è gestito da thread separati. Mediaserver invia i buffer con output decodificato ai SurfaceTexture, i consumatori di BufferQueue. Gli oggetti TextureView eseguono il rendering ed eseguono il thread dell'interfaccia utente.
L'implementazione del doppio decodifica di Grafika con SurfaceView è più complessa rispetto all'implementazione con TextureView perché gli oggetti SurfaceView distruggono le superfici durante le modifiche dell'orientamento. Inoltre, l'utilizzo di oggetti SurfaceView aggiunge due livelli, il che non è l'ideale a causa delle limitazioni del numero di overlay disponibili sull'hardware.