Abrufen und Wechseln von WebVTT-Untertiteln in ExoPlayer

Hallo alle zusammen.

Vor ein paar Tagen gab es eine Aufgabe, WebVTT-Untertitel im HLS-Stream zu wechseln.
Wir spielen das Video mit ExoPlayer ab, und zunächst schien es, als müssten Google und Co. eine Lösung aus dem Feld "Take and Doed" bereitstellen. Aber die Realität stimmte nicht mit der Erwartung überein :)

Googeln und Habring führten nicht zu einem Ergebnis und es kam darauf an, die offizielle Demo-Anwendung ExoPlayer auszuwählen.

weil Da der Artikel eine gewisse Vertrautheit mit der Struktur von HLS und das Vorhandensein einiger Erfahrungen in ExoPlayer voraussetzt, kommen wir gleich zur Sache.

Dies sagt uns die Dokumentation zum Wechseln von Streams (Video, Audio, Untertitel). Und sie sagt uns fast nichts - initialisiere den Player mit DefaultTrackSelector und benutze ihn. Das war's, ok :)

Alle Untertitel jeglicher Art (CEA-608, WebVtt) wie alle anderen Tracks werden in DefaultTrackSelector gespeichert und müssen in der Lage sein, darauf zuzugreifen. Alles ist in Gruppen und Untergruppen unterteilt, und kurz gesagt, die interne Struktur sieht ungefähr so ​​aus:


Versuchen wir nun, Untertitel vom Typ WebVTT abzurufen. Sie sollten im Renderer mit dem Typ "3" (C.TRACK_TYPE_TEXT - Konstante in Exo) gespeichert werden:

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
    }

Wie Sie sehen können, werden einige bequeme Möglichkeiten zum Iterieren und Suchen nicht bereitgestellt, und Sie müssen alle Schleifen mit Stiften ausführen.

Wir müssen jedoch nicht nur über die Liste selbst Bescheid wissen, sondern auch über die ausgewählten Optionen (die wir in Zukunft auswählen werden). Aus irgendeinem Grund können Sie die ausgewählten Tracks nicht von DefaultTrackSelector abrufen, aber Sie können ExoPlayer selbst verwenden. Das Schema für das Erhalten ist ungefähr das gleiche, wir gehen tiefer, aber hier überspringen wir die Passage durch die Renderer:

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
    }

Nun, wir haben eine gemeinsame Liste und eine Liste ausgewählter. Das Kombinieren und Anzeigen auf der Benutzeroberfläche ist nicht der Zweck dieses Artikels. Es gibt viele Wege. Wir müssen nur lernen, wie man die Spur setzt.

Aus Gründen der Klarheit werden wir dies auf der Stirn tun, so dass alle Zyklen und Indizes offensichtlich sind.

Installation eines Tracks durch seinen Sprachcode ("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
                            }
                        }
                    }
                }
            }
        }
    }

Fazit


Wir haben die Grundlagen zum Abrufen und Wechseln von WebVTT-Untertiteln in ExoPlayer behandelt.
In der Realität kann dieselbe Methode problemlos mit anderen Untertiteltypen verwendet werden. Es reicht aus, die Methoden mit dem Typ zu parametrisieren. Ebenso wird es nicht schwierig sein, mit Audiospuren zu arbeiten. Natürlich ist es ziemlich schwierig, die Lösung in dieser Form zu verwenden, und es ist erforderlich, sie in eine bequemere Form zu bringen.

Vielen Dank für Ihre Aufmerksamkeit.

All Articles