mirror of
https://github.com/androidx/media.git
synced 2025-05-21 23:56:32 +08:00
Add HlsCheckedSampleQueue to check timstamp range
Add a SampleQueue subclass that checks the timestamp range of media samples queued to it and reports an exception on load if the timestamp is outside of spec bounds. (Smashed to a single commit prior to rebase)
This commit is contained in:
parent
7b82a3c889
commit
f78cbd2c9e
@ -488,7 +488,7 @@ public class SampleQueue implements TrackOutput {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void sampleMetadata(
|
||||
public void sampleMetadata(
|
||||
long timeUs,
|
||||
@C.BufferFlags int flags,
|
||||
int size,
|
||||
|
@ -0,0 +1,26 @@
|
||||
package com.google.android.exoplayer2.source;
|
||||
|
||||
import android.net.Uri;
|
||||
import com.google.android.exoplayer2.C;
|
||||
|
||||
/**
|
||||
* Thrown from the loader thread when an attempt is made to commit a sample that is far
|
||||
* deviant from the expected sequence of timestamps in the SampleStream. This is likely
|
||||
* caused by a discontinuity in a segment that was not split and reported by metadata in
|
||||
* an HLS (EXT-X-DISCONTINUITY) or DASH stream.
|
||||
*/
|
||||
public class UnreportedDiscontinuityException extends RuntimeException {
|
||||
|
||||
public final long timesUs;
|
||||
|
||||
/**
|
||||
* Consturct the exception
|
||||
*
|
||||
* @param timesUs last timestamp before attempted commit of the deviant sample
|
||||
* @param uri uri of the segment with the unreported discontinuity
|
||||
*/
|
||||
public UnreportedDiscontinuityException(long timesUs, Uri uri) {
|
||||
super("Unreported discontinuity timeMs: " + C.usToMs(timesUs) + " in URI: " + uri);
|
||||
this.timesUs = timesUs;
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer2.source.SampleQueue;
|
||||
import com.google.android.exoplayer2.source.UnreportedDiscontinuityException;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
import com.google.android.exoplayer2.util.Log;
|
||||
|
||||
public class HlsCheckedSampleQueue extends SampleQueue {
|
||||
private static final String TAG = "HlsCheckedSampleQueue";
|
||||
|
||||
private long lowestTimeUs = C.TIME_UNSET;
|
||||
private long highestTimeUs = C.TIME_UNSET;
|
||||
|
||||
private HlsMediaChunk chunk;
|
||||
private boolean loggedFirst = false;
|
||||
|
||||
HlsCheckedSampleQueue(Allocator allocator, DrmSessionManager drmSessionManager) {
|
||||
super(allocator, drmSessionManager);
|
||||
}
|
||||
|
||||
void setCurrentLoadingChunk(HlsMediaChunk chunk) {
|
||||
double tolerance = (chunk.endTimeUs - chunk.startTimeUs) * 0.1;
|
||||
this.lowestTimeUs = chunk.startTimeUs;
|
||||
this.highestTimeUs = (long) (chunk.endTimeUs + tolerance);
|
||||
this.chunk = chunk;
|
||||
loggedFirst = false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void sampleMetadata(long timeUs, int flags, int size, int offset, @Nullable CryptoData cryptoData) {
|
||||
if (lowestTimeUs != C.TIME_UNSET && timeUs < lowestTimeUs && ! loggedFirst) {
|
||||
Log.d(TAG, "sampleMetadata() - committed timeUs: " + timeUs + " is " + C.usToMs(lowestTimeUs - timeUs) + "ms less then segment start time. chunk: " + chunk.dataSpec.uri);
|
||||
loggedFirst = true;
|
||||
}
|
||||
if (lowestTimeUs != C.TIME_UNSET && timeUs < (lowestTimeUs - C.msToUs(50_000))) {
|
||||
Log.d(TAG, "sampleMetadata() - committed timeUs: " + timeUs + " is " + C.usToMs(lowestTimeUs - timeUs) + "ms less (MUCH!) then segment start time. chunk: " + chunk.dataSpec.uri);
|
||||
throw new UnreportedDiscontinuityException(timeUs, chunk.dataSpec.uri);
|
||||
}
|
||||
if (highestTimeUs != C.TIME_UNSET && timeUs > highestTimeUs) {
|
||||
Log.d(TAG, "sampleMetadata() - committed timeUs: " + timeUs + " is " + C.usToMs(lowestTimeUs - timeUs) + "ms greater then segment end time. chunk: " + chunk.dataSpec.uri);
|
||||
throw new UnreportedDiscontinuityException(timeUs, chunk.dataSpec.uri);
|
||||
}
|
||||
super.sampleMetadata(timeUs, flags, size, offset, cryptoData);
|
||||
}
|
||||
|
||||
}
|
@ -27,11 +27,13 @@ import com.google.android.exoplayer2.extractor.PositionHolder;
|
||||
import com.google.android.exoplayer2.metadata.Metadata;
|
||||
import com.google.android.exoplayer2.metadata.id3.Id3Decoder;
|
||||
import com.google.android.exoplayer2.metadata.id3.PrivFrame;
|
||||
import com.google.android.exoplayer2.source.UnreportedDiscontinuityException;
|
||||
import com.google.android.exoplayer2.source.chunk.MediaChunk;
|
||||
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.Log;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
import com.google.android.exoplayer2.util.TimestampAdjuster;
|
||||
import com.google.android.exoplayer2.util.UriUtil;
|
||||
@ -50,6 +52,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
* An HLS {@link MediaChunk}.
|
||||
*/
|
||||
/* package */ final class HlsMediaChunk extends MediaChunk {
|
||||
private static final String TAG = "HlsMediaChunk";
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
@ -282,7 +285,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
*/
|
||||
public void init(HlsSampleStreamWrapper output) {
|
||||
this.output = output;
|
||||
output.init(uid, shouldSpliceIn);
|
||||
output.init(uid, this, shouldSpliceIn);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -376,6 +379,10 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
while (result == Extractor.RESULT_CONTINUE && !loadCanceled) {
|
||||
result = extractor.read(input, DUMMY_POSITION_HOLDER);
|
||||
}
|
||||
} catch (UnreportedDiscontinuityException e) {
|
||||
Log.d(TAG, "Unreported discontinuity at timeUs: "+ e.timesUs + " uri: " + dataSpec.uri);
|
||||
throw new IOException("Timestamp error", e);
|
||||
|
||||
} finally {
|
||||
nextLoadPosition = (int) (input.getPosition() - dataSpec.position);
|
||||
}
|
||||
|
@ -820,15 +820,16 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
|
||||
/**
|
||||
* Initializes the wrapper for loading a chunk.
|
||||
*
|
||||
* @param chunkUid The chunk's uid.
|
||||
* @param loadingChunk the Chunk that is about to start loading.
|
||||
* @param shouldSpliceIn Whether the samples parsed from the chunk should be spliced into any
|
||||
* samples already queued to the wrapper.
|
||||
*/
|
||||
public void init(int chunkUid, boolean shouldSpliceIn) {
|
||||
public void init(int chunkUid, HlsMediaChunk loadingChunk, boolean shouldSpliceIn) {
|
||||
this.chunkUid = chunkUid;
|
||||
for (SampleQueue sampleQueue : sampleQueues) {
|
||||
for (HlsCheckedSampleQueue sampleQueue : sampleQueues) {
|
||||
sampleQueue.sourceId(chunkUid);
|
||||
sampleQueue.setCurrentLoadingChunk(loadingChunk);
|
||||
}
|
||||
if (shouldSpliceIn) {
|
||||
for (SampleQueue sampleQueue : sampleQueues) {
|
||||
@ -1342,7 +1343,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
return new DummyTrackOutput();
|
||||
}
|
||||
|
||||
private static final class FormatAdjustingSampleQueue extends SampleQueue {
|
||||
private static final class FormatAdjustingSampleQueue extends HlsCheckedSampleQueue {
|
||||
|
||||
private final Map<String, DrmInitData> overridingDrmInitData;
|
||||
@Nullable private DrmInitData drmInitData;
|
||||
|
Loading…
x
Reference in New Issue
Block a user