mirror of
https://github.com/androidx/media.git
synced 2025-05-09 00:20:45 +08:00
Chose timestamp adjustment master based on track selection
Select the timestamp master depending on track availability. If a variant is being loaded, then that is the timestmap master. Otherwise, if an audio track is being loaded, then the responsible chunk source is the timestmap master. If no variant or audio rendition is enabled, then a subtitle chunk source is selected as timestamp master. This CL will become specially relevant once ID3 PRIV timestamps are used for audio renditions. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=140201385
This commit is contained in:
parent
ce9ec79e59
commit
9c612f94c5
@ -103,6 +103,7 @@ import java.util.Locale;
|
|||||||
private final HlsPlaylistTracker playlistTracker;
|
private final HlsPlaylistTracker playlistTracker;
|
||||||
private final TrackGroup trackGroup;
|
private final TrackGroup trackGroup;
|
||||||
|
|
||||||
|
private boolean isTimestampMaster;
|
||||||
private byte[] scratchSpace;
|
private byte[] scratchSpace;
|
||||||
private IOException fatalError;
|
private IOException fatalError;
|
||||||
|
|
||||||
@ -176,6 +177,16 @@ import java.util.Locale;
|
|||||||
fatalError = null;
|
fatalError = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether this chunk source is responsible for initializing timestamp adjusters.
|
||||||
|
*
|
||||||
|
* @param isTimestampMaster True if this chunk source is responsible for initializing timestamp
|
||||||
|
* adjusters.
|
||||||
|
*/
|
||||||
|
public void setIsTimestampMaster(boolean isTimestampMaster) {
|
||||||
|
this.isTimestampMaster = isTimestampMaster;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the next chunk to load.
|
* Returns the next chunk to load.
|
||||||
* <p>
|
* <p>
|
||||||
@ -280,7 +291,6 @@ import java.util.Locale;
|
|||||||
|| previous.discontinuitySequenceNumber != segment.discontinuitySequenceNumber
|
|| previous.discontinuitySequenceNumber != segment.discontinuitySequenceNumber
|
||||||
|| format != previous.trackFormat;
|
|| format != previous.trackFormat;
|
||||||
boolean extractorNeedsInit = true;
|
boolean extractorNeedsInit = true;
|
||||||
boolean isTimestampMaster = false;
|
|
||||||
TimestampAdjuster timestampAdjuster = null;
|
TimestampAdjuster timestampAdjuster = null;
|
||||||
String lastPathSegment = chunkUri.getLastPathSegment();
|
String lastPathSegment = chunkUri.getLastPathSegment();
|
||||||
if (lastPathSegment.endsWith(AAC_FILE_EXTENSION)) {
|
if (lastPathSegment.endsWith(AAC_FILE_EXTENSION)) {
|
||||||
@ -299,7 +309,6 @@ import java.util.Locale;
|
|||||||
startTimeUs);
|
startTimeUs);
|
||||||
extractor = new WebvttExtractor(format.language, timestampAdjuster);
|
extractor = new WebvttExtractor(format.language, timestampAdjuster);
|
||||||
} else if (lastPathSegment.endsWith(MP4_FILE_EXTENSION)) {
|
} else if (lastPathSegment.endsWith(MP4_FILE_EXTENSION)) {
|
||||||
isTimestampMaster = true;
|
|
||||||
if (needNewExtractor) {
|
if (needNewExtractor) {
|
||||||
timestampAdjuster = timestampAdjusterProvider.getAdjuster(
|
timestampAdjuster = timestampAdjusterProvider.getAdjuster(
|
||||||
segment.discontinuitySequenceNumber, startTimeUs);
|
segment.discontinuitySequenceNumber, startTimeUs);
|
||||||
@ -310,7 +319,6 @@ import java.util.Locale;
|
|||||||
}
|
}
|
||||||
} else if (needNewExtractor) {
|
} else if (needNewExtractor) {
|
||||||
// MPEG-2 TS segments, but we need a new extractor.
|
// MPEG-2 TS segments, but we need a new extractor.
|
||||||
isTimestampMaster = true;
|
|
||||||
timestampAdjuster = timestampAdjusterProvider.getAdjuster(
|
timestampAdjuster = timestampAdjusterProvider.getAdjuster(
|
||||||
segment.discontinuitySequenceNumber, startTimeUs);
|
segment.discontinuitySequenceNumber, startTimeUs);
|
||||||
// This flag ensures the change of pid between streams does not affect the sample queues.
|
// This flag ensures the change of pid between streams does not affect the sample queues.
|
||||||
|
@ -26,6 +26,7 @@ import com.google.android.exoplayer2.source.SampleStream;
|
|||||||
import com.google.android.exoplayer2.source.TrackGroup;
|
import com.google.android.exoplayer2.source.TrackGroup;
|
||||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||||
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist;
|
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist;
|
||||||
|
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl;
|
||||||
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistTracker;
|
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistTracker;
|
||||||
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||||
import com.google.android.exoplayer2.upstream.Allocator;
|
import com.google.android.exoplayer2.upstream.Allocator;
|
||||||
@ -166,6 +167,18 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
|
|||||||
// Update the local state.
|
// Update the local state.
|
||||||
enabledSampleStreamWrappers = new HlsSampleStreamWrapper[enabledSampleStreamWrapperList.size()];
|
enabledSampleStreamWrappers = new HlsSampleStreamWrapper[enabledSampleStreamWrapperList.size()];
|
||||||
enabledSampleStreamWrapperList.toArray(enabledSampleStreamWrappers);
|
enabledSampleStreamWrapperList.toArray(enabledSampleStreamWrappers);
|
||||||
|
|
||||||
|
// The first enabled sample stream wrapper is responsible for intializing the timestamp
|
||||||
|
// adjuster. This way, if present, variants are responsible. Otherwise, audio renditions are.
|
||||||
|
// If only subtitles are present, then text renditions are used for timestamp adjustment
|
||||||
|
// initialization.
|
||||||
|
if (enabledSampleStreamWrappers.length > 0) {
|
||||||
|
enabledSampleStreamWrappers[0].setIsTimestampMaster(true);
|
||||||
|
for (int i = 1; i < enabledSampleStreamWrappers.length; i++) {
|
||||||
|
enabledSampleStreamWrappers[i].setIsTimestampMaster(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sequenceableLoader = new CompositeSequenceableLoader(enabledSampleStreamWrappers);
|
sequenceableLoader = new CompositeSequenceableLoader(enabledSampleStreamWrappers);
|
||||||
if (seenFirstTrackSelection && selectedNewTracks) {
|
if (seenFirstTrackSelection && selectedNewTracks) {
|
||||||
seekToUs(positionUs);
|
seekToUs(positionUs);
|
||||||
@ -241,7 +254,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPlaylistRefreshRequired(HlsMasterPlaylist.HlsUrl url) {
|
public void onPlaylistRefreshRequired(HlsUrl url) {
|
||||||
playlistTracker.refreshPlaylist(url, this);
|
playlistTracker.refreshPlaylist(url, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,7 +282,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPlaylistLoadError(HlsMasterPlaylist.HlsUrl url, IOException error) {
|
public void onPlaylistLoadError(HlsUrl url, IOException error) {
|
||||||
for (HlsSampleStreamWrapper sampleStreamWrapper : enabledSampleStreamWrappers) {
|
for (HlsSampleStreamWrapper sampleStreamWrapper : enabledSampleStreamWrappers) {
|
||||||
sampleStreamWrapper.onPlaylistLoadError(url, error);
|
sampleStreamWrapper.onPlaylistLoadError(url, error);
|
||||||
}
|
}
|
||||||
@ -281,11 +294,11 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
|
|||||||
private void buildAndPrepareSampleStreamWrappers() {
|
private void buildAndPrepareSampleStreamWrappers() {
|
||||||
HlsMasterPlaylist masterPlaylist = playlistTracker.getMasterPlaylist();
|
HlsMasterPlaylist masterPlaylist = playlistTracker.getMasterPlaylist();
|
||||||
// Build the default stream wrapper.
|
// Build the default stream wrapper.
|
||||||
List<HlsMasterPlaylist.HlsUrl> selectedVariants = new ArrayList<>(masterPlaylist.variants);
|
List<HlsUrl> selectedVariants = new ArrayList<>(masterPlaylist.variants);
|
||||||
ArrayList<HlsMasterPlaylist.HlsUrl> definiteVideoVariants = new ArrayList<>();
|
ArrayList<HlsUrl> definiteVideoVariants = new ArrayList<>();
|
||||||
ArrayList<HlsMasterPlaylist.HlsUrl> definiteAudioOnlyVariants = new ArrayList<>();
|
ArrayList<HlsUrl> definiteAudioOnlyVariants = new ArrayList<>();
|
||||||
for (int i = 0; i < selectedVariants.size(); i++) {
|
for (int i = 0; i < selectedVariants.size(); i++) {
|
||||||
HlsMasterPlaylist.HlsUrl variant = selectedVariants.get(i);
|
HlsUrl variant = selectedVariants.get(i);
|
||||||
if (variant.format.height > 0 || variantHasExplicitCodecWithPrefix(variant, "avc")) {
|
if (variant.format.height > 0 || variantHasExplicitCodecWithPrefix(variant, "avc")) {
|
||||||
definiteVideoVariants.add(variant);
|
definiteVideoVariants.add(variant);
|
||||||
} else if (variantHasExplicitCodecWithPrefix(variant, "mp4a")) {
|
} else if (variantHasExplicitCodecWithPrefix(variant, "mp4a")) {
|
||||||
@ -304,41 +317,44 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
|
|||||||
} else {
|
} else {
|
||||||
// Leave the enabled variants unchanged. They're likely either all video or all audio.
|
// Leave the enabled variants unchanged. They're likely either all video or all audio.
|
||||||
}
|
}
|
||||||
List<HlsMasterPlaylist.HlsUrl> audioVariants = masterPlaylist.audios;
|
List<HlsUrl> audioRenditions = masterPlaylist.audios;
|
||||||
List<HlsMasterPlaylist.HlsUrl> subtitleVariants = masterPlaylist.subtitles;
|
List<HlsUrl> subtitleRenditions = masterPlaylist.subtitles;
|
||||||
sampleStreamWrappers = new HlsSampleStreamWrapper[(selectedVariants.isEmpty() ? 0 : 1)
|
sampleStreamWrappers = new HlsSampleStreamWrapper[1 /* variants */ + audioRenditions.size()
|
||||||
+ audioVariants.size() + subtitleVariants.size()];
|
+ subtitleRenditions.size()];
|
||||||
int currentWrapperIndex = 0;
|
int currentWrapperIndex = 0;
|
||||||
pendingPrepareCount = sampleStreamWrappers.length;
|
pendingPrepareCount = sampleStreamWrappers.length;
|
||||||
if (!selectedVariants.isEmpty()) {
|
|
||||||
HlsMasterPlaylist.HlsUrl[] variants = new HlsMasterPlaylist.HlsUrl[selectedVariants.size()];
|
Assertions.checkArgument(!selectedVariants.isEmpty());
|
||||||
selectedVariants.toArray(variants);
|
HlsUrl[] variants = new HlsMasterPlaylist.HlsUrl[selectedVariants.size()];
|
||||||
HlsSampleStreamWrapper sampleStreamWrapper = buildSampleStreamWrapper(C.TRACK_TYPE_DEFAULT,
|
selectedVariants.toArray(variants);
|
||||||
variants, masterPlaylist.muxedAudioFormat, masterPlaylist.muxedCaptionFormat);
|
HlsSampleStreamWrapper sampleStreamWrapper = buildSampleStreamWrapper(C.TRACK_TYPE_DEFAULT,
|
||||||
sampleStreamWrappers[currentWrapperIndex++] = sampleStreamWrapper;
|
variants, masterPlaylist.muxedAudioFormat, masterPlaylist.muxedCaptionFormat);
|
||||||
sampleStreamWrapper.continuePreparing();
|
sampleStreamWrappers[currentWrapperIndex++] = sampleStreamWrapper;
|
||||||
}
|
sampleStreamWrapper.setIsTimestampMaster(true);
|
||||||
|
sampleStreamWrapper.continuePreparing();
|
||||||
|
|
||||||
|
// TODO: Build video stream wrappers here.
|
||||||
|
|
||||||
// Build audio stream wrappers.
|
// Build audio stream wrappers.
|
||||||
for (int i = 0; i < audioVariants.size(); i++) {
|
for (int i = 0; i < audioRenditions.size(); i++) {
|
||||||
HlsSampleStreamWrapper sampleStreamWrapper = buildSampleStreamWrapper(C.TRACK_TYPE_AUDIO,
|
sampleStreamWrapper = buildSampleStreamWrapper(C.TRACK_TYPE_AUDIO,
|
||||||
new HlsMasterPlaylist.HlsUrl[] {audioVariants.get(i)}, null, null);
|
new HlsUrl[] {audioRenditions.get(i)}, null, null);
|
||||||
sampleStreamWrappers[currentWrapperIndex++] = sampleStreamWrapper;
|
sampleStreamWrappers[currentWrapperIndex++] = sampleStreamWrapper;
|
||||||
sampleStreamWrapper.continuePreparing();
|
sampleStreamWrapper.continuePreparing();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build subtitle stream wrappers.
|
// Build subtitle stream wrappers.
|
||||||
for (int i = 0; i < subtitleVariants.size(); i++) {
|
for (int i = 0; i < subtitleRenditions.size(); i++) {
|
||||||
HlsMasterPlaylist.HlsUrl url = subtitleVariants.get(i);
|
HlsUrl url = subtitleRenditions.get(i);
|
||||||
HlsSampleStreamWrapper sampleStreamWrapper = buildSampleStreamWrapper(C.TRACK_TYPE_TEXT,
|
sampleStreamWrapper = buildSampleStreamWrapper(C.TRACK_TYPE_TEXT, new HlsUrl[] {url}, null,
|
||||||
new HlsMasterPlaylist.HlsUrl[] {url}, null, null);
|
null);
|
||||||
sampleStreamWrapper.prepareSingleTrack(url.format);
|
sampleStreamWrapper.prepareSingleTrack(url.format);
|
||||||
sampleStreamWrappers[currentWrapperIndex++] = sampleStreamWrapper;
|
sampleStreamWrappers[currentWrapperIndex++] = sampleStreamWrapper;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private HlsSampleStreamWrapper buildSampleStreamWrapper(int trackType,
|
private HlsSampleStreamWrapper buildSampleStreamWrapper(int trackType, HlsUrl[] variants,
|
||||||
HlsMasterPlaylist.HlsUrl[] variants, Format muxedAudioFormat, Format muxedCaptionFormat) {
|
Format muxedAudioFormat, Format muxedCaptionFormat) {
|
||||||
DataSource dataSource = dataSourceFactory.createDataSource();
|
DataSource dataSource = dataSourceFactory.createDataSource();
|
||||||
HlsChunkSource defaultChunkSource = new HlsChunkSource(playlistTracker, variants, dataSource,
|
HlsChunkSource defaultChunkSource = new HlsChunkSource(playlistTracker, variants, dataSource,
|
||||||
timestampAdjusterProvider);
|
timestampAdjusterProvider);
|
||||||
@ -347,8 +363,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
|
|||||||
eventDispatcher);
|
eventDispatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean variantHasExplicitCodecWithPrefix(HlsMasterPlaylist.HlsUrl variant,
|
private static boolean variantHasExplicitCodecWithPrefix(HlsUrl variant, String prefix) {
|
||||||
String prefix) {
|
|
||||||
String codecs = variant.format.codecs;
|
String codecs = variant.format.codecs;
|
||||||
if (TextUtils.isEmpty(codecs)) {
|
if (TextUtils.isEmpty(codecs)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -275,6 +275,10 @@ import java.util.LinkedList;
|
|||||||
return largestQueuedTimestampUs;
|
return largestQueuedTimestampUs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setIsTimestampMaster(boolean isTimestampMaster) {
|
||||||
|
chunkSource.setIsTimestampMaster(isTimestampMaster);
|
||||||
|
}
|
||||||
|
|
||||||
public void onPlaylistLoadError(HlsUrl url, IOException error) {
|
public void onPlaylistLoadError(HlsUrl url, IOException error) {
|
||||||
chunkSource.onPlaylistLoadError(url, error);
|
chunkSource.onPlaylistLoadError(url, error);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user