Obtenga y cambie los subtítulos de WebVTT en ExoPlayer

Hola a todos.

Hace un par de días hubo una tarea para cambiar los subtítulos de WebVTT en la secuencia HLS.
Reproducimos el video usando ExoPlayer, y al principio parecía que Google y compañía deberían haber proporcionado una solución desde el cuadro "tomado y listo". Pero la realidad no coincidió con la expectativa :)

Buscar en Google y Habring no condujeron a un resultado y todo se redujo a elegir la aplicación de demostración oficial ExoPlayer.

Porque Dado que el artículo supone cierta familiaridad con la estructura de HLS y la presencia de cierta experiencia en ExoPlayer, llegamos directamente al punto.

Esto es lo que nos dice la documentación sobre el cambio de transmisiones (video, audio, subtítulos). Y ella no nos dice casi nada: inicialice el reproductor con DefaultTrackSelector y úselo. Eso es todo, ok :)

Todos los subtítulos de cualquier tipo (CEA-608, WebVtt) como cualquier otra pista se almacenan dentro de DefaultTrackSelector y debes poder acceder a ellos. Todo se divide en grupos y subgrupos, y en resumen, la estructura interna se parece a esto:


Ahora intentemos obtener subtítulos de tipo WebVTT, deben almacenarse dentro de Renderer con el tipo "3" (C.TRACK_TYPE_TEXT - constante en 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 puede ver, no se proporcionan algunas formas convenientes de iterar y buscar y tiene que hacer todos los bucles con bolígrafos.

Pero necesitamos saber no solo sobre la lista en sí, sino también sobre las opciones seleccionadas (que elegiremos en el futuro). Por alguna razón, no puede obtener las pistas seleccionadas de DefaultTrackSelector, pero puede obtenerlas del propio ExoPlayer. El esquema para obtener es aproximadamente el mismo, vamos más profundo, pero aquí omitimos el pasaje a través de los 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
    }

Bueno, tenemos una lista común y una lista de los seleccionados. Cómo combinar y mostrar en la interfaz de usuario no es el propósito de este artículo. Hay muchas maneras. Solo tenemos que aprender a configurar la pista.

Para mayor claridad, haremos esto de manera frontal, de modo que todos los ciclos e índices sean obvios.

Instalación de una pista por su 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
                            }
                        }
                    }
                }
            }
        }
    }

Conclusión


Hemos cubierto los conceptos básicos sobre cómo obtener y cambiar los subtítulos de WebVTT en ExoPlayer.
En realidad, el mismo método puede funcionar fácilmente con otros tipos de subtítulos: es suficiente para parametrizar los métodos con el tipo. Del mismo modo, no será difícil trabajar con pistas de audio. Por supuesto, es bastante difícil usar la solución de esta forma y es necesario llevarla a una forma más conveniente.

Gracias por la atención.

All Articles