mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Add MediaChunkIterator for SmoothStreaming stream elements.
This allows to iterate through the available chunks of the stream. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=203105813
This commit is contained in:
parent
97120bc6cb
commit
f4bcfe082d
1 changed files with 172 additions and 118 deletions
|
|
@ -20,6 +20,9 @@ import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.Format;
|
import com.google.android.exoplayer2.Format;
|
||||||
import com.google.android.exoplayer2.offline.FilterableManifest;
|
import com.google.android.exoplayer2.offline.FilterableManifest;
|
||||||
import com.google.android.exoplayer2.offline.StreamKey;
|
import com.google.android.exoplayer2.offline.StreamKey;
|
||||||
|
import com.google.android.exoplayer2.source.chunk.BaseMediaChunkIterator;
|
||||||
|
import com.google.android.exoplayer2.source.chunk.MediaChunkIterator;
|
||||||
|
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.UriUtil;
|
import com.google.android.exoplayer2.util.UriUtil;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
|
|
@ -36,123 +39,7 @@ import java.util.UUID;
|
||||||
*/
|
*/
|
||||||
public class SsManifest implements FilterableManifest<SsManifest> {
|
public class SsManifest implements FilterableManifest<SsManifest> {
|
||||||
|
|
||||||
public static final int UNSET_LOOKAHEAD = -1;
|
/** Represents a protection element containing a single header. */
|
||||||
|
|
||||||
/**
|
|
||||||
* The client manifest major version.
|
|
||||||
*/
|
|
||||||
public final int majorVersion;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The client manifest minor version.
|
|
||||||
*/
|
|
||||||
public final int minorVersion;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of fragments in a lookahead, or {@link #UNSET_LOOKAHEAD} if the lookahead is
|
|
||||||
* unspecified.
|
|
||||||
*/
|
|
||||||
public final int lookAheadCount;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the manifest describes a live presentation still in progress.
|
|
||||||
*/
|
|
||||||
public final boolean isLive;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Content protection information, or null if the content is not protected.
|
|
||||||
*/
|
|
||||||
public final ProtectionElement protectionElement;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The contained stream elements.
|
|
||||||
*/
|
|
||||||
public final StreamElement[] streamElements;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The overall presentation duration of the media in microseconds, or {@link C#TIME_UNSET}
|
|
||||||
* if the duration is unknown.
|
|
||||||
*/
|
|
||||||
public final long durationUs;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The length of the trailing window for a live broadcast in microseconds, or
|
|
||||||
* {@link C#TIME_UNSET} if the stream is not live or if the window length is unspecified.
|
|
||||||
*/
|
|
||||||
public final long dvrWindowLengthUs;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param majorVersion The client manifest major version.
|
|
||||||
* @param minorVersion The client manifest minor version.
|
|
||||||
* @param timescale The timescale of the media as the number of units that pass in one second.
|
|
||||||
* @param duration The overall presentation duration in units of the timescale attribute, or 0
|
|
||||||
* if the duration is unknown.
|
|
||||||
* @param dvrWindowLength The length of the trailing window in units of the timescale attribute,
|
|
||||||
* or 0 if this attribute is unspecified or not applicable.
|
|
||||||
* @param lookAheadCount The number of fragments in a lookahead, or {@link #UNSET_LOOKAHEAD} if
|
|
||||||
* this attribute is unspecified or not applicable.
|
|
||||||
* @param isLive True if the manifest describes a live presentation still in progress. False
|
|
||||||
* otherwise.
|
|
||||||
* @param protectionElement Content protection information, or null if the content is not
|
|
||||||
* protected.
|
|
||||||
* @param streamElements The contained stream elements.
|
|
||||||
*/
|
|
||||||
public SsManifest(int majorVersion, int minorVersion, long timescale, long duration,
|
|
||||||
long dvrWindowLength, int lookAheadCount, boolean isLive, ProtectionElement protectionElement,
|
|
||||||
StreamElement[] streamElements) {
|
|
||||||
this(majorVersion, minorVersion,
|
|
||||||
duration == 0 ? C.TIME_UNSET
|
|
||||||
: Util.scaleLargeTimestamp(duration, C.MICROS_PER_SECOND, timescale),
|
|
||||||
dvrWindowLength == 0 ? C.TIME_UNSET
|
|
||||||
: Util.scaleLargeTimestamp(dvrWindowLength, C.MICROS_PER_SECOND, timescale),
|
|
||||||
lookAheadCount, isLive, protectionElement, streamElements);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SsManifest(int majorVersion, int minorVersion, long durationUs, long dvrWindowLengthUs,
|
|
||||||
int lookAheadCount, boolean isLive, ProtectionElement protectionElement,
|
|
||||||
StreamElement[] streamElements) {
|
|
||||||
this.majorVersion = majorVersion;
|
|
||||||
this.minorVersion = minorVersion;
|
|
||||||
this.durationUs = durationUs;
|
|
||||||
this.dvrWindowLengthUs = dvrWindowLengthUs;
|
|
||||||
this.lookAheadCount = lookAheadCount;
|
|
||||||
this.isLive = isLive;
|
|
||||||
this.protectionElement = protectionElement;
|
|
||||||
this.streamElements = streamElements;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final SsManifest copy(List<StreamKey> streamKeys) {
|
|
||||||
ArrayList<StreamKey> sortedKeys = new ArrayList<>(streamKeys);
|
|
||||||
Collections.sort(sortedKeys);
|
|
||||||
|
|
||||||
StreamElement currentStreamElement = null;
|
|
||||||
List<StreamElement> copiedStreamElements = new ArrayList<>();
|
|
||||||
List<Format> copiedFormats = new ArrayList<>();
|
|
||||||
for (int i = 0; i < sortedKeys.size(); i++) {
|
|
||||||
StreamKey key = sortedKeys.get(i);
|
|
||||||
StreamElement streamElement = streamElements[key.groupIndex];
|
|
||||||
if (streamElement != currentStreamElement && currentStreamElement != null) {
|
|
||||||
// We're advancing to a new stream element. Add the current one.
|
|
||||||
copiedStreamElements.add(currentStreamElement.copy(copiedFormats.toArray(new Format[0])));
|
|
||||||
copiedFormats.clear();
|
|
||||||
}
|
|
||||||
currentStreamElement = streamElement;
|
|
||||||
copiedFormats.add(streamElement.formats[key.trackIndex]);
|
|
||||||
}
|
|
||||||
if (currentStreamElement != null) {
|
|
||||||
// Add the last stream element.
|
|
||||||
copiedStreamElements.add(currentStreamElement.copy(copiedFormats.toArray(new Format[0])));
|
|
||||||
}
|
|
||||||
|
|
||||||
StreamElement[] copiedStreamElementsArray = copiedStreamElements.toArray(new StreamElement[0]);
|
|
||||||
return new SsManifest(majorVersion, minorVersion, durationUs, dvrWindowLengthUs, lookAheadCount,
|
|
||||||
isLive, protectionElement, copiedStreamElementsArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a protection element containing a single header.
|
|
||||||
*/
|
|
||||||
public static class ProtectionElement {
|
public static class ProtectionElement {
|
||||||
|
|
||||||
public final UUID uuid;
|
public final UUID uuid;
|
||||||
|
|
@ -162,7 +49,45 @@ public class SsManifest implements FilterableManifest<SsManifest> {
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@link MediaChunkIterator} wrapping a track of a {@link StreamElement}. */
|
||||||
|
public static final class StreamElementIterator extends BaseMediaChunkIterator {
|
||||||
|
|
||||||
|
private final StreamElement streamElement;
|
||||||
|
private final int trackIndex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates iterator.
|
||||||
|
*
|
||||||
|
* @param streamElement The {@link StreamElement} to wrap.
|
||||||
|
* @param trackIndex The track index in the stream element.
|
||||||
|
* @param chunkIndex The chunk index at which the iterator will start.
|
||||||
|
*/
|
||||||
|
public StreamElementIterator(StreamElement streamElement, int trackIndex, int chunkIndex) {
|
||||||
|
super(/* fromIndex= */ chunkIndex, /* toIndex= */ streamElement.chunkCount - 1);
|
||||||
|
this.streamElement = streamElement;
|
||||||
|
this.trackIndex = trackIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataSpec getDataSpec() {
|
||||||
|
checkInBounds();
|
||||||
|
Uri uri = streamElement.buildRequestUri(trackIndex, (int) getCurrentIndex());
|
||||||
|
return new DataSpec(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getChunkStartTimeUs() {
|
||||||
|
checkInBounds();
|
||||||
|
return streamElement.getStartTimeUs((int) getCurrentIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getChunkEndTimeUs() {
|
||||||
|
long chunkStartTimeUs = getChunkStartTimeUs();
|
||||||
|
return chunkStartTimeUs + streamElement.getChunkDurationUs((int) getCurrentIndex());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -302,7 +227,136 @@ public class SsManifest implements FilterableManifest<SsManifest> {
|
||||||
.replace(URL_PLACEHOLDER_START_TIME_2, startTimeString);
|
.replace(URL_PLACEHOLDER_START_TIME_2, startTimeString);
|
||||||
return UriUtil.resolveToUri(baseUri, chunkUrl);
|
return UriUtil.resolveToUri(baseUri, chunkUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final int UNSET_LOOKAHEAD = -1;
|
||||||
|
|
||||||
|
/** The client manifest major version. */
|
||||||
|
public final int majorVersion;
|
||||||
|
|
||||||
|
/** The client manifest minor version. */
|
||||||
|
public final int minorVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of fragments in a lookahead, or {@link #UNSET_LOOKAHEAD} if the lookahead is
|
||||||
|
* unspecified.
|
||||||
|
*/
|
||||||
|
public final int lookAheadCount;
|
||||||
|
|
||||||
|
/** Whether the manifest describes a live presentation still in progress. */
|
||||||
|
public final boolean isLive;
|
||||||
|
|
||||||
|
/** Content protection information, or null if the content is not protected. */
|
||||||
|
public final ProtectionElement protectionElement;
|
||||||
|
|
||||||
|
/** The contained stream elements. */
|
||||||
|
public final StreamElement[] streamElements;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The overall presentation duration of the media in microseconds, or {@link C#TIME_UNSET} if the
|
||||||
|
* duration is unknown.
|
||||||
|
*/
|
||||||
|
public final long durationUs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The length of the trailing window for a live broadcast in microseconds, or {@link C#TIME_UNSET}
|
||||||
|
* if the stream is not live or if the window length is unspecified.
|
||||||
|
*/
|
||||||
|
public final long dvrWindowLengthUs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param majorVersion The client manifest major version.
|
||||||
|
* @param minorVersion The client manifest minor version.
|
||||||
|
* @param timescale The timescale of the media as the number of units that pass in one second.
|
||||||
|
* @param duration The overall presentation duration in units of the timescale attribute, or 0 if
|
||||||
|
* the duration is unknown.
|
||||||
|
* @param dvrWindowLength The length of the trailing window in units of the timescale attribute,
|
||||||
|
* or 0 if this attribute is unspecified or not applicable.
|
||||||
|
* @param lookAheadCount The number of fragments in a lookahead, or {@link #UNSET_LOOKAHEAD} if
|
||||||
|
* this attribute is unspecified or not applicable.
|
||||||
|
* @param isLive True if the manifest describes a live presentation still in progress. False
|
||||||
|
* otherwise.
|
||||||
|
* @param protectionElement Content protection information, or null if the content is not
|
||||||
|
* protected.
|
||||||
|
* @param streamElements The contained stream elements.
|
||||||
|
*/
|
||||||
|
public SsManifest(
|
||||||
|
int majorVersion,
|
||||||
|
int minorVersion,
|
||||||
|
long timescale,
|
||||||
|
long duration,
|
||||||
|
long dvrWindowLength,
|
||||||
|
int lookAheadCount,
|
||||||
|
boolean isLive,
|
||||||
|
ProtectionElement protectionElement,
|
||||||
|
StreamElement[] streamElements) {
|
||||||
|
this(
|
||||||
|
majorVersion,
|
||||||
|
minorVersion,
|
||||||
|
duration == 0
|
||||||
|
? C.TIME_UNSET
|
||||||
|
: Util.scaleLargeTimestamp(duration, C.MICROS_PER_SECOND, timescale),
|
||||||
|
dvrWindowLength == 0
|
||||||
|
? C.TIME_UNSET
|
||||||
|
: Util.scaleLargeTimestamp(dvrWindowLength, C.MICROS_PER_SECOND, timescale),
|
||||||
|
lookAheadCount,
|
||||||
|
isLive,
|
||||||
|
protectionElement,
|
||||||
|
streamElements);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SsManifest(
|
||||||
|
int majorVersion,
|
||||||
|
int minorVersion,
|
||||||
|
long durationUs,
|
||||||
|
long dvrWindowLengthUs,
|
||||||
|
int lookAheadCount,
|
||||||
|
boolean isLive,
|
||||||
|
ProtectionElement protectionElement,
|
||||||
|
StreamElement[] streamElements) {
|
||||||
|
this.majorVersion = majorVersion;
|
||||||
|
this.minorVersion = minorVersion;
|
||||||
|
this.durationUs = durationUs;
|
||||||
|
this.dvrWindowLengthUs = dvrWindowLengthUs;
|
||||||
|
this.lookAheadCount = lookAheadCount;
|
||||||
|
this.isLive = isLive;
|
||||||
|
this.protectionElement = protectionElement;
|
||||||
|
this.streamElements = streamElements;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final SsManifest copy(List<StreamKey> streamKeys) {
|
||||||
|
ArrayList<StreamKey> sortedKeys = new ArrayList<>(streamKeys);
|
||||||
|
Collections.sort(sortedKeys);
|
||||||
|
|
||||||
|
StreamElement currentStreamElement = null;
|
||||||
|
List<StreamElement> copiedStreamElements = new ArrayList<>();
|
||||||
|
List<Format> copiedFormats = new ArrayList<>();
|
||||||
|
for (int i = 0; i < sortedKeys.size(); i++) {
|
||||||
|
StreamKey key = sortedKeys.get(i);
|
||||||
|
StreamElement streamElement = streamElements[key.groupIndex];
|
||||||
|
if (streamElement != currentStreamElement && currentStreamElement != null) {
|
||||||
|
// We're advancing to a new stream element. Add the current one.
|
||||||
|
copiedStreamElements.add(currentStreamElement.copy(copiedFormats.toArray(new Format[0])));
|
||||||
|
copiedFormats.clear();
|
||||||
|
}
|
||||||
|
currentStreamElement = streamElement;
|
||||||
|
copiedFormats.add(streamElement.formats[key.trackIndex]);
|
||||||
|
}
|
||||||
|
if (currentStreamElement != null) {
|
||||||
|
// Add the last stream element.
|
||||||
|
copiedStreamElements.add(currentStreamElement.copy(copiedFormats.toArray(new Format[0])));
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamElement[] copiedStreamElementsArray = copiedStreamElements.toArray(new StreamElement[0]);
|
||||||
|
return new SsManifest(
|
||||||
|
majorVersion,
|
||||||
|
minorVersion,
|
||||||
|
durationUs,
|
||||||
|
dvrWindowLengthUs,
|
||||||
|
lookAheadCount,
|
||||||
|
isLive,
|
||||||
|
protectionElement,
|
||||||
|
copiedStreamElementsArray);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue