ExoPlayer Hls质量

时间:2018-02-12 14:24:26

标签: android video hls exoplayer

我有播放HLS视频的ExoPlayer,我需要让用户能够改变视频质量(自动/ 1080/720/480)。

我发现使用AdaptiveTrackSelection.Factory可以确定质量,但它会一直存在,直到对象被杀死。

我也尝试使用MappingTrackSelector,我知道我的视频有4首曲目,但我没有得到如何手动选择任何曲目。这个选择会使它有效吗?

感谢任何想法。

MappingTrackSelector.MappedTrackInfo trackInfo = mDefaultTrackSelector.getCurrentMappedTrackInfo();
                        mDefaultTrackSelector.selectTracks(

//应该去哪里?

  , trackInfo.getTrackGroups(4));

1 个答案:

答案 0 :(得分:2)

关于这个帖子:https://github.com/google/ExoPlayer/issues/2250,我设法在播放前一个播放器时改变了外部播放器的视频质量,因此它不会立即进入缓冲状态。

所以我有下一堂课:

public enum HLSQuality {
    Auto, Quality1080, Quality720, Quality480, NoValue
}

class HLSUtil {

    private HLSUtil() {
    }

    @NonNull
    static HLSQuality getQuality(@NonNull Format format) {
             switch (format.height) {
            case 1080: {
                return HLSQuality.Quality1080;
            }
            case 720: {
                return HLSQuality.Quality720;
            }
            case 480:
            case 486: {
                return HLSQuality.Quality480;
            }
            default: {
                return HLSQuality.NoValue;
            }
        }
    }

    static boolean isQualityPlayable(@NonNull Format format) {
        return format.height <= 1080;
    }
}


public class ClassAdaptiveTrackSelection extends BaseTrackSelection {

    public static final class Factory implements TrackSelection.Factory {
        private final BandwidthMeter bandwidthMeter;
        private final int maxInitialBitrate = 2000000;
        private final int minDurationForQualityIncreaseMs = 10000;
        private final int maxDurationForQualityDecreaseMs = 25000;
        private final int minDurationToRetainAfterDiscardMs = 25000;
        private final float bandwidthFraction = 0.75f;
        private final float bufferedFractionToLiveEdgeForQualityIncrease = 0.75f;

        public Factory(BandwidthMeter bandwidthMeter) {
            this.bandwidthMeter = bandwidthMeter;
        }

        @Override
        public ClassAdaptiveTrackSelection createTrackSelection(TrackGroup group, int... tracks) {
            Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality reset to Auto");
            sHLSQuality = HLSQuality.Auto;

            return new ClassAdaptiveTrackSelection(
                    group,
                    tracks,
                    bandwidthMeter,
                    maxInitialBitrate,
                    minDurationForQualityIncreaseMs,
                    maxDurationForQualityDecreaseMs,
                    minDurationToRetainAfterDiscardMs,
                    bandwidthFraction,
                    bufferedFractionToLiveEdgeForQualityIncrease
            );
        }
    }

    private static HLSQuality sHLSQuality = HLSQuality.Auto;
    private final BandwidthMeter bandwidthMeter;
    private final int maxInitialBitrate;
    private final long minDurationForQualityIncreaseUs;
    private final long maxDurationForQualityDecreaseUs;
    private final long minDurationToRetainAfterDiscardUs;
    private final float bandwidthFraction;
    private final float bufferedFractionToLiveEdgeForQualityIncrease;

    private int selectedIndex;
    private int reason;

    private ClassAdaptiveTrackSelection(TrackGroup group,
                                        int[] tracks,
                                        BandwidthMeter bandwidthMeter,
                                        int maxInitialBitrate,
                                        long minDurationForQualityIncreaseMs,
                                        long maxDurationForQualityDecreaseMs,
                                        long minDurationToRetainAfterDiscardMs,
                                        float bandwidthFraction,
                                        float bufferedFractionToLiveEdgeForQualityIncrease) {
        super(group, tracks);
        this.bandwidthMeter = bandwidthMeter;
        this.maxInitialBitrate = maxInitialBitrate;
        this.minDurationForQualityIncreaseUs = minDurationForQualityIncreaseMs * 1000L;
        this.maxDurationForQualityDecreaseUs = maxDurationForQualityDecreaseMs * 1000L;
        this.minDurationToRetainAfterDiscardUs = minDurationToRetainAfterDiscardMs * 1000L;
        this.bandwidthFraction = bandwidthFraction;
        this.bufferedFractionToLiveEdgeForQualityIncrease = bufferedFractionToLiveEdgeForQualityIncrease;
        selectedIndex = determineIdealSelectedIndex(Long.MIN_VALUE);
        reason = C.SELECTION_REASON_INITIAL;
    }

    @Override
    public void updateSelectedTrack(long playbackPositionUs, long bufferedDurationUs, long availableDurationUs) {
        long nowMs = SystemClock.elapsedRealtime();
        // Stash the current selection, then make a new one.
        int currentSelectedIndex = selectedIndex;
        selectedIndex = determineIdealSelectedIndex(nowMs);
        if (selectedIndex == currentSelectedIndex) {
            return;
        }

        if (!isBlacklisted(currentSelectedIndex, nowMs)) {
            // Revert back to the current selection if conditions are not suitable for switching.
            Format currentFormat = getFormat(currentSelectedIndex);
            Format selectedFormat = getFormat(selectedIndex);
            if (selectedFormat.bitrate > currentFormat.bitrate
                    && bufferedDurationUs < minDurationForQualityIncreaseUs(availableDurationUs)) {
                // The selected track is a higher quality, but we have insufficient buffer to safely switch
                // up. Defer switching up for now.
                selectedIndex = currentSelectedIndex;
            } else if (selectedFormat.bitrate < currentFormat.bitrate
                    && bufferedDurationUs >= maxDurationForQualityDecreaseUs) {
                // The selected track is a lower quality, but we have sufficient buffer to defer switching
                // down for now.
                selectedIndex = currentSelectedIndex;
            }
        }
        // If we adapted, update the trigger.
        if (selectedIndex != currentSelectedIndex) {
            reason = C.SELECTION_REASON_ADAPTIVE;
        }
    }

    @Override
    public int getSelectedIndex() {
        return selectedIndex;
    }

    @Override
    public int getSelectionReason() {
        return reason;
    }

    @Override
    public Object getSelectionData() {
        return null;
    }

    @Override
    public int evaluateQueueSize(long playbackPositionUs, List<? extends MediaChunk> queue) {
        if (queue.isEmpty()) {
            return 0;
        }
        int queueSize = queue.size();
        long bufferedDurationUs = queue.get(queueSize - 1).endTimeUs - playbackPositionUs;
        if (bufferedDurationUs < minDurationToRetainAfterDiscardUs) {
            return queueSize;
        }
        int idealSelectedIndex = determineIdealSelectedIndex(SystemClock.elapsedRealtime());
        Format idealFormat = getFormat(idealSelectedIndex);
        // If the chunks contain video, discard from the first SD chunk beyond
        // minDurationToRetainAfterDiscardUs whose resolution and bitrate are both lower than the ideal
        // track.
        for (int i = 0; i < queueSize; i++) {
            MediaChunk chunk = queue.get(i);
            Format format = chunk.trackFormat;
            long durationBeforeThisChunkUs = chunk.startTimeUs - playbackPositionUs;
            if (durationBeforeThisChunkUs >= minDurationToRetainAfterDiscardUs
                    && format.bitrate < idealFormat.bitrate
                    && format.height != Format.NO_VALUE && format.height < 720
                    && format.width != Format.NO_VALUE && format.width < 1280
                    && format.height < idealFormat.height) {
                return i;
            }
        }
        return queueSize;
    }

    private int determineIdealSelectedIndex(long nowMs) {
        if (sHLSQuality != HLSQuality.Auto) {
            Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality seeking for " + String.valueOf(sHLSQuality));
            for (int i = 0; i < length; i++) {
                Format format = getFormat(i);
                if (HLSUtil.getQuality(format) == sHLSQuality) {
                    Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality set to " + String.valueOf(sHLSQuality));
                    return i;
                }
            }
        }

        Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality seeking for auto quality " + String.valueOf(sHLSQuality));
        long bitrateEstimate = bandwidthMeter.getBitrateEstimate();
        long effectiveBitrate = bitrateEstimate == BandwidthMeter.NO_ESTIMATE
                ? maxInitialBitrate : (long) (bitrateEstimate * bandwidthFraction);
        int lowestBitrateNonBlacklistedIndex = 0;
        for (int i = 0; i < length; i++) {
            if (nowMs == Long.MIN_VALUE || !isBlacklisted(i, nowMs)) {
                Format format = getFormat(i);
                if (format.bitrate <= effectiveBitrate && HLSUtil.isQualityPlayable(format)) {
                    Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality auto quality found " + String.valueOf(sHLSQuality));
                    return i;
                } else {
                    lowestBitrateNonBlacklistedIndex = i;
                }
            }
        }
        return lowestBitrateNonBlacklistedIndex;
    }

    private long minDurationForQualityIncreaseUs(long availableDurationUs) {
        boolean isAvailableDurationTooShort = availableDurationUs != C.TIME_UNSET
                && availableDurationUs <= minDurationForQualityIncreaseUs;
        return isAvailableDurationTooShort
                ? (long) (availableDurationUs * bufferedFractionToLiveEdgeForQualityIncrease)
                : minDurationForQualityIncreaseUs;
    }

    static void setHLSQuality(HLSQuality HLSQuality) {
        sHLSQuality = HLSQuality;
    }
}

希望它有所帮助。