Obtenez et changez les sous-titres WebVTT dans ExoPlayer

Bonjour à tous.

Il y a quelques jours, il y avait une tâche pour changer les sous-titres WebVTT dans le flux HLS.
Nous lisons la vidéo à l'aide d'ExoPlayer, et au début, il semblait que Google et Cie devraient fournir une solution à partir de la boîte «prise et terminée». Mais la réalité n'a pas coïncidé avec l'attente :)

Google et Habring n'ont pas abouti à un résultat et tout s'est résolu à choisir l' application de démonstration officielle ExoPlayer.

Parce que Étant donné que l'article suppose une certaine familiarité avec la structure de HLS et la présence d'une certaine expérience dans ExoPlayer, nous allons droit au but.

C'est ce que la documentation sur le changement de flux (vidéo, audio, sous-titres) nous dit . Et elle ne nous dit presque rien - initialisez le lecteur à l'aide de DefaultTrackSelector et utilisez-le. Ça y est, ok :)

Tous les sous-titres de toute nature (CEA-608, WebVtt) comme toutes les autres pistes sont stockés dans DefaultTrackSelector et vous devez pouvoir y accéder. Tout est divisé en groupes et sous-groupes, et en bref, la structure interne ressemble à ceci:


Essayons maintenant d'obtenir des sous-titres de type WebVTT, ils doivent être stockés dans Renderer avec le type "3" (C.TRACK_TYPE_TEXT - constant dans 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
    }

Comme vous pouvez le voir, certains moyens pratiques d'itérer et de rechercher ne sont pas fournis et vous devez effectuer toutes les boucles avec des stylos.

Mais nous devons connaître non seulement la liste elle-même, mais aussi les options sélectionnées (que nous choisirons à l'avenir). Pour une raison quelconque, vous ne pouvez pas obtenir les pistes sélectionnées à partir de DefaultTrackSelector, mais vous pouvez les obtenir à partir d'ExoPlayer lui-même. Le schéma d'obtention est à peu près le même, nous allons plus loin, mais ici nous sautons le passage à travers les moteurs de rendu:

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
    }

Eh bien, nous avons une liste commune et une liste de celles sélectionnées. Comment combiner et afficher sur l'interface utilisateur n'est pas le but de cet article. Il y a plusieurs façons. Nous devons juste apprendre à définir la piste.

Pour plus de clarté, nous allons le faire de manière frontale, afin que tous les cycles et indices soient évidents.

Installation d'une piste par son code de langue ("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
                            }
                        }
                    }
                }
            }
        }
    }

Conclusion


Nous avons couvert les bases de l'obtention et du changement de sous-titres WebVTT dans ExoPlayer.
En réalité, la même méthode peut facilement fonctionner avec d'autres types de sous-titres - il suffit de paramétrer les méthodes avec le type. De la même manière, il ne sera pas difficile de travailler avec des pistes audio. Bien sûr, il est assez difficile d'utiliser la solution sous cette forme et il est nécessaire de l'amener sous une forme plus pratique.

Merci pour l'attention.

All Articles