Как показать HLS встроенные подписи на Exoplayer

Как включить, а также выбрать разные субтитры, которые встроены в видео Vimeo в формате HLS, используя Exoplayer, ExoMedia или другой проигрыватель? В iOS это же видео уже изначально предоставляет возможность субтитров, но в Android я не могу найти средства для его реализации.

1 ответ

Решение

Мой ответ здесь будет выглядеть примерно так, так что вы можете сначала проверить его.

ExoPlayer - это библиотека для Android. Получить субтитры для показа нетривиальной задачи, но демонстрационное приложение для этой библиотеки содержит весь код, необходимый для работы с видео HLS. Более конкретно PlayerActivity учебный класс. Вы можете перейти в HLS -> "Основной поток Apple 16x9" в демонстрационном приложении, и у этого видео есть тонна субтитров (так называемые "текстовые дорожки").

Просто чтобы упростить их код, чтобы он не зависел от помощника (и чтобы вы могли видеть, как он работает только с закрытыми заголовками), я написал / задокументировал часть их кода ниже.

private static class ClosedCaptionManager {

    ClosedCaptionManager(MappingTrackSelector mappingTrackSelector, SimpleExoPlayer player) {
        this.player = player;
        this.trackSelector = mappingTrackSelector;
    }

    SimpleExoPlayer player;
    MappingTrackSelector trackSelector;

    // These two could be fields OR passed around
    int textTrackIndex;
    TrackGroupArray trackGroups;

    ArrayList<Pair<Integer, Integer>> pairTrackList = new ArrayList<>();

    private boolean checkAndSetClosedCaptions() {
        // This is the body of the logic  for see if there are even video tracks
        // It also does some field setting
        MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo();
        if (mappedTrackInfo == null) {
            return false;
        }
        for (int i = 0; i < mappedTrackInfo.length; i++) {
            trackGroups = mappedTrackInfo.getTrackGroups(i);
            if (trackGroups.length != 0) {
                switch (player.getRendererType(i)) {
                    case C.TRACK_TYPE_TEXT:
                        textTrackIndex = i;
                        return true;
                }
            }
        }
        return false;
    }

    private void buildTrackList() {
        // This next part is actually about getting the list.
        // Below you'd be building up items in a list. This just does
        // views directly, but you could just have a list of track names (with indexes)
        for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) {
            TrackGroup group = trackGroups.get(groupIndex);
            for (int trackIndex = 0; trackIndex < group.length; trackIndex++) {
                if (trackIndex == 0) {
                    // Beginning of a new set, the demo app adds a divider
                }
                //CheckedTextView trackView = ...; // The TextView to show in the list
                // The below points to a util which extracts the quality from the TrackGroup
                //trackView.setText(DemoUtil.buildTrackName(group.getFormat(trackIndex)));
                Log.e("Thing", DemoUtil.buildTrackName(group.getFormat(trackIndex)));
                pairTrackList.add(new Pair<>(groupIndex, trackIndex));
            }
        }
    }

    private void onTrackViewClick(Pair<Integer, Integer> trackPair) {
        // Assuming you tagged the view with the groupIndex and trackIndex, you
        // can build your override with that info.
        Pair<Integer, Integer> tag = trackPair;
        int groupIndex = tag.first;
        int trackIndex = tag.second;
        // This is the override you'd use for something that isn't adaptive.
        // `override = new SelectionOverride(FIXED_FACTORY, groupIndex, trackIndex);`
        // Otherwise they call their helper for adaptives (HLS/DASH), which roughly does:
        int[] tracks = getTracksAdding(new MappingTrackSelector.SelectionOverride(
                        new FixedTrackSelection.Factory(), groupIndex, trackIndex),
                trackIndex
        );
        TrackSelection.Factory factory = tracks.length == 1
                ? new FixedTrackSelection.Factory()
                : new AdaptiveTrackSelection.Factory(BANDWIDTH_METER);

        MappingTrackSelector.SelectionOverride override =
                new MappingTrackSelector.SelectionOverride(factory, groupIndex, tracks);

        // Then we actually set our override on the selector to switch the text track
        trackSelector.setSelectionOverride(textTrackIndex, trackGroups, override);
    }

    private static int[] getTracksAdding(MappingTrackSelector.SelectionOverride override, int addedTrack) {
        int[] tracks = override.tracks;
        tracks = Arrays.copyOf(tracks, tracks.length + 1);
        tracks[tracks.length - 1] = addedTrack;
        return tracks;
    }
}

Затем, если вы разместите следующий код в конце их initializePlayer() метод, вы получите представление о том, как эти части сочетаются друг с другом.

    final ClosedCaptionManager closedCaptionManager = new ClosedCaptionManager(trackSelector, player);

    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            boolean hasTracks = closedCaptionManager.checkAndSetClosedCaptions();
            if (hasTracks) {
                closedCaptionManager.buildTrackList();
                closedCaptionManager.onTrackViewClick(closedCaptionManager.pairTrackList.get(4));
            }
        }
    }, 2000);

Приведенный выше код является очень небрежным, но, надеюсь, по крайней мере, вы должны начать в правильном направлении. Я бы не рекомендовал использовать какую-либо часть написанного - лучше всего понять, как разные части сочетаются друг с другом. То, что у них есть в демо-приложении, немного лучше, так как их код можно многократно использовать для различных типов выбора дорожек (поскольку вы можете иметь видео, аудио и текстовые дорожки).

Это хорошо работает!

TrackGroupArray trackGroups = mappedTrackInfo.getTrackGroups(rendererIndex);   

TrackSelectionArray currentTrackGroups = player.getCurrentTrackSelections();
TrackSelection currentTrackSelection = currentTrackGroups.get(rendererIndex);


    for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) {

        TrackGroup group = trackGroups.get(groupIndex);

        for (int trackIndex = 0; trackIndex < group.length; trackIndex++) {
            Format trackFormat = group.getFormat(trackIndex);


            if(currentTrackSelection!=null && currentTrackSelection.getSelectedFormat()==trackFormat){
                //THIS ONE IS SELECTED
            }




        }
    }

rendererIndex является 0 для видео, 1 для аудио и 2 для субтитров / текста

Другие вопросы по тегам