mirror of
https://github.com/samsonjs/media.git
synced 2026-03-26 09:35:47 +00:00
Introduce PtsTimestampAdjusterProvider for HLS WebVTT.
This allows the same adjusters to be used by multiple HlsChunkSource instances. This is necessary because WebVTT chunks will be loaded by a second chunk source to the one loading audio/video. In both cases the same timestamp adjustments will need to be applied. Each source may transition from one discontinuity sequence to the next at a slightly different time, so it's necessary to maintain a separate adjuster for each sequence. An adjuster can only be initialized correctly using audio/video and not WebVTT, because the start time in a WebVTT file in HLS doesn't necessarily correspond to the chunk start time, which means the timestamp offset calculated by the adjuster could end up being incorrect. Hence sources providing WebVTT chunks will set isMasterSource to false. Lovely, right :(? Issue: #151 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=111693126
This commit is contained in:
parent
86c4fb3ac2
commit
00f8deda3d
5 changed files with 105 additions and 16 deletions
|
|
@ -30,6 +30,7 @@ import com.google.android.exoplayer.hls.HlsMasterPlaylist;
|
|||
import com.google.android.exoplayer.hls.HlsPlaylist;
|
||||
import com.google.android.exoplayer.hls.HlsPlaylistParser;
|
||||
import com.google.android.exoplayer.hls.HlsSampleSource;
|
||||
import com.google.android.exoplayer.hls.PtsTimestampAdjusterProvider;
|
||||
import com.google.android.exoplayer.metadata.Id3Parser;
|
||||
import com.google.android.exoplayer.metadata.MetadataTrackRenderer;
|
||||
import com.google.android.exoplayer.text.eia608.Eia608TrackRenderer;
|
||||
|
|
@ -147,7 +148,7 @@ public class HlsRendererBuilder implements RendererBuilder {
|
|||
|
||||
DataSource dataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
|
||||
HlsChunkSource chunkSource = new HlsChunkSource(dataSource, url, manifest, bandwidthMeter,
|
||||
variantIndices, HlsChunkSource.ADAPTIVE_MODE_SPLICE);
|
||||
new PtsTimestampAdjusterProvider(), variantIndices, HlsChunkSource.ADAPTIVE_MODE_SPLICE);
|
||||
HlsSampleSource sampleSource = new HlsSampleSource(chunkSource, loadControl,
|
||||
BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, DemoPlayer.TYPE_VIDEO);
|
||||
MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context,
|
||||
|
|
|
|||
|
|
@ -37,7 +37,9 @@ public final class PtsTimestampAdjuster {
|
|||
private final long firstSampleTimestampUs;
|
||||
|
||||
private long timestampOffsetUs;
|
||||
private long lastPts;
|
||||
|
||||
// Volatile to allow isInitialized to be called on a different thread to adjustTimestamp.
|
||||
private volatile long lastPts;
|
||||
|
||||
/**
|
||||
* @param firstSampleTimestampUs The desired result of the first call to
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ public class HlsChunkSource {
|
|||
private final DataSource dataSource;
|
||||
private final HlsPlaylistParser playlistParser;
|
||||
private final BandwidthMeter bandwidthMeter;
|
||||
private final PtsTimestampAdjusterProvider timestampAdjusterProvider;
|
||||
private final int adaptiveMode;
|
||||
private final String baseUri;
|
||||
private final int adaptiveMaxWidth;
|
||||
|
|
@ -145,24 +146,43 @@ public class HlsChunkSource {
|
|||
private boolean live;
|
||||
private long durationUs;
|
||||
private IOException fatalError;
|
||||
private PtsTimestampAdjuster ptsTimestampAdjuster;
|
||||
|
||||
private Uri encryptionKeyUri;
|
||||
private byte[] encryptionKey;
|
||||
private String encryptionIvString;
|
||||
private byte[] encryptionIv;
|
||||
|
||||
/**
|
||||
* @param dataSource A {@link DataSource} suitable for loading the media data.
|
||||
* @param playlistUrl The playlist URL.
|
||||
* @param playlist The hls playlist.
|
||||
* @param bandwidthMeter Provides an estimate of the currently available bandwidth.
|
||||
* @param timestampAdjusterProvider A provider of {@link PtsTimestampAdjuster} instances. If
|
||||
* multiple {@link HlsChunkSource}s are used for a single playback, they should all share the
|
||||
* same provider.
|
||||
* @param variantIndices If {@code playlist} is a {@link HlsMasterPlaylist}, the subset of variant
|
||||
* indices to consider, or null to consider all of the variants. For other playlist types
|
||||
* this parameter is ignored.
|
||||
* @param adaptiveMode The mode for switching from one variant to another. One of
|
||||
* {@link #ADAPTIVE_MODE_NONE}, {@link #ADAPTIVE_MODE_ABRUPT} and
|
||||
* {@link #ADAPTIVE_MODE_SPLICE}.
|
||||
*/
|
||||
public HlsChunkSource(DataSource dataSource, String playlistUrl, HlsPlaylist playlist,
|
||||
BandwidthMeter bandwidthMeter, int[] variantIndices, int adaptiveMode) {
|
||||
this(dataSource, playlistUrl, playlist, bandwidthMeter, variantIndices, adaptiveMode,
|
||||
DEFAULT_MIN_BUFFER_TO_SWITCH_UP_MS, DEFAULT_MAX_BUFFER_TO_SWITCH_DOWN_MS);
|
||||
BandwidthMeter bandwidthMeter, PtsTimestampAdjusterProvider timestampAdjusterProvider,
|
||||
int[] variantIndices, int adaptiveMode) {
|
||||
this(dataSource, playlistUrl, playlist, bandwidthMeter, timestampAdjusterProvider,
|
||||
variantIndices, adaptiveMode, DEFAULT_MIN_BUFFER_TO_SWITCH_UP_MS,
|
||||
DEFAULT_MAX_BUFFER_TO_SWITCH_DOWN_MS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dataSource A {@link DataSource} suitable for loading the media data.
|
||||
* @param playlistUrl The playlist URL.
|
||||
* @param playlist The hls playlist.
|
||||
* @param bandwidthMeter provides an estimate of the currently available bandwidth.
|
||||
* @param bandwidthMeter Provides an estimate of the currently available bandwidth.
|
||||
* @param timestampAdjusterProvider A provider of {@link PtsTimestampAdjuster} instances. If
|
||||
* multiple {@link HlsChunkSource}s are used for a single playback, they should all share the
|
||||
* same provider.
|
||||
* @param variantIndices If {@code playlist} is a {@link HlsMasterPlaylist}, the subset of variant
|
||||
* indices to consider, or null to consider all of the variants. For other playlist types
|
||||
* this parameter is ignored.
|
||||
|
|
@ -175,10 +195,12 @@ public class HlsChunkSource {
|
|||
* for a switch to a lower quality variant to be considered.
|
||||
*/
|
||||
public HlsChunkSource(DataSource dataSource, String playlistUrl, HlsPlaylist playlist,
|
||||
BandwidthMeter bandwidthMeter, int[] variantIndices, int adaptiveMode,
|
||||
long minBufferDurationToSwitchUpMs, long maxBufferDurationToSwitchDownMs) {
|
||||
BandwidthMeter bandwidthMeter, PtsTimestampAdjusterProvider timestampAdjusterProvider,
|
||||
int[] variantIndices, int adaptiveMode, long minBufferDurationToSwitchUpMs,
|
||||
long maxBufferDurationToSwitchDownMs) {
|
||||
this.dataSource = dataSource;
|
||||
this.bandwidthMeter = bandwidthMeter;
|
||||
this.timestampAdjusterProvider = timestampAdjusterProvider;
|
||||
this.adaptiveMode = adaptiveMode;
|
||||
minBufferDurationToSwitchUpUs = minBufferDurationToSwitchUpMs * 1000;
|
||||
maxBufferDurationToSwitchDownUs = maxBufferDurationToSwitchDownMs * 1000;
|
||||
|
|
@ -353,6 +375,9 @@ public class HlsChunkSource {
|
|||
// Configure the extractor that will read the chunk.
|
||||
HlsExtractorWrapper extractorWrapper;
|
||||
if (chunkUri.getLastPathSegment().endsWith(AAC_FILE_EXTENSION)) {
|
||||
// TODO: Inject a timestamp adjuster and use it along with ID3 PRIV tag values with owner
|
||||
// identifier com.apple.streaming.transportStreamTimestamp. This may also apply to the MP3
|
||||
// case below.
|
||||
Extractor extractor = new AdtsExtractor(startTimeUs);
|
||||
extractorWrapper = new HlsExtractorWrapper(trigger, format, startTimeUs, extractor,
|
||||
switchingVariantSpliced, MediaFormat.NO_VALUE, MediaFormat.NO_VALUE);
|
||||
|
|
@ -364,13 +389,9 @@ public class HlsChunkSource {
|
|||
|| previousTsChunk.discontinuitySequenceNumber != segment.discontinuitySequenceNumber
|
||||
|| !format.equals(previousTsChunk.format)) {
|
||||
// MPEG-2 TS segments, but we need a new extractor.
|
||||
if (previousTsChunk == null
|
||||
|| previousTsChunk.discontinuitySequenceNumber != segment.discontinuitySequenceNumber) {
|
||||
// TODO: Use this for AAC as well, along with the ID3 PRIV priv tag values with owner
|
||||
// identifier com.apple.streaming.transportStreamTimestamp.
|
||||
ptsTimestampAdjuster = new PtsTimestampAdjuster(startTimeUs);
|
||||
}
|
||||
Extractor extractor = new TsExtractor(ptsTimestampAdjuster);
|
||||
PtsTimestampAdjuster timestampAdjuster = timestampAdjusterProvider.getAdjuster(true,
|
||||
segment.discontinuitySequenceNumber, startTimeUs);
|
||||
Extractor extractor = new TsExtractor(timestampAdjuster);
|
||||
extractorWrapper = new HlsExtractorWrapper(trigger, format, startTimeUs, extractor,
|
||||
switchingVariantSpliced, adaptiveMaxWidth, adaptiveMaxHeight);
|
||||
} else {
|
||||
|
|
@ -454,6 +475,7 @@ public class HlsChunkSource {
|
|||
}
|
||||
|
||||
public void reset() {
|
||||
timestampAdjusterProvider.reset();
|
||||
fatalError = null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -358,6 +358,7 @@ public final class HlsSampleSource implements SampleSource, SampleSourceReader,
|
|||
for (int i = 0; i < pendingDiscontinuities.length; i++) {
|
||||
pendingDiscontinuities[i] = true;
|
||||
}
|
||||
chunkSource.reset();
|
||||
restartFrom(positionUs);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (C) 2014 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.exoplayer.hls;
|
||||
|
||||
import com.google.android.exoplayer.extractor.ts.PtsTimestampAdjuster;
|
||||
|
||||
import android.util.SparseArray;
|
||||
|
||||
/**
|
||||
* Provides {@link PtsTimestampAdjuster} instances for use during HLS playbacks.
|
||||
*/
|
||||
public final class PtsTimestampAdjusterProvider {
|
||||
|
||||
// TODO: Prevent this array from growing indefinitely large by removing adjusters that are no
|
||||
// longer required.
|
||||
private final SparseArray<PtsTimestampAdjuster> ptsTimestampAdjusters;
|
||||
|
||||
public PtsTimestampAdjusterProvider() {
|
||||
ptsTimestampAdjusters = new SparseArray<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@link PtsTimestampAdjuster} suitable for adjusting the pts timestamps contained in
|
||||
* a chunk with a given discontinuity sequence.
|
||||
* <p>
|
||||
* This method may return null if the master source has yet to initialize a suitable adjuster.
|
||||
*
|
||||
* @param isMasterSource True if the calling chunk source is the master.
|
||||
* @param discontinuitySequence The chunk's discontinuity sequence.
|
||||
* @param startTimeUs The chunk's start time.
|
||||
* @return A {@link PtsTimestampAdjuster}.
|
||||
*/
|
||||
public PtsTimestampAdjuster getAdjuster(boolean isMasterSource, int discontinuitySequence,
|
||||
long startTimeUs) {
|
||||
PtsTimestampAdjuster adjuster = ptsTimestampAdjusters.get(discontinuitySequence);
|
||||
if (isMasterSource && adjuster == null) {
|
||||
adjuster = new PtsTimestampAdjuster(startTimeUs);
|
||||
ptsTimestampAdjusters.put(discontinuitySequence, adjuster);
|
||||
}
|
||||
return isMasterSource || (adjuster != null && adjuster.isInitialized()) ? adjuster : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the provider.
|
||||
*/
|
||||
public void reset() {
|
||||
ptsTimestampAdjusters.clear();
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in a new issue