在ExoPlayer中获取和切换WebVTT字幕

大家好。

几天前,有一项任务是在HLS流中切换WebVTT字幕。
我们使用ExoPlayer播放视频,起初看来Google和Co.应该已经在“完成并完成”框中提供了解决方案。但是现实并不符合预期:)

Googling和Habring并没有导致结果,而是归结为选择了官方演示应用程序 ExoPlayer。

因为由于本文假设您对HLS的结构已经有所了解,并且在ExoPlayer中具有一定的经验,因此我们将重点放在这一点上。

这是有关切换流(视频,音频,字幕)文档告诉我们的。她几乎没有告诉我们-使用DefaultTrackSelector初始化播放器并使用它。就是这样,好的:)

所有其他字幕(CEA-608,WebVtt)都像其他轨道一样,都存储在DefaultTrackSelector中,您需要能够获取它们。一切都分为组和子组,总之,内部结构如下所示:


现在,让我们尝试获取WebVTT类型的字幕,它们应该以“ 3”类型存储在Renderer中(C.TRACK_TYPE_TEXT-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
    }

如您所见,没有提供一些方便的迭代和搜索方式,您必须使用笔进行所有循环。

但是我们不仅需要了解列表本身,还需要了解所选的选项(我们将在以后选择)。由于某些原因,您无法从DefaultTrackSelector获取选定的曲目,但可以使用ExoPlayer本身。获取方案大致相同,我们会做得更深入,但是在这里,我们跳过了通过渲染器的过程:

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
    }

好吧,我们有一个公共列表和一个选定列表。如何在UI上组合和显示不是本文的目的。有很多方法。我们只需要学习如何设置轨道即可。

为了清楚起见,我们将以额头方式进行此操作,以便所有循环和索引均显而易见。

通过其语言代码(“ 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
                            }
                        }
                    }
                }
            }
        }
    }

结论


我们已经介绍了如何在ExoPlayer中获取和切换WebVTT字幕的基础知识。
实际上,相同的方法可以轻松地与其他类型的字幕配合使用-只需用该类型参数化方法就足够了。同样,处理音轨也不难。当然,以这种形式使用该解决方案是非常困难的,并且需要将其带到更方便的形式。

谢谢您的关注。

All Articles