mirror of
https://github.com/samsonjs/media.git
synced 2026-03-26 09:35:47 +00:00
Rework HlsPlaylist attribute inheritance
The reason for the change is that variable substititution requires master playlist variable definitions at the moment of parsing. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=208997963
This commit is contained in:
parent
7a34869f9a
commit
4530944ed7
7 changed files with 162 additions and 61 deletions
|
|
@ -36,6 +36,7 @@ import com.google.android.exoplayer2.source.hls.playlist.DefaultHlsPlaylistTrack
|
|||
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist;
|
||||
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylist;
|
||||
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistParser;
|
||||
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistParserFactory;
|
||||
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistTracker;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
|
|
@ -171,7 +172,11 @@ public final class HlsMediaSource extends BaseMediaSource
|
|||
* @param playlistParser A {@link ParsingLoadable.Parser} for HLS playlists.
|
||||
* @return This factory, for convenience.
|
||||
* @throws IllegalStateException If one of the {@code create} methods has already been called.
|
||||
* @deprecated Use {@link #setPlaylistTracker(HlsPlaylistTracker)} instead. Using this method
|
||||
* prevents support for attributes that are carried over from the master playlist to the
|
||||
* media playlists.
|
||||
*/
|
||||
@Deprecated
|
||||
public Factory setPlaylistParser(ParsingLoadable.Parser<HlsPlaylist> playlistParser) {
|
||||
Assertions.checkState(!isCreateCalled);
|
||||
Assertions.checkState(playlistTracker == null, "A playlist tracker has already been set.");
|
||||
|
|
@ -239,11 +244,15 @@ public final class HlsMediaSource extends BaseMediaSource
|
|||
public HlsMediaSource createMediaSource(Uri playlistUri) {
|
||||
isCreateCalled = true;
|
||||
if (playlistTracker == null) {
|
||||
playlistTracker =
|
||||
new DefaultHlsPlaylistTracker(
|
||||
hlsDataSourceFactory,
|
||||
loadErrorHandlingPolicy,
|
||||
playlistParser != null ? playlistParser : new HlsPlaylistParser());
|
||||
if (playlistParser == null) {
|
||||
playlistTracker =
|
||||
new DefaultHlsPlaylistTracker(
|
||||
hlsDataSourceFactory, loadErrorHandlingPolicy, HlsPlaylistParserFactory.DEFAULT);
|
||||
} else {
|
||||
playlistTracker =
|
||||
new DefaultHlsPlaylistTracker(
|
||||
hlsDataSourceFactory, loadErrorHandlingPolicy, playlistParser);
|
||||
}
|
||||
}
|
||||
return new HlsMediaSource(
|
||||
playlistUri,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package com.google.android.exoplayer2.source.hls.playlist;
|
|||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.SystemClock;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ParserException;
|
||||
import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher;
|
||||
|
|
@ -47,18 +48,19 @@ public final class DefaultHlsPlaylistTracker
|
|||
private static final double PLAYLIST_STUCK_TARGET_DURATION_COEFFICIENT = 3.5;
|
||||
|
||||
private final HlsDataSourceFactory dataSourceFactory;
|
||||
private final ParsingLoadable.Parser<HlsPlaylist> playlistParser;
|
||||
private final HlsPlaylistParserFactory playlistParserFactory;
|
||||
private final LoadErrorHandlingPolicy loadErrorHandlingPolicy;
|
||||
private final IdentityHashMap<HlsUrl, MediaPlaylistBundle> playlistBundles;
|
||||
private final List<PlaylistEventListener> listeners;
|
||||
|
||||
private EventDispatcher eventDispatcher;
|
||||
private Loader initialPlaylistLoader;
|
||||
private Handler playlistRefreshHandler;
|
||||
private PrimaryPlaylistListener primaryPlaylistListener;
|
||||
private HlsMasterPlaylist masterPlaylist;
|
||||
private HlsUrl primaryHlsUrl;
|
||||
private HlsMediaPlaylist primaryUrlSnapshot;
|
||||
private @Nullable ParsingLoadable.Parser<HlsPlaylist> mediaPlaylistParser;
|
||||
private @Nullable EventDispatcher eventDispatcher;
|
||||
private @Nullable Loader initialPlaylistLoader;
|
||||
private @Nullable Handler playlistRefreshHandler;
|
||||
private @Nullable PrimaryPlaylistListener primaryPlaylistListener;
|
||||
private @Nullable HlsMasterPlaylist masterPlaylist;
|
||||
private @Nullable HlsUrl primaryHlsUrl;
|
||||
private @Nullable HlsMediaPlaylist primaryUrlSnapshot;
|
||||
private boolean isLive;
|
||||
private long initialStartTimeUs;
|
||||
|
||||
|
|
@ -66,13 +68,30 @@ public final class DefaultHlsPlaylistTracker
|
|||
* @param dataSourceFactory A factory for {@link DataSource} instances.
|
||||
* @param loadErrorHandlingPolicy The {@link LoadErrorHandlingPolicy}.
|
||||
* @param playlistParser A {@link ParsingLoadable.Parser} for HLS playlists.
|
||||
* @deprecated Use {@link #DefaultHlsPlaylistTracker(HlsDataSourceFactory,
|
||||
* LoadErrorHandlingPolicy, HlsPlaylistParserFactory)} instead. Using this constructor
|
||||
* prevents support for attributes that are carried over from the master playlist to the media
|
||||
* playlists.
|
||||
*/
|
||||
@Deprecated
|
||||
public DefaultHlsPlaylistTracker(
|
||||
HlsDataSourceFactory dataSourceFactory,
|
||||
LoadErrorHandlingPolicy loadErrorHandlingPolicy,
|
||||
ParsingLoadable.Parser<HlsPlaylist> playlistParser) {
|
||||
this(dataSourceFactory, loadErrorHandlingPolicy, createFixedFactory(playlistParser));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dataSourceFactory A factory for {@link DataSource} instances.
|
||||
* @param loadErrorHandlingPolicy The {@link LoadErrorHandlingPolicy}.
|
||||
* @param playlistParserFactory An {@link HlsPlaylistParserFactory}.
|
||||
*/
|
||||
public DefaultHlsPlaylistTracker(
|
||||
HlsDataSourceFactory dataSourceFactory,
|
||||
LoadErrorHandlingPolicy loadErrorHandlingPolicy,
|
||||
HlsPlaylistParserFactory playlistParserFactory) {
|
||||
this.dataSourceFactory = dataSourceFactory;
|
||||
this.playlistParser = playlistParser;
|
||||
this.playlistParserFactory = playlistParserFactory;
|
||||
this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
|
||||
listeners = new ArrayList<>();
|
||||
playlistBundles = new IdentityHashMap<>();
|
||||
|
|
@ -94,7 +113,7 @@ public final class DefaultHlsPlaylistTracker
|
|||
dataSourceFactory.createDataSource(C.DATA_TYPE_MANIFEST),
|
||||
initialPlaylistUri,
|
||||
C.DATA_TYPE_MANIFEST,
|
||||
playlistParser);
|
||||
playlistParserFactory.createPlaylistParser());
|
||||
Assertions.checkState(initialPlaylistLoader == null);
|
||||
initialPlaylistLoader = new Loader("DefaultHlsPlaylistTracker:MasterPlaylist");
|
||||
long elapsedRealtime =
|
||||
|
|
@ -198,6 +217,7 @@ public final class DefaultHlsPlaylistTracker
|
|||
masterPlaylist = (HlsMasterPlaylist) result;
|
||||
}
|
||||
this.masterPlaylist = masterPlaylist;
|
||||
mediaPlaylistParser = playlistParserFactory.createPlaylistParser(masterPlaylist);
|
||||
primaryHlsUrl = masterPlaylist.variants.get(0);
|
||||
ArrayList<HlsUrl> urls = new ArrayList<>();
|
||||
urls.addAll(masterPlaylist.variants);
|
||||
|
|
@ -420,7 +440,7 @@ public final class DefaultHlsPlaylistTracker
|
|||
dataSourceFactory.createDataSource(C.DATA_TYPE_MANIFEST),
|
||||
UriUtil.resolveToUri(masterPlaylist.baseUri, playlistUrl.url),
|
||||
C.DATA_TYPE_MANIFEST,
|
||||
playlistParser);
|
||||
mediaPlaylistParser);
|
||||
}
|
||||
|
||||
public HlsMediaPlaylist getPlaylistSnapshot() {
|
||||
|
|
@ -569,9 +589,6 @@ public final class DefaultHlsPlaylistTracker
|
|||
}
|
||||
|
||||
private void processLoadedPlaylist(HlsMediaPlaylist loadedPlaylist, long loadDurationMs) {
|
||||
// Update the loaded playlist with any inheritable information from the master playlist.
|
||||
loadedPlaylist = loadedPlaylist.copyWithMasterPlaylistInfo(masterPlaylist);
|
||||
|
||||
HlsMediaPlaylist oldPlaylist = playlistSnapshot;
|
||||
long currentTimeMs = SystemClock.elapsedRealtime();
|
||||
lastSnapshotLoadMs = currentTimeMs;
|
||||
|
|
@ -630,4 +647,26 @@ public final class DefaultHlsPlaylistTracker
|
|||
return primaryHlsUrl == playlistUrl && !maybeSelectNewPrimaryUrl();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a factory which always returns the given playlist parser.
|
||||
*
|
||||
* @param playlistParser The parser to return.
|
||||
* @return A factory which always returns the given playlist parser.
|
||||
*/
|
||||
private static HlsPlaylistParserFactory createFixedFactory(
|
||||
ParsingLoadable.Parser<HlsPlaylist> playlistParser) {
|
||||
return new HlsPlaylistParserFactory() {
|
||||
@Override
|
||||
public ParsingLoadable.Parser<HlsPlaylist> createPlaylistParser() {
|
||||
return playlistParser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParsingLoadable.Parser<HlsPlaylist> createPlaylistParser(
|
||||
HlsMasterPlaylist masterPlaylist) {
|
||||
return playlistParser;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,18 @@ import java.util.List;
|
|||
/** Represents an HLS master playlist. */
|
||||
public final class HlsMasterPlaylist extends HlsPlaylist {
|
||||
|
||||
/** Represents an empty master playlist, from which no attributes can be inherited. */
|
||||
public static final HlsMasterPlaylist EMPTY =
|
||||
new HlsMasterPlaylist(
|
||||
/* baseUri= */ "",
|
||||
/* tags= */ Collections.emptyList(),
|
||||
/* variants= */ Collections.emptyList(),
|
||||
/* audios= */ Collections.emptyList(),
|
||||
/* subtitles= */ Collections.emptyList(),
|
||||
/* muxedAudioFormat= */ null,
|
||||
/* muxedCaptionFormats= */ Collections.emptyList(),
|
||||
/* hasIndependentSegments= */ false);
|
||||
|
||||
public static final int GROUP_INDEX_VARIANT = 0;
|
||||
public static final int GROUP_INDEX_AUDIO = 1;
|
||||
public static final int GROUP_INDEX_SUBTITLE = 2;
|
||||
|
|
|
|||
|
|
@ -336,40 +336,6 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
|
|||
segments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a playlist identical to this one, except for adding any inheritable attributes from the
|
||||
* provided {@link HlsMasterPlaylist}.
|
||||
*
|
||||
* <p>The inheritable attributes are:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link #hasIndependentSegments}.
|
||||
* </ul>
|
||||
*
|
||||
* @return An identical playlist including the inheritable attributes from {@code masterPlaylist}.
|
||||
*/
|
||||
public HlsMediaPlaylist copyWithMasterPlaylistInfo(HlsMasterPlaylist masterPlaylist) {
|
||||
if (hasIndependentSegments || !masterPlaylist.hasIndependentSegments) {
|
||||
return this;
|
||||
}
|
||||
return new HlsMediaPlaylist(
|
||||
playlistType,
|
||||
baseUri,
|
||||
tags,
|
||||
startOffsetUs,
|
||||
startTimeUs,
|
||||
hasDiscontinuitySequence,
|
||||
discontinuitySequence,
|
||||
mediaSequence,
|
||||
version,
|
||||
targetDurationUs,
|
||||
hasIndependentSegments || masterPlaylist.hasIndependentSegments,
|
||||
hasEndTag,
|
||||
hasProgramDateTime,
|
||||
protectionSchemes,
|
||||
segments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a playlist identical to this one except that an end tag is added. If an end tag is
|
||||
* already present then the playlist will return itself.
|
||||
|
|
|
|||
|
|
@ -148,6 +148,26 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
|||
private static final Pattern REGEX_DEFAULT = compileBooleanAttrPattern("DEFAULT");
|
||||
private static final Pattern REGEX_FORCED = compileBooleanAttrPattern("FORCED");
|
||||
|
||||
private final HlsMasterPlaylist masterPlaylist;
|
||||
|
||||
/**
|
||||
* Creates an instance where media playlists are parsed without inheriting attributes from a
|
||||
* master playlist.
|
||||
*/
|
||||
public HlsPlaylistParser() {
|
||||
this(HlsMasterPlaylist.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance where parsed media playlists inherit attributes from the given master
|
||||
* playlist.
|
||||
*
|
||||
* @param masterPlaylist The master playlist from which media playlists will inherit attributes.
|
||||
*/
|
||||
public HlsPlaylistParser(HlsMasterPlaylist masterPlaylist) {
|
||||
this.masterPlaylist = masterPlaylist;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HlsPlaylist parse(Uri uri, InputStream inputStream) throws IOException {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
|
|
@ -174,7 +194,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
|||
|| line.equals(TAG_DISCONTINUITY_SEQUENCE)
|
||||
|| line.equals(TAG_ENDLIST)) {
|
||||
extraLines.add(line);
|
||||
return parseMediaPlaylist(new LineIterator(extraLines, reader), uri.toString());
|
||||
return parseMediaPlaylist(
|
||||
masterPlaylist, new LineIterator(extraLines, reader), uri.toString());
|
||||
} else {
|
||||
extraLines.add(line);
|
||||
}
|
||||
|
|
@ -402,14 +423,14 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
|||
return flags;
|
||||
}
|
||||
|
||||
private static HlsMediaPlaylist parseMediaPlaylist(LineIterator iterator, String baseUri)
|
||||
throws IOException {
|
||||
private static HlsMediaPlaylist parseMediaPlaylist(
|
||||
HlsMasterPlaylist masterPlaylist, LineIterator iterator, String baseUri) throws IOException {
|
||||
@HlsMediaPlaylist.PlaylistType int playlistType = HlsMediaPlaylist.PLAYLIST_TYPE_UNKNOWN;
|
||||
long startOffsetUs = C.TIME_UNSET;
|
||||
long mediaSequence = 0;
|
||||
int version = 1; // Default version == 1.
|
||||
long targetDurationUs = C.TIME_UNSET;
|
||||
boolean hasIndependentSegmentsTag = false;
|
||||
boolean hasIndependentSegmentsTag = masterPlaylist.hasIndependentSegments;
|
||||
boolean hasEndTag = false;
|
||||
Segment initializationSegment = null;
|
||||
List<Segment> segments = new ArrayList<>();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.source.hls.playlist;
|
||||
|
||||
import com.google.android.exoplayer2.upstream.ParsingLoadable;
|
||||
|
||||
/** Factory for {@link HlsPlaylist} parsers. */
|
||||
public interface HlsPlaylistParserFactory {
|
||||
|
||||
HlsPlaylistParserFactory DEFAULT =
|
||||
new HlsPlaylistParserFactory() {
|
||||
@Override
|
||||
public ParsingLoadable.Parser<HlsPlaylist> createPlaylistParser() {
|
||||
return new HlsPlaylistParser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParsingLoadable.Parser<HlsPlaylist> createPlaylistParser(
|
||||
HlsMasterPlaylist masterPlaylist) {
|
||||
return new HlsPlaylistParser(masterPlaylist);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a stand-alone playlist parser. Playlists parsed by the returned parser do not inherit
|
||||
* any attributes from other playlists.
|
||||
*/
|
||||
ParsingLoadable.Parser<HlsPlaylist> createPlaylistParser();
|
||||
|
||||
/**
|
||||
* Returns a playlist parser for playlists that were referenced by the given {@link
|
||||
* HlsMasterPlaylist}. Returned {@link HlsMediaPlaylist} instances may inherit attributes from
|
||||
* {@code masterPlaylist}.
|
||||
*
|
||||
* @param masterPlaylist The master playlist that referenced any parsed media playlists.
|
||||
* @return A parser for HLS playlists.
|
||||
*/
|
||||
ParsingLoadable.Parser<HlsPlaylist> createPlaylistParser(HlsMasterPlaylist masterPlaylist);
|
||||
}
|
||||
|
|
@ -383,10 +383,11 @@ public class HlsMediaPlaylistParserTest {
|
|||
+ "#EXTINF:5.005,\n"
|
||||
+ "02/00/47.ts\n";
|
||||
InputStream inputStream = new ByteArrayInputStream(Util.getUtf8Bytes(playlistString));
|
||||
HlsMediaPlaylist playlist =
|
||||
HlsMediaPlaylist standalonePlaylist =
|
||||
(HlsMediaPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream);
|
||||
assertThat(playlist.hasIndependentSegments).isFalse();
|
||||
assertThat(standalonePlaylist.hasIndependentSegments).isFalse();
|
||||
|
||||
inputStream.reset();
|
||||
HlsMasterPlaylist masterPlaylist =
|
||||
new HlsMasterPlaylist(
|
||||
/* baseUri= */ "https://example.com/",
|
||||
|
|
@ -397,7 +398,8 @@ public class HlsMediaPlaylistParserTest {
|
|||
/* muxedAudioFormat= */ null,
|
||||
/* muxedCaptionFormats= */ null,
|
||||
/* hasIndependentSegments= */ true);
|
||||
|
||||
assertThat(playlist.copyWithMasterPlaylistInfo(masterPlaylist).hasIndependentSegments).isTrue();
|
||||
HlsMediaPlaylist playlistWithInheritance =
|
||||
(HlsMediaPlaylist) new HlsPlaylistParser(masterPlaylist).parse(playlistUri, inputStream);
|
||||
assertThat(playlistWithInheritance.hasIndependentSegments).isTrue();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue