Ensure only timestamp adjustment masters set first sample timestamps

Without this, it is possible that a non timestamp master instances
the adjuster with its own chunk start time. When chunks are not
aligned, this breaks adjustment.

Issue:#2424

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=149764488
This commit is contained in:
aquilescanta 2017-03-10 09:19:54 -08:00 committed by Oliver Woodman
parent 15aad266b6
commit 952bde700b
6 changed files with 35 additions and 11 deletions

View File

@ -383,7 +383,8 @@ public final class TsExtractor implements Extractor {
if (mode == MODE_SINGLE_PMT || mode == MODE_HLS || remainingPmts == 1) {
timestampAdjuster = timestampAdjusters.get(0);
} else {
timestampAdjuster = new TimestampAdjuster(timestampAdjusters.get(0).firstSampleTimestampUs);
timestampAdjuster = new TimestampAdjuster(
timestampAdjusters.get(0).getFirstSampleTimestampUs());
timestampAdjusters.add(timestampAdjuster);
}

View File

@ -88,6 +88,9 @@ public final class SpliceInfoDecoder implements MetadataDecoder {
case TYPE_PRIVATE_COMMAND:
command = PrivateCommand.parseFromSection(sectionData, spliceCommandLength, ptsAdjustment);
break;
default:
// Do nothing.
break;
}
return command == null ? new Metadata() : new Metadata(command);
}

View File

@ -282,7 +282,7 @@ import java.util.Locale;
int discontinuitySequence = mediaPlaylist.discontinuitySequence
+ segment.relativeDiscontinuitySequence;
TimestampAdjuster timestampAdjuster = timestampAdjusterProvider.getAdjuster(
discontinuitySequence, startTimeUs);
discontinuitySequence);
// Configure the data source and spec for the chunk.
Uri chunkUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, segment.url);

View File

@ -242,6 +242,9 @@ import java.util.concurrent.atomic.AtomicInteger;
}
if (!isMasterTimestampSource) {
timestampAdjuster.waitUntilInitialized();
} else if (timestampAdjuster.getFirstSampleTimestampUs() == TimestampAdjuster.DO_NOT_OFFSET) {
// We're the master and we haven't set the desired first sample timestamp yet.
timestampAdjuster.setFirstSampleTimestampUs(startTimeUs);
}
try {
ExtractorInput input = new DefaultExtractorInput(dataSource,

View File

@ -36,13 +36,12 @@ public final class TimestampAdjusterProvider {
* a chunk with a given discontinuity sequence.
*
* @param discontinuitySequence The chunk's discontinuity sequence.
* @param startTimeUs The chunk's start time.
* @return A {@link TimestampAdjuster}.
*/
public TimestampAdjuster getAdjuster(int discontinuitySequence, long startTimeUs) {
public TimestampAdjuster getAdjuster(int discontinuitySequence) {
TimestampAdjuster adjuster = timestampAdjusters.get(discontinuitySequence);
if (adjuster == null) {
adjuster = new TimestampAdjuster(startTimeUs);
adjuster = new TimestampAdjuster(TimestampAdjuster.DO_NOT_OFFSET);
timestampAdjusters.put(discontinuitySequence, adjuster);
}
return adjuster;

View File

@ -34,21 +34,39 @@ public final class TimestampAdjuster {
*/
private static final long MAX_PTS_PLUS_ONE = 0x200000000L;
public final long firstSampleTimestampUs;
private long firstSampleTimestampUs;
private long timestampOffsetUs;
// Volatile to allow isInitialized to be called on a different thread to adjustSampleTimestamp.
private volatile long lastSampleTimestamp;
/**
* @param firstSampleTimestampUs The desired result of the first call to
* {@link #adjustSampleTimestamp(long)}, or {@link #DO_NOT_OFFSET} if presentation timestamps
* should not be offset.
* @param firstSampleTimestampUs See {@link #setFirstSampleTimestampUs(long)}.
*/
public TimestampAdjuster(long firstSampleTimestampUs) {
this.firstSampleTimestampUs = firstSampleTimestampUs;
lastSampleTimestamp = C.TIME_UNSET;
setFirstSampleTimestampUs(firstSampleTimestampUs);
}
/**
* Sets the desired result of the first call to {@link #adjustSampleTimestamp(long)}. Can only be
* called before any timestamps have been adjusted.
*
* @param firstSampleTimestampUs The first adjusted sample timestamp in microseconds, or
* {@link #DO_NOT_OFFSET} if presentation timestamps should not be offset.
*/
public synchronized void setFirstSampleTimestampUs(long firstSampleTimestampUs) {
Assertions.checkState(lastSampleTimestamp == C.TIME_UNSET);
this.firstSampleTimestampUs = firstSampleTimestampUs;
}
/**
* Returns the first adjusted sample timestamp in microseconds.
*
* @return The first adjusted sample timestamp in microseconds.
*/
public long getFirstSampleTimestampUs() {
return firstSampleTimestampUs;
}
/**