Obter e alternar legendas WebVTT no ExoPlayer

Olá a todos.

Alguns dias atrás, havia uma tarefa de alternar legendas do WebVTT no fluxo HLS.
Reproduzimos o vídeo usando o ExoPlayer e, a princípio, parecia que o Google e a empresa precisariam fornecer uma solução da caixa "tirado e pronto". Mas a realidade não coincidiu com a expectativa :)

Googling e Habring não levaram a um resultado e tudo se resumiu a escolher o aplicativo de demonstração oficial ExoPlayer.

Porque Como o artigo assume alguma familiaridade com a estrutura do HLS e a presença de alguma experiência no ExoPlayer, vamos direto ao ponto.

É o que a documentação sobre a troca de fluxos (vídeo, áudio, legendas) nos diz . E ela não nos diz quase nada - inicialize o player usando DefaultTrackSelector e use-o. É isso aí, ok :)

Todas as legendas de qualquer tipo (CEA-608, WebVtt), como qualquer outra faixa, são armazenadas no DefaultTrackSelector e você precisa acessá-las. Tudo é dividido em grupos e subgrupos e, em suma, a estrutura interna se parece com isso:


Agora vamos tentar obter legendas do tipo WebVTT, elas devem ser armazenadas no Renderer com o tipo “3” (C.TRACK_TYPE_TEXT - constante no Exo):

fun getVttSubtitles(): List<String> {
        val tracks = mutableListOf<String>()
        //  MappedTrackInfo
        defaultTrackSelector.currentMappedTrackInfo?.let { mappedTrackInfo ->
            val renderCount = mappedTrackInfo.rendererCount
            for (renderIndex in 0 until renderCount) {
                //    renderer'    TEXT
                val renderType = mappedTrackInfo.getRendererType(renderIndex)
                if (renderType == C.TRACK_TYPE_TEXT) {
                    val trackGroupArray = mappedTrackInfo.getTrackGroups(renderIndex)
                    //    
                    for (trackGroupArrayIndex in 0 until trackGroupArray.length) {
                        val trackGroup = trackGroupArray[trackGroupArrayIndex]
                        for (trackGroupIndex in 0 until trackGroup.length) {
                            //       TEXT_VTT
                            val format = trackGroup.getFormat(trackGroupIndex)
                            if (format.sampleMimeType == MimeTypes.TEXT_VTT) {
                                tracks += format.language.orEmpty()
                            }
                        }
                    }
                }
            }
        }

        return tracks
    }

Como você pode ver, algumas maneiras convenientes de iterar e pesquisar não são fornecidas e você deve fazer todos os loops com canetas.

Mas precisamos saber não apenas sobre a lista em si, mas também sobre as opções selecionadas (que escolheremos no futuro). Por algum motivo, não é possível obter as faixas selecionadas no DefaultTrackSelector, mas no ExoPlayer. O esquema para obter é aproximadamente o mesmo, vamos mais fundo, mas aqui pulamos a passagem pelos renderizadores:

fun getSelectedVttSubtitles(): List<String> {
        val selectedLangs = mutableListOf<String>()
        val currentTrackSelections = exoPlayer.currentTrackSelections
        for (selectionIndex in 0 until currentTrackSelections.length) {
            val trackSelection = currentTrackSelections[selectionIndex]
            if (trackSelection != null) {
                //     
                val length = trackSelection.length()
                for (trackIndex in 0 until length) {
                    //      
                    val format = trackSelection.getFormat(trackIndex)
                    if (format.sampleMimeType == MimeTypes.TEXT_VTT) {
                        selectedLangs += format.language.orEmpty()
                    }
                }
            }
        }

        return selectedLangs
    }

Bem, temos uma lista comum e uma lista dos selecionados. Como combinar e exibir na interface do usuário não é o objetivo deste artigo. Existem muitos caminhos. Nós apenas temos que aprender como definir a trilha.

Para maior clareza, faremos isso de maneira frontal, para que todos os ciclos e índices sejam óbvios.

Instalação de uma faixa pelo seu código de idioma ("ru", "en", ...):

fun selectTrackByIsoCodeAndType(langCode: String) {
        defaultTrackSelector.currentMappedTrackInfo?.let { mappedTrackInfo ->
            //         
            val renderCount = mappedTrackInfo.rendererCount
            for (renderIndex in 0 until renderCount) {
                //    renderer'    TEXT
                val renderType = mappedTrackInfo.getRendererType(renderIndex)
                if (renderType == C.TRACK_TYPE_TEXT) {
                    val trackGroupArray = mappedTrackInfo.getTrackGroups(renderIndex)
                    //    
                    for (trackGroupArrayIndex in 0 until trackGroupArray.length) {
                        val trackGroup = trackGroupArray[trackGroupArrayIndex]
                        for (trackGroupIndex in 0 until trackGroup.length) {
                            val format = trackGroup.getFormat(trackGroupIndex)
                            //       
                            if (format.sampleMimeType == MimeTypes.TEXT_VTT
                                && format.language == langCode
                            ) {
                                //    
                                val currentParams = defaultTrackSelector.buildUponParameters()
                                //   ( )
                                //  
                                currentParams.clearSelectionOverride(
                                    renderIndex, trackGroupArray
                                )

                                // ,    
                                //       

                                //       
                                val override = DefaultTrackSelector.SelectionOverride(
                                    trackGroupArrayIndex, trackGroupIndex
                                )

                                //     
                                currentParams.setSelectionOverride(
                                    renderIndex,
                                    trackGroupArray,
                                    override
                                )

                                //     
                                defaultTrackSelector.setParameters(currentParams)

                                return
                            }
                        }
                    }
                }
            }
        }
    }

Conclusão


Abordamos o básico de como obter e alternar legendas do WebVTT no ExoPlayer.
Na realidade, o mesmo método pode trabalhar facilmente com outros tipos de legendas - basta parametrizar os métodos com o tipo. Da mesma forma, não será difícil trabalhar com faixas de áudio. Obviamente, é bastante difícil usar a solução nesta forma e é necessário trazê-la para uma forma mais conveniente.

Obrigado pela atenção.

All Articles