mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Refactor #6.HLS.4
Pull more logic up to HlsSampleSource. Somewhat regretfully, this also backs out the optimization work done toward the ref'd issue. I think that's one for another time perhaps... Issue: #551 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=123417413
This commit is contained in:
parent
6d62962ab0
commit
8744e8dce9
3 changed files with 183 additions and 253 deletions
|
|
@ -28,9 +28,7 @@ import com.google.android.exoplayer.extractor.mp3.Mp3Extractor;
|
||||||
import com.google.android.exoplayer.extractor.ts.AdtsExtractor;
|
import com.google.android.exoplayer.extractor.ts.AdtsExtractor;
|
||||||
import com.google.android.exoplayer.extractor.ts.PtsTimestampAdjuster;
|
import com.google.android.exoplayer.extractor.ts.PtsTimestampAdjuster;
|
||||||
import com.google.android.exoplayer.extractor.ts.TsExtractor;
|
import com.google.android.exoplayer.extractor.ts.TsExtractor;
|
||||||
import com.google.android.exoplayer.hls.playlist.HlsMasterPlaylist;
|
|
||||||
import com.google.android.exoplayer.hls.playlist.HlsMediaPlaylist;
|
import com.google.android.exoplayer.hls.playlist.HlsMediaPlaylist;
|
||||||
import com.google.android.exoplayer.hls.playlist.HlsPlaylist;
|
|
||||||
import com.google.android.exoplayer.hls.playlist.HlsPlaylistParser;
|
import com.google.android.exoplayer.hls.playlist.HlsPlaylistParser;
|
||||||
import com.google.android.exoplayer.hls.playlist.Variant;
|
import com.google.android.exoplayer.hls.playlist.Variant;
|
||||||
import com.google.android.exoplayer.upstream.DataSource;
|
import com.google.android.exoplayer.upstream.DataSource;
|
||||||
|
|
@ -48,10 +46,8 @@ import android.util.Log;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -70,54 +66,53 @@ public class HlsChunkSource {
|
||||||
private static final String VTT_FILE_EXTENSION = ".vtt";
|
private static final String VTT_FILE_EXTENSION = ".vtt";
|
||||||
private static final String WEBVTT_FILE_EXTENSION = ".webvtt";
|
private static final String WEBVTT_FILE_EXTENSION = ".webvtt";
|
||||||
|
|
||||||
private final int type;
|
private final String baseUri;
|
||||||
private final DataSource dataSource;
|
private final DataSource dataSource;
|
||||||
private final FormatEvaluator adaptiveFormatEvaluator;
|
private final FormatEvaluator adaptiveFormatEvaluator;
|
||||||
private final Evaluation evaluation;
|
private final Evaluation evaluation;
|
||||||
private final HlsPlaylistParser playlistParser;
|
private final HlsPlaylistParser playlistParser;
|
||||||
private final PtsTimestampAdjusterProvider timestampAdjusterProvider;
|
private final PtsTimestampAdjusterProvider timestampAdjusterProvider;
|
||||||
|
private final Variant[] variants;
|
||||||
|
private final HlsMediaPlaylist[] variantPlaylists;
|
||||||
|
private final long[] variantLastPlaylistLoadTimesMs;
|
||||||
|
|
||||||
private byte[] scratchSpace;
|
private byte[] scratchSpace;
|
||||||
private boolean live;
|
private boolean live;
|
||||||
private long durationUs;
|
private long durationUs;
|
||||||
private IOException fatalError;
|
private IOException fatalError;
|
||||||
private String baseUri;
|
|
||||||
private Format muxedAudioFormat;
|
|
||||||
private Format muxedCaptionFormat;
|
|
||||||
|
|
||||||
private Uri encryptionKeyUri;
|
private Uri encryptionKeyUri;
|
||||||
private byte[] encryptionKey;
|
private byte[] encryptionKey;
|
||||||
private String encryptionIvString;
|
private String encryptionIvString;
|
||||||
private byte[] encryptionIv;
|
private byte[] encryptionIv;
|
||||||
|
|
||||||
// Properties of exposed tracks.
|
|
||||||
private Variant[] variants;
|
|
||||||
private HlsMediaPlaylist[] variantPlaylists;
|
|
||||||
private long[] variantLastPlaylistLoadTimesMs;
|
|
||||||
|
|
||||||
// Properties of enabled variants.
|
// Properties of enabled variants.
|
||||||
private Variant[] enabledVariants;
|
private Variant[] enabledVariants;
|
||||||
private long[] enabledVariantBlacklistTimes;
|
private long[] enabledVariantBlacklistTimes;
|
||||||
private boolean[] enabledVariantBlacklistFlags;
|
private boolean[] enabledVariantBlacklistFlags;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param type The type of chunk provided by the source. One of {@link C#TRACK_TYPE_DEFAULT},
|
* @param baseUri The playlist's base uri.
|
||||||
* {@link C#TRACK_TYPE_AUDIO} and {@link C#TRACK_TYPE_TEXT}.
|
* @param variants The available variants.
|
||||||
* @param dataSource A {@link DataSource} suitable for loading the media data.
|
* @param dataSource A {@link DataSource} suitable for loading the media data.
|
||||||
* @param timestampAdjusterProvider A provider of {@link PtsTimestampAdjuster} instances. If
|
* @param timestampAdjusterProvider A provider of {@link PtsTimestampAdjuster} instances. If
|
||||||
* multiple {@link HlsChunkSource}s are used for a single playback, they should all share the
|
* multiple {@link HlsChunkSource}s are used for a single playback, they should all share the
|
||||||
* same provider.
|
* same provider.
|
||||||
* @param adaptiveFormatEvaluator For adaptive tracks, selects from the available formats.
|
* @param adaptiveFormatEvaluator For adaptive tracks, selects from the available formats.
|
||||||
*/
|
*/
|
||||||
public HlsChunkSource(int type, DataSource dataSource,
|
public HlsChunkSource(String baseUri, Variant[] variants, DataSource dataSource,
|
||||||
PtsTimestampAdjusterProvider timestampAdjusterProvider,
|
PtsTimestampAdjusterProvider timestampAdjusterProvider,
|
||||||
FormatEvaluator adaptiveFormatEvaluator) {
|
FormatEvaluator adaptiveFormatEvaluator) {
|
||||||
this.type = type;
|
this.baseUri = baseUri;
|
||||||
|
this.variants = variants;
|
||||||
this.dataSource = dataSource;
|
this.dataSource = dataSource;
|
||||||
this.adaptiveFormatEvaluator = adaptiveFormatEvaluator;
|
this.adaptiveFormatEvaluator = adaptiveFormatEvaluator;
|
||||||
this.timestampAdjusterProvider = timestampAdjusterProvider;
|
this.timestampAdjusterProvider = timestampAdjusterProvider;
|
||||||
playlistParser = new HlsPlaylistParser();
|
playlistParser = new HlsPlaylistParser();
|
||||||
evaluation = new Evaluation();
|
evaluation = new Evaluation();
|
||||||
|
variantPlaylists = new HlsMediaPlaylist[variants.length];
|
||||||
|
variantLastPlaylistLoadTimesMs = new long[variants.length];
|
||||||
|
selectTracks(new int[] {0});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -141,19 +136,6 @@ public class HlsChunkSource {
|
||||||
return adaptiveFormatEvaluator != null;
|
return adaptiveFormatEvaluator != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepares the source.
|
|
||||||
*
|
|
||||||
* @param playlist A {@link HlsPlaylist}.
|
|
||||||
*/
|
|
||||||
public void prepare(HlsPlaylist playlist) {
|
|
||||||
processPlaylist(playlist);
|
|
||||||
if (variants.length > 0) {
|
|
||||||
// Select the first variant listed in the master playlist.
|
|
||||||
selectTracks(new int[] {0});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether this is a live playback.
|
* Returns whether this is a live playback.
|
||||||
* <p>
|
* <p>
|
||||||
|
|
@ -199,28 +181,6 @@ public class HlsChunkSource {
|
||||||
return variants[index].format;
|
return variants[index].format;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the format of the audio muxed into variants, or null if unknown.
|
|
||||||
* <p>
|
|
||||||
* This method should only be called after the source has been prepared.
|
|
||||||
*
|
|
||||||
* @return The format of the audio muxed into variants, or null if unknown.
|
|
||||||
*/
|
|
||||||
public Format getMuxedAudioFormat() {
|
|
||||||
return muxedAudioFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the format of the captions muxed into variants, or null if unknown.
|
|
||||||
* <p>
|
|
||||||
* This method should only be called after the source has been prepared.
|
|
||||||
*
|
|
||||||
* @return The format of the captions muxed into variants, or null if unknown.
|
|
||||||
*/
|
|
||||||
public Format getMuxedCaptionFormat() {
|
|
||||||
return muxedCaptionFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Selects tracks for use.
|
* Selects tracks for use.
|
||||||
* <p>
|
* <p>
|
||||||
|
|
@ -269,17 +229,6 @@ public class HlsChunkSource {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Notifies the source that a seek has occurred.
|
|
||||||
* <p>
|
|
||||||
* This method should only be called after the source has been prepared.
|
|
||||||
*/
|
|
||||||
public void seek() {
|
|
||||||
if (type == C.TRACK_TYPE_DEFAULT) {
|
|
||||||
timestampAdjusterProvider.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the source.
|
* Resets the source.
|
||||||
* <p>
|
* <p>
|
||||||
|
|
@ -493,90 +442,6 @@ public class HlsChunkSource {
|
||||||
|
|
||||||
// Private methods.
|
// Private methods.
|
||||||
|
|
||||||
private void processPlaylist(HlsPlaylist playlist) {
|
|
||||||
baseUri = playlist.baseUri;
|
|
||||||
|
|
||||||
if (playlist instanceof HlsMediaPlaylist) {
|
|
||||||
if (type == C.TRACK_TYPE_TEXT || type == C.TRACK_TYPE_AUDIO) {
|
|
||||||
variants = new Variant[0];
|
|
||||||
variantPlaylists = new HlsMediaPlaylist[variants.length];
|
|
||||||
variantLastPlaylistLoadTimesMs = new long[variants.length];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// type == C.TRACK_TYPE_DEFAULT
|
|
||||||
Format format = Format.createContainerFormat("0", MimeTypes.APPLICATION_M3U8, null,
|
|
||||||
Format.NO_VALUE);
|
|
||||||
variants = new Variant[] {new Variant(baseUri, format, null)};
|
|
||||||
variantPlaylists = new HlsMediaPlaylist[variants.length];
|
|
||||||
variantLastPlaylistLoadTimesMs = new long[variants.length];
|
|
||||||
setMediaPlaylist(0, (HlsMediaPlaylist) playlist);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
HlsMasterPlaylist masterPlaylist = (HlsMasterPlaylist) playlist;
|
|
||||||
muxedAudioFormat = masterPlaylist.muxedAudioFormat;
|
|
||||||
muxedCaptionFormat = masterPlaylist.muxedCaptionFormat;
|
|
||||||
if (type == C.TRACK_TYPE_TEXT || type == C.TRACK_TYPE_AUDIO) {
|
|
||||||
List<Variant> variantList = type == C.TRACK_TYPE_AUDIO ? masterPlaylist.audios
|
|
||||||
: masterPlaylist.subtitles;
|
|
||||||
if (variantList != null && !variantList.isEmpty()) {
|
|
||||||
variants = new Variant[variantList.size()];
|
|
||||||
variantList.toArray(variants);
|
|
||||||
} else {
|
|
||||||
variants = new Variant[0];
|
|
||||||
}
|
|
||||||
variantPlaylists = new HlsMediaPlaylist[variants.length];
|
|
||||||
variantLastPlaylistLoadTimesMs = new long[variants.length];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// type == C.TRACK_TYPE_DEFAULT
|
|
||||||
List<Variant> enabledVariantList = new ArrayList<>(masterPlaylist.variants);
|
|
||||||
ArrayList<Variant> definiteVideoVariants = new ArrayList<>();
|
|
||||||
ArrayList<Variant> definiteAudioOnlyVariants = new ArrayList<>();
|
|
||||||
for (int i = 0; i < enabledVariantList.size(); i++) {
|
|
||||||
Variant variant = enabledVariantList.get(i);
|
|
||||||
if (variant.format.height > 0 || variantHasExplicitCodecWithPrefix(variant, "avc")) {
|
|
||||||
definiteVideoVariants.add(variant);
|
|
||||||
} else if (variantHasExplicitCodecWithPrefix(variant, "mp4a")) {
|
|
||||||
definiteAudioOnlyVariants.add(variant);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!definiteVideoVariants.isEmpty()) {
|
|
||||||
// We've identified some variants as definitely containing video. Assume variants within the
|
|
||||||
// master playlist are marked consistently, and hence that we have the full set. Filter out
|
|
||||||
// any other variants, which are likely to be audio only.
|
|
||||||
enabledVariantList = definiteVideoVariants;
|
|
||||||
} else if (definiteAudioOnlyVariants.size() < enabledVariantList.size()) {
|
|
||||||
// We've identified some variants, but not all, as being audio only. Filter them out to leave
|
|
||||||
// the remaining variants, which are likely to contain video.
|
|
||||||
enabledVariantList.removeAll(definiteAudioOnlyVariants);
|
|
||||||
} else {
|
|
||||||
// Leave the enabled variants unchanged. They're likely either all video or all audio.
|
|
||||||
}
|
|
||||||
|
|
||||||
variants = new Variant[enabledVariantList.size()];
|
|
||||||
enabledVariantList.toArray(variants);
|
|
||||||
variantPlaylists = new HlsMediaPlaylist[variants.length];
|
|
||||||
variantLastPlaylistLoadTimesMs = new long[variants.length];
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean variantHasExplicitCodecWithPrefix(Variant variant, String prefix) {
|
|
||||||
String codecs = variant.codecs;
|
|
||||||
if (TextUtils.isEmpty(codecs)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
String[] codecArray = codecs.split("(\\s*,\\s*)|(\\s*$)");
|
|
||||||
for (String codec : codecArray) {
|
|
||||||
if (codec.startsWith(prefix)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getNextVariantIndex(HlsMediaChunk previous, long playbackPositionUs) {
|
private int getNextVariantIndex(HlsMediaChunk previous, long playbackPositionUs) {
|
||||||
clearStaleBlacklistedVariants();
|
clearStaleBlacklistedVariants();
|
||||||
if (enabledVariants.length > 1) {
|
if (enabledVariants.length > 1) {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer.hls;
|
||||||
|
|
||||||
import com.google.android.exoplayer.C;
|
import com.google.android.exoplayer.C;
|
||||||
import com.google.android.exoplayer.DefaultLoadControl;
|
import com.google.android.exoplayer.DefaultLoadControl;
|
||||||
|
import com.google.android.exoplayer.Format;
|
||||||
import com.google.android.exoplayer.LoadControl;
|
import com.google.android.exoplayer.LoadControl;
|
||||||
import com.google.android.exoplayer.SampleSource;
|
import com.google.android.exoplayer.SampleSource;
|
||||||
import com.google.android.exoplayer.TrackGroup;
|
import com.google.android.exoplayer.TrackGroup;
|
||||||
|
|
@ -25,17 +26,21 @@ import com.google.android.exoplayer.TrackSelection;
|
||||||
import com.google.android.exoplayer.TrackStream;
|
import com.google.android.exoplayer.TrackStream;
|
||||||
import com.google.android.exoplayer.chunk.ChunkTrackStreamEventListener;
|
import com.google.android.exoplayer.chunk.ChunkTrackStreamEventListener;
|
||||||
import com.google.android.exoplayer.chunk.FormatEvaluator;
|
import com.google.android.exoplayer.chunk.FormatEvaluator;
|
||||||
|
import com.google.android.exoplayer.hls.playlist.HlsMasterPlaylist;
|
||||||
|
import com.google.android.exoplayer.hls.playlist.HlsMediaPlaylist;
|
||||||
import com.google.android.exoplayer.hls.playlist.HlsPlaylist;
|
import com.google.android.exoplayer.hls.playlist.HlsPlaylist;
|
||||||
import com.google.android.exoplayer.hls.playlist.HlsPlaylistParser;
|
import com.google.android.exoplayer.hls.playlist.HlsPlaylistParser;
|
||||||
|
import com.google.android.exoplayer.hls.playlist.Variant;
|
||||||
import com.google.android.exoplayer.upstream.BandwidthMeter;
|
import com.google.android.exoplayer.upstream.BandwidthMeter;
|
||||||
import com.google.android.exoplayer.upstream.DataSource;
|
import com.google.android.exoplayer.upstream.DataSource;
|
||||||
import com.google.android.exoplayer.upstream.DataSourceFactory;
|
import com.google.android.exoplayer.upstream.DataSourceFactory;
|
||||||
import com.google.android.exoplayer.upstream.DefaultAllocator;
|
import com.google.android.exoplayer.upstream.DefaultAllocator;
|
||||||
import com.google.android.exoplayer.util.Assertions;
|
|
||||||
import com.google.android.exoplayer.util.ManifestFetcher;
|
import com.google.android.exoplayer.util.ManifestFetcher;
|
||||||
|
import com.google.android.exoplayer.util.MimeTypes;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
@ -54,75 +59,59 @@ public final class HlsSampleSource implements SampleSource {
|
||||||
// TODO: Use this for playlist loads as well.
|
// TODO: Use this for playlist loads as well.
|
||||||
private static final int MIN_LOADABLE_RETRY_COUNT = 3;
|
private static final int MIN_LOADABLE_RETRY_COUNT = 3;
|
||||||
|
|
||||||
private final ManifestFetcher<HlsPlaylist> manifestFetcher;
|
private final DataSourceFactory dataSourceFactory;
|
||||||
private final HlsChunkSource[] chunkSources;
|
private final BandwidthMeter bandwidthMeter;
|
||||||
private final HlsTrackStreamWrapper[] trackStreamWrappers;
|
private final Handler eventHandler;
|
||||||
|
private final ChunkTrackStreamEventListener eventListener;
|
||||||
|
private final LoadControl loadControl;
|
||||||
private final IdentityHashMap<TrackStream, HlsTrackStreamWrapper> trackStreamSources;
|
private final IdentityHashMap<TrackStream, HlsTrackStreamWrapper> trackStreamSources;
|
||||||
private final int[] selectedTrackCounts;
|
private final PtsTimestampAdjusterProvider timestampAdjusterProvider;
|
||||||
|
private final ManifestFetcher<HlsPlaylist> manifestFetcher;
|
||||||
|
|
||||||
private boolean prepared;
|
|
||||||
private boolean seenFirstTrackSelection;
|
private boolean seenFirstTrackSelection;
|
||||||
private long durationUs;
|
private long durationUs;
|
||||||
private HlsPlaylist playlist;
|
private boolean isLive;
|
||||||
private TrackGroupArray trackGroups;
|
private TrackGroupArray trackGroups;
|
||||||
|
private int[] selectedTrackCounts;
|
||||||
|
private HlsTrackStreamWrapper[] trackStreamWrappers;
|
||||||
private HlsTrackStreamWrapper[] enabledTrackStreamWrappers;
|
private HlsTrackStreamWrapper[] enabledTrackStreamWrappers;
|
||||||
|
private boolean pendingReset;
|
||||||
|
private long lastSeekPositionUs;
|
||||||
|
|
||||||
public HlsSampleSource(Uri uri, DataSourceFactory dataSourceFactory,
|
public HlsSampleSource(Uri uri, DataSourceFactory dataSourceFactory,
|
||||||
BandwidthMeter bandwidthMeter, Handler eventHandler,
|
BandwidthMeter bandwidthMeter, Handler eventHandler,
|
||||||
ChunkTrackStreamEventListener eventListener) {
|
ChunkTrackStreamEventListener eventListener) {
|
||||||
|
this.dataSourceFactory = dataSourceFactory;
|
||||||
|
this.bandwidthMeter = bandwidthMeter;
|
||||||
|
this.eventHandler = eventHandler;
|
||||||
|
this.eventListener = eventListener;
|
||||||
|
|
||||||
|
loadControl = new DefaultLoadControl(new DefaultAllocator(C.DEFAULT_BUFFER_SEGMENT_SIZE));
|
||||||
|
timestampAdjusterProvider = new PtsTimestampAdjusterProvider();
|
||||||
|
trackStreamSources = new IdentityHashMap<>();
|
||||||
|
|
||||||
HlsPlaylistParser parser = new HlsPlaylistParser();
|
HlsPlaylistParser parser = new HlsPlaylistParser();
|
||||||
DataSource manifestDataSource = dataSourceFactory.createDataSource();
|
DataSource manifestDataSource = dataSourceFactory.createDataSource();
|
||||||
manifestFetcher = new ManifestFetcher<>(uri, manifestDataSource, parser);
|
manifestFetcher = new ManifestFetcher<>(uri, manifestDataSource, parser);
|
||||||
|
|
||||||
LoadControl loadControl = new DefaultLoadControl(
|
|
||||||
new DefaultAllocator(C.DEFAULT_BUFFER_SEGMENT_SIZE));
|
|
||||||
PtsTimestampAdjusterProvider timestampAdjusterProvider = new PtsTimestampAdjusterProvider();
|
|
||||||
|
|
||||||
DataSource defaultDataSource = dataSourceFactory.createDataSource(bandwidthMeter);
|
|
||||||
HlsChunkSource defaultChunkSource = new HlsChunkSource(C.TRACK_TYPE_DEFAULT,
|
|
||||||
defaultDataSource, timestampAdjusterProvider,
|
|
||||||
new FormatEvaluator.AdaptiveEvaluator(bandwidthMeter));
|
|
||||||
HlsTrackStreamWrapper defaultTrackStreamWrapper = new HlsTrackStreamWrapper(defaultChunkSource,
|
|
||||||
loadControl, C.DEFAULT_MUXED_BUFFER_SIZE, eventHandler, eventListener, C.TRACK_TYPE_VIDEO,
|
|
||||||
MIN_LOADABLE_RETRY_COUNT);
|
|
||||||
|
|
||||||
DataSource audioDataSource = dataSourceFactory.createDataSource(bandwidthMeter);
|
|
||||||
HlsChunkSource audioChunkSource = new HlsChunkSource(C.TRACK_TYPE_AUDIO,
|
|
||||||
audioDataSource, timestampAdjusterProvider, null);
|
|
||||||
HlsTrackStreamWrapper audioTrackStreamWrapper = new HlsTrackStreamWrapper(audioChunkSource,
|
|
||||||
loadControl, C.DEFAULT_AUDIO_BUFFER_SIZE, eventHandler, eventListener, C.TRACK_TYPE_AUDIO,
|
|
||||||
MIN_LOADABLE_RETRY_COUNT);
|
|
||||||
|
|
||||||
DataSource textDataSource = dataSourceFactory.createDataSource(bandwidthMeter);
|
|
||||||
HlsChunkSource textChunkSource = new HlsChunkSource(C.TRACK_TYPE_TEXT, textDataSource,
|
|
||||||
timestampAdjusterProvider, null);
|
|
||||||
HlsTrackStreamWrapper textTrackStreamWrapper = new HlsTrackStreamWrapper(
|
|
||||||
textChunkSource, loadControl, C.DEFAULT_TEXT_BUFFER_SIZE, eventHandler, eventListener,
|
|
||||||
C.TRACK_TYPE_TEXT, MIN_LOADABLE_RETRY_COUNT);
|
|
||||||
|
|
||||||
chunkSources = new HlsChunkSource[] {defaultChunkSource, audioChunkSource, textChunkSource};
|
|
||||||
trackStreamWrappers = new HlsTrackStreamWrapper[] {defaultTrackStreamWrapper,
|
|
||||||
audioTrackStreamWrapper, textTrackStreamWrapper};
|
|
||||||
selectedTrackCounts = new int[trackStreamWrappers.length];
|
|
||||||
trackStreamSources = new IdentityHashMap<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean prepare(long positionUs) throws IOException {
|
public boolean prepare(long positionUs) throws IOException {
|
||||||
if (prepared) {
|
if (trackGroups != null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playlist == null) {
|
if (trackStreamWrappers == null) {
|
||||||
playlist = manifestFetcher.getManifest();
|
HlsPlaylist playlist = manifestFetcher.getManifest();
|
||||||
if (playlist == null) {
|
if (playlist == null) {
|
||||||
manifestFetcher.maybeThrowError();
|
manifestFetcher.maybeThrowError();
|
||||||
manifestFetcher.requestRefresh();
|
manifestFetcher.requestRefresh();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (HlsChunkSource chunkSource : chunkSources) {
|
List<HlsTrackStreamWrapper> trackStreamWrapperList = buildTrackStreamWrappers(playlist);
|
||||||
chunkSource.prepare(playlist);
|
trackStreamWrappers = new HlsTrackStreamWrapper[trackStreamWrapperList.size()];
|
||||||
}
|
trackStreamWrapperList.toArray(trackStreamWrappers);
|
||||||
|
selectedTrackCounts = new int[trackStreamWrappers.length];
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean trackStreamWrappersPrepared = true;
|
boolean trackStreamWrappersPrepared = true;
|
||||||
|
|
@ -133,15 +122,13 @@ public final class HlsSampleSource implements SampleSource {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
durationUs = 0;
|
// The wrapper at index 0 is the one of type TRACK_TYPE_DEFAULT.
|
||||||
|
durationUs = trackStreamWrappers[0].getDurationUs();
|
||||||
|
isLive = trackStreamWrappers[0].isLive();
|
||||||
|
|
||||||
int totalTrackGroupCount = 0;
|
int totalTrackGroupCount = 0;
|
||||||
for (HlsTrackStreamWrapper trackStreamWrapper : trackStreamWrappers) {
|
for (HlsTrackStreamWrapper trackStreamWrapper : trackStreamWrappers) {
|
||||||
totalTrackGroupCount += trackStreamWrapper.getTrackGroups().length;
|
totalTrackGroupCount += trackStreamWrapper.getTrackGroups().length;
|
||||||
if (durationUs != C.UNSET_TIME_US) {
|
|
||||||
long wrapperDurationUs = trackStreamWrapper.getDurationUs();
|
|
||||||
durationUs = wrapperDurationUs == C.UNSET_TIME_US
|
|
||||||
? C.UNSET_TIME_US : Math.max(durationUs, wrapperDurationUs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
TrackGroup[] trackGroupArray = new TrackGroup[totalTrackGroupCount];
|
TrackGroup[] trackGroupArray = new TrackGroup[totalTrackGroupCount];
|
||||||
int trackGroupIndex = 0;
|
int trackGroupIndex = 0;
|
||||||
|
|
@ -152,7 +139,6 @@ public final class HlsSampleSource implements SampleSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trackGroups = new TrackGroupArray(trackGroupArray);
|
trackGroups = new TrackGroupArray(trackGroupArray);
|
||||||
prepared = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -169,7 +155,6 @@ public final class HlsSampleSource implements SampleSource {
|
||||||
@Override
|
@Override
|
||||||
public TrackStream[] selectTracks(List<TrackStream> oldStreams,
|
public TrackStream[] selectTracks(List<TrackStream> oldStreams,
|
||||||
List<TrackSelection> newSelections, long positionUs) {
|
List<TrackSelection> newSelections, long positionUs) {
|
||||||
Assertions.checkState(prepared);
|
|
||||||
TrackStream[] newStreams = new TrackStream[newSelections.size()];
|
TrackStream[] newStreams = new TrackStream[newSelections.size()];
|
||||||
// Select tracks for each wrapper.
|
// Select tracks for each wrapper.
|
||||||
int enabledTrackStreamWrapperCount = 0;
|
int enabledTrackStreamWrapperCount = 0;
|
||||||
|
|
@ -201,37 +186,37 @@ public final class HlsSampleSource implements SampleSource {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long readReset() {
|
public long readReset() {
|
||||||
long resetPositionUs = C.UNSET_TIME_US;
|
if (pendingReset) {
|
||||||
for (HlsTrackStreamWrapper trackStreamWrapper : enabledTrackStreamWrappers) {
|
pendingReset = false;
|
||||||
long childResetPositionUs = trackStreamWrapper.readReset();
|
for (HlsTrackStreamWrapper trackStreamWrapper : trackStreamWrappers) {
|
||||||
if (resetPositionUs == C.UNSET_TIME_US) {
|
trackStreamWrapper.setReadingEnabled(true);
|
||||||
resetPositionUs = childResetPositionUs;
|
|
||||||
} else if (childResetPositionUs != C.UNSET_TIME_US) {
|
|
||||||
resetPositionUs = Math.min(resetPositionUs, childResetPositionUs);
|
|
||||||
}
|
}
|
||||||
|
return lastSeekPositionUs;
|
||||||
}
|
}
|
||||||
return resetPositionUs;
|
return C.UNSET_TIME_US;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getBufferedPositionUs() {
|
public long getBufferedPositionUs() {
|
||||||
long bufferedPositionUs = durationUs != C.UNSET_TIME_US ? durationUs : Long.MAX_VALUE;
|
long bufferedPositionUs = Long.MAX_VALUE;
|
||||||
for (HlsTrackStreamWrapper trackStreamWrapper : enabledTrackStreamWrappers) {
|
for (HlsTrackStreamWrapper trackStreamWrapper : enabledTrackStreamWrappers) {
|
||||||
long rendererBufferedPositionUs = trackStreamWrapper.getBufferedPositionUs();
|
long rendererBufferedPositionUs = trackStreamWrapper.getBufferedPositionUs();
|
||||||
if (rendererBufferedPositionUs == C.UNSET_TIME_US) {
|
if (rendererBufferedPositionUs != C.END_OF_SOURCE_US) {
|
||||||
return C.UNSET_TIME_US;
|
|
||||||
} else if (rendererBufferedPositionUs == C.END_OF_SOURCE_US) {
|
|
||||||
// This wrapper is fully buffered.
|
|
||||||
} else {
|
|
||||||
bufferedPositionUs = Math.min(bufferedPositionUs, rendererBufferedPositionUs);
|
bufferedPositionUs = Math.min(bufferedPositionUs, rendererBufferedPositionUs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bufferedPositionUs == Long.MAX_VALUE ? C.UNSET_TIME_US : bufferedPositionUs;
|
return bufferedPositionUs == Long.MAX_VALUE ? C.END_OF_SOURCE_US : bufferedPositionUs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void seekToUs(long positionUs) {
|
public void seekToUs(long positionUs) {
|
||||||
|
// Treat all seeks into non-seekable media as being to t=0.
|
||||||
|
positionUs = isLive ? 0 : positionUs;
|
||||||
|
lastSeekPositionUs = positionUs;
|
||||||
|
pendingReset = true;
|
||||||
|
timestampAdjusterProvider.reset();
|
||||||
for (HlsTrackStreamWrapper trackStreamWrapper : enabledTrackStreamWrappers) {
|
for (HlsTrackStreamWrapper trackStreamWrapper : enabledTrackStreamWrappers) {
|
||||||
|
trackStreamWrapper.setReadingEnabled(false);
|
||||||
trackStreamWrapper.seekToUs(positionUs);
|
trackStreamWrapper.seekToUs(positionUs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -239,13 +224,92 @@ public final class HlsSampleSource implements SampleSource {
|
||||||
@Override
|
@Override
|
||||||
public void release() {
|
public void release() {
|
||||||
manifestFetcher.release();
|
manifestFetcher.release();
|
||||||
for (HlsTrackStreamWrapper trackStreamWrapper : trackStreamWrappers) {
|
if (trackStreamWrappers != null) {
|
||||||
trackStreamWrapper.release();
|
for (HlsTrackStreamWrapper trackStreamWrapper : trackStreamWrappers) {
|
||||||
|
trackStreamWrapper.release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal methods.
|
// Internal methods.
|
||||||
|
|
||||||
|
private List<HlsTrackStreamWrapper> buildTrackStreamWrappers(HlsPlaylist playlist) {
|
||||||
|
ArrayList<HlsTrackStreamWrapper> trackStreamWrappers = new ArrayList<>();
|
||||||
|
String baseUri = playlist.baseUri;
|
||||||
|
|
||||||
|
if (playlist instanceof HlsMediaPlaylist) {
|
||||||
|
Format format = Format.createContainerFormat("0", MimeTypes.APPLICATION_M3U8, null,
|
||||||
|
Format.NO_VALUE);
|
||||||
|
Variant[] variants = new Variant[] {new Variant(playlist.baseUri, format, null)};
|
||||||
|
trackStreamWrappers.add(buildTrackStreamWrapper(C.TRACK_TYPE_DEFAULT, baseUri, variants,
|
||||||
|
new FormatEvaluator.AdaptiveEvaluator(bandwidthMeter), C.DEFAULT_MUXED_BUFFER_SIZE,
|
||||||
|
null, null));
|
||||||
|
return trackStreamWrappers;
|
||||||
|
}
|
||||||
|
|
||||||
|
HlsMasterPlaylist masterPlaylist = (HlsMasterPlaylist) playlist;
|
||||||
|
|
||||||
|
// Build the default stream wrapper.
|
||||||
|
List<Variant> selectedVariants = new ArrayList<>(masterPlaylist.variants);
|
||||||
|
ArrayList<Variant> definiteVideoVariants = new ArrayList<>();
|
||||||
|
ArrayList<Variant> definiteAudioOnlyVariants = new ArrayList<>();
|
||||||
|
for (int i = 0; i < selectedVariants.size(); i++) {
|
||||||
|
Variant variant = selectedVariants.get(i);
|
||||||
|
if (variant.format.height > 0 || variantHasExplicitCodecWithPrefix(variant, "avc")) {
|
||||||
|
definiteVideoVariants.add(variant);
|
||||||
|
} else if (variantHasExplicitCodecWithPrefix(variant, "mp4a")) {
|
||||||
|
definiteAudioOnlyVariants.add(variant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!definiteVideoVariants.isEmpty()) {
|
||||||
|
// We've identified some variants as definitely containing video. Assume variants within the
|
||||||
|
// master playlist are marked consistently, and hence that we have the full set. Filter out
|
||||||
|
// any other variants, which are likely to be audio only.
|
||||||
|
selectedVariants = definiteVideoVariants;
|
||||||
|
} else if (definiteAudioOnlyVariants.size() < selectedVariants.size()) {
|
||||||
|
// We've identified some variants, but not all, as being audio only. Filter them out to leave
|
||||||
|
// the remaining variants, which are likely to contain video.
|
||||||
|
selectedVariants.removeAll(definiteAudioOnlyVariants);
|
||||||
|
} else {
|
||||||
|
// Leave the enabled variants unchanged. They're likely either all video or all audio.
|
||||||
|
}
|
||||||
|
Variant[] variants = new Variant[selectedVariants.size()];
|
||||||
|
selectedVariants.toArray(variants);
|
||||||
|
trackStreamWrappers.add(buildTrackStreamWrapper(C.TRACK_TYPE_DEFAULT, baseUri, variants,
|
||||||
|
new FormatEvaluator.AdaptiveEvaluator(bandwidthMeter), C.DEFAULT_MUXED_BUFFER_SIZE,
|
||||||
|
masterPlaylist.muxedAudioFormat, masterPlaylist.muxedCaptionFormat));
|
||||||
|
|
||||||
|
// Build the audio stream wrapper if applicable.
|
||||||
|
List<Variant> audioVariants = masterPlaylist.audios;
|
||||||
|
if (!audioVariants.isEmpty()) {
|
||||||
|
variants = new Variant[audioVariants.size()];
|
||||||
|
audioVariants.toArray(variants);
|
||||||
|
trackStreamWrappers.add(buildTrackStreamWrapper(C.TRACK_TYPE_AUDIO, baseUri, variants, null,
|
||||||
|
C.DEFAULT_AUDIO_BUFFER_SIZE, null, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the text stream wrapper if applicable.
|
||||||
|
List<Variant> subtitleVariants = masterPlaylist.subtitles;
|
||||||
|
if (!subtitleVariants.isEmpty()) {
|
||||||
|
variants = new Variant[subtitleVariants.size()];
|
||||||
|
subtitleVariants.toArray(variants);
|
||||||
|
trackStreamWrappers.add(buildTrackStreamWrapper(C.TRACK_TYPE_TEXT, baseUri, variants, null,
|
||||||
|
C.DEFAULT_TEXT_BUFFER_SIZE, null, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
return trackStreamWrappers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HlsTrackStreamWrapper buildTrackStreamWrapper(int trackType, String baseUri,
|
||||||
|
Variant[] variants, FormatEvaluator formatEvaluator, int bufferSize, Format muxedAudioFormat,
|
||||||
|
Format muxedCaptionFormat) {
|
||||||
|
DataSource dataSource = dataSourceFactory.createDataSource(bandwidthMeter);
|
||||||
|
HlsChunkSource defaultChunkSource = new HlsChunkSource(baseUri, variants, dataSource,
|
||||||
|
timestampAdjusterProvider, formatEvaluator);
|
||||||
|
return new HlsTrackStreamWrapper(defaultChunkSource, loadControl, bufferSize, muxedAudioFormat,
|
||||||
|
muxedCaptionFormat, eventHandler, eventListener, trackType, MIN_LOADABLE_RETRY_COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
private int selectTracks(HlsTrackStreamWrapper trackStreamWrapper,
|
private int selectTracks(HlsTrackStreamWrapper trackStreamWrapper,
|
||||||
List<TrackStream> allOldStreams, List<TrackSelection> allNewSelections, long positionUs,
|
List<TrackStream> allOldStreams, List<TrackSelection> allNewSelections, long positionUs,
|
||||||
TrackStream[] allNewStreams) {
|
TrackStream[] allNewStreams) {
|
||||||
|
|
@ -295,4 +359,18 @@ public final class HlsSampleSource implements SampleSource {
|
||||||
throw new IndexOutOfBoundsException();
|
throw new IndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean variantHasExplicitCodecWithPrefix(Variant variant, String prefix) {
|
||||||
|
String codecs = variant.codecs;
|
||||||
|
if (TextUtils.isEmpty(codecs)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String[] codecArray = codecs.split("(\\s*,\\s*)|(\\s*$)");
|
||||||
|
for (String codec : codecArray) {
|
||||||
|
if (codec.startsWith(prefix)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,12 +62,14 @@ import java.util.List;
|
||||||
private final ChunkHolder nextChunkHolder;
|
private final ChunkHolder nextChunkHolder;
|
||||||
private final EventDispatcher eventDispatcher;
|
private final EventDispatcher eventDispatcher;
|
||||||
private final LoadControl loadControl;
|
private final LoadControl loadControl;
|
||||||
|
private final Format muxedAudioFormat;
|
||||||
|
private final Format muxedCaptionFormat;
|
||||||
|
|
||||||
private volatile boolean sampleQueuesBuilt;
|
private volatile boolean sampleQueuesBuilt;
|
||||||
|
|
||||||
private boolean prepared;
|
private boolean prepared;
|
||||||
private boolean seenFirstTrackSelection;
|
private boolean seenFirstTrackSelection;
|
||||||
private boolean notifyReset;
|
private boolean readingEnabled;
|
||||||
private int enabledTrackCount;
|
private int enabledTrackCount;
|
||||||
private Format downstreamFormat;
|
private Format downstreamFormat;
|
||||||
|
|
||||||
|
|
@ -88,6 +90,10 @@ import java.util.List;
|
||||||
* @param chunkSource A {@link HlsChunkSource} from which chunks to load are obtained.
|
* @param chunkSource A {@link HlsChunkSource} from which chunks to load are obtained.
|
||||||
* @param loadControl Controls when the source is permitted to load data.
|
* @param loadControl Controls when the source is permitted to load data.
|
||||||
* @param bufferSizeContribution The contribution of this source to the media buffer, in bytes.
|
* @param bufferSizeContribution The contribution of this source to the media buffer, in bytes.
|
||||||
|
* @param muxedAudioFormat If HLS master playlist indicates that the stream contains muxed audio,
|
||||||
|
* this is the audio {@link Format} as defined by the playlist.
|
||||||
|
* @param muxedAudioFormat If HLS master playlist indicates that the stream contains muxed
|
||||||
|
* captions, this is the audio {@link Format} as defined by the playlist.
|
||||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
||||||
* null if delivery of events is not required.
|
* null if delivery of events is not required.
|
||||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||||
|
|
@ -96,16 +102,20 @@ import java.util.List;
|
||||||
* before propagating an error.
|
* before propagating an error.
|
||||||
*/
|
*/
|
||||||
public HlsTrackStreamWrapper(HlsChunkSource chunkSource, LoadControl loadControl,
|
public HlsTrackStreamWrapper(HlsChunkSource chunkSource, LoadControl loadControl,
|
||||||
int bufferSizeContribution, Handler eventHandler,
|
int bufferSizeContribution, Format muxedAudioFormat, Format muxedCaptionFormat,
|
||||||
ChunkTrackStreamEventListener eventListener, int eventSourceId, int minLoadableRetryCount) {
|
Handler eventHandler, ChunkTrackStreamEventListener eventListener, int eventSourceId,
|
||||||
|
int minLoadableRetryCount) {
|
||||||
this.chunkSource = chunkSource;
|
this.chunkSource = chunkSource;
|
||||||
this.loadControl = loadControl;
|
this.loadControl = loadControl;
|
||||||
this.bufferSizeContribution = bufferSizeContribution;
|
this.bufferSizeContribution = bufferSizeContribution;
|
||||||
|
this.muxedAudioFormat = muxedAudioFormat;
|
||||||
|
this.muxedCaptionFormat = muxedCaptionFormat;
|
||||||
loader = new Loader("Loader:HlsTrackStreamWrapper", minLoadableRetryCount);
|
loader = new Loader("Loader:HlsTrackStreamWrapper", minLoadableRetryCount);
|
||||||
eventDispatcher = new EventDispatcher(eventHandler, eventListener, eventSourceId);
|
eventDispatcher = new EventDispatcher(eventHandler, eventListener, eventSourceId);
|
||||||
nextChunkHolder = new ChunkHolder();
|
nextChunkHolder = new ChunkHolder();
|
||||||
sampleQueues = new SparseArray<>();
|
sampleQueues = new SparseArray<>();
|
||||||
mediaChunks = new LinkedList<>();
|
mediaChunks = new LinkedList<>();
|
||||||
|
readingEnabled = true;
|
||||||
pendingResetPositionUs = C.UNSET_TIME_US;
|
pendingResetPositionUs = C.UNSET_TIME_US;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -150,6 +160,10 @@ import java.util.List;
|
||||||
return chunkSource.getDurationUs();
|
return chunkSource.getDurationUs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isLive() {
|
||||||
|
return chunkSource.isLive();
|
||||||
|
}
|
||||||
|
|
||||||
public TrackGroupArray getTrackGroups() {
|
public TrackGroupArray getTrackGroups() {
|
||||||
return trackGroups;
|
return trackGroups;
|
||||||
}
|
}
|
||||||
|
|
@ -211,12 +225,8 @@ import java.util.List;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public long readReset() {
|
public void setReadingEnabled(boolean readingEnabled) {
|
||||||
if (notifyReset) {
|
this.readingEnabled = readingEnabled;
|
||||||
notifyReset = false;
|
|
||||||
return lastSeekPositionUs;
|
|
||||||
}
|
|
||||||
return C.UNSET_TIME_US;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getBufferedPositionUs() {
|
public long getBufferedPositionUs() {
|
||||||
|
|
@ -265,7 +275,7 @@ import java.util.List;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ int readData(int group, FormatHolder formatHolder, DecoderInputBuffer buffer) {
|
/* package */ int readData(int group, FormatHolder formatHolder, DecoderInputBuffer buffer) {
|
||||||
if (notifyReset || isPendingReset()) {
|
if (!readingEnabled || isPendingReset()) {
|
||||||
return TrackStream.NOTHING_READ;
|
return TrackStream.NOTHING_READ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -449,9 +459,9 @@ import java.util.List;
|
||||||
Format trackFormat = null;
|
Format trackFormat = null;
|
||||||
if (primaryExtractorTrackType == PRIMARY_TYPE_VIDEO) {
|
if (primaryExtractorTrackType == PRIMARY_TYPE_VIDEO) {
|
||||||
if (MimeTypes.isAudio(sampleFormat.sampleMimeType)) {
|
if (MimeTypes.isAudio(sampleFormat.sampleMimeType)) {
|
||||||
trackFormat = chunkSource.getMuxedAudioFormat();
|
trackFormat = muxedAudioFormat;
|
||||||
} else if (MimeTypes.APPLICATION_EIA608.equals(sampleFormat.sampleMimeType)) {
|
} else if (MimeTypes.APPLICATION_EIA608.equals(sampleFormat.sampleMimeType)) {
|
||||||
trackFormat = chunkSource.getMuxedCaptionFormat();
|
trackFormat = muxedCaptionFormat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trackGroups[i] = new TrackGroup(getSampleFormat(trackFormat, sampleFormat));
|
trackGroups[i] = new TrackGroup(getSampleFormat(trackFormat, sampleFormat));
|
||||||
|
|
@ -494,32 +504,9 @@ import java.util.List;
|
||||||
* @param positionUs The position to seek to.
|
* @param positionUs The position to seek to.
|
||||||
*/
|
*/
|
||||||
private void seekToInternal(long positionUs) {
|
private void seekToInternal(long positionUs) {
|
||||||
// Treat all seeks into non-seekable media as being to t=0.
|
|
||||||
positionUs = chunkSource.isLive() ? 0 : positionUs;
|
|
||||||
lastSeekPositionUs = positionUs;
|
lastSeekPositionUs = positionUs;
|
||||||
downstreamPositionUs = positionUs;
|
downstreamPositionUs = positionUs;
|
||||||
notifyReset = true;
|
restartFrom(positionUs);
|
||||||
boolean seekInsideBuffer = !isPendingReset();
|
|
||||||
// TODO[REFACTOR]: This will nearly always fail to seek inside all buffers due to sparse tracks
|
|
||||||
// such as ID3 (probably EIA608 too). We need a way to not care if we can't seek to the keyframe
|
|
||||||
// before for such tracks. For ID3 we probably explicitly don't want the keyframe before, even
|
|
||||||
// if we do have it, since it might be quite a long way behind the seek position. We probably
|
|
||||||
// only want to output ID3 buffers whose timestamps are greater than or equal to positionUs.
|
|
||||||
int sampleQueueCount = sampleQueues.size();
|
|
||||||
for (int i = 0; seekInsideBuffer && i < sampleQueueCount; i++) {
|
|
||||||
if (groupEnabledStates[i]) {
|
|
||||||
seekInsideBuffer = sampleQueues.valueAt(i).skipToKeyframeBefore(positionUs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (seekInsideBuffer) {
|
|
||||||
while (mediaChunks.size() > 1 && mediaChunks.get(1).startTimeUs <= positionUs) {
|
|
||||||
mediaChunks.removeFirst();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If we failed to seek within the sample queues, we need to restart.
|
|
||||||
chunkSource.seek();
|
|
||||||
restartFrom(positionUs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void discardSamplesForDisabledTracks() {
|
private void discardSamplesForDisabledTracks() {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue