Java源码示例:com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.StreamElement
示例1
private void parseStreamElementStartTag(XmlPullParser parser) throws ParserException {
type = parseType(parser);
putNormalizedAttribute(KEY_TYPE, type);
if (type == StreamElement.TYPE_TEXT) {
subType = parseRequiredString(parser, KEY_SUB_TYPE);
} else {
subType = parser.getAttributeValue(null, KEY_SUB_TYPE);
}
name = parser.getAttributeValue(null, KEY_NAME);
qualityLevels = parseInt(parser, KEY_QUALITY_LEVELS, -1);
url = parseRequiredString(parser, KEY_URL);
maxWidth = parseInt(parser, KEY_MAX_WIDTH, -1);
maxHeight = parseInt(parser, KEY_MAX_HEIGHT, -1);
displayWidth = parseInt(parser, KEY_DISPLAY_WIDTH, -1);
displayHeight = parseInt(parser, KEY_DISPLAY_HEIGHT, -1);
language = parser.getAttributeValue(null, KEY_LANGUAGE);
timescale = parseInt(parser, KEY_TIME_SCALE, -1);
if (timescale == -1) {
timescale = (Long) getNormalizedAttribute(KEY_TIME_SCALE);
}
startTimes = new ArrayList<Long>();
}
示例2
@Override
public void continueBuffering(long playbackPositionUs) {
if (manifestFetcher == null || !currentManifest.isLive || fatalError != null) {
return;
}
SmoothStreamingManifest newManifest = manifestFetcher.getManifest();
if (currentManifest != newManifest && newManifest != null) {
StreamElement currentElement = getElement(currentManifest);
StreamElement newElement = getElement(newManifest);
if (newElement.chunkCount == 0) {
currentManifestChunkOffset += currentElement.chunkCount;
} else if (currentElement.chunkCount > 0) {
currentManifestChunkOffset += currentElement.getChunkIndex(newElement.getStartTimeUs(0));
}
currentManifest = newManifest;
finishedCurrentManifest = false;
}
if (finishedCurrentManifest && (SystemClock.elapsedRealtime()
> manifestFetcher.getManifestLoadTimestamp() + MINIMUM_MANIFEST_REFRESH_PERIOD_MS)) {
manifestFetcher.requestRefresh();
}
}
示例3
@Override
public void addChild(Object child) {
if (child instanceof StreamElement) {
streamElements.add((StreamElement) child);
} else if (child instanceof ProtectionElement) {
Assertions.checkState(protectionElement == null);
protectionElement = (ProtectionElement) child;
}
}
示例4
@Override
public Object build() {
StreamElement[] streamElementArray = new StreamElement[streamElements.size()];
streamElements.toArray(streamElementArray);
return new SmoothStreamingManifest(majorVersion, minorVersion, timescale, duration,
dvrWindowLength, lookAheadCount, isLive, protectionElement, streamElementArray);
}
示例5
private int parseType(XmlPullParser parser) throws ParserException {
String value = parser.getAttributeValue(null, KEY_TYPE);
if (value != null) {
if (KEY_TYPE_AUDIO.equalsIgnoreCase(value)) {
return StreamElement.TYPE_AUDIO;
} else if (KEY_TYPE_VIDEO.equalsIgnoreCase(value)) {
return StreamElement.TYPE_VIDEO;
} else if (KEY_TYPE_TEXT.equalsIgnoreCase(value)) {
return StreamElement.TYPE_TEXT;
} else {
throw new ParserException("Invalid key value[" + value + "]");
}
}
throw new MissingFieldException(KEY_TYPE);
}
示例6
@Override
public Object build() {
TrackElement[] trackElements = new TrackElement[tracks.size()];
tracks.toArray(trackElements);
return new StreamElement(baseUri, url, type, subType, timescale, name, qualityLevels,
maxWidth, maxHeight, displayWidth, displayHeight, language, trackElements, startTimes,
lastChunkDuration);
}
示例7
/**
* For live playbacks, determines the seek position that snaps playback to be
* {@link #liveEdgeLatencyUs} behind the live edge of the current manifest
*
* @return The seek position in microseconds.
*/
private long getLiveSeekPosition() {
long liveEdgeTimestampUs = Long.MIN_VALUE;
for (int i = 0; i < currentManifest.streamElements.length; i++) {
StreamElement streamElement = currentManifest.streamElements[i];
if (streamElement.chunkCount > 0) {
long elementLiveEdgeTimestampUs =
streamElement.getStartTimeUs(streamElement.chunkCount - 1)
+ streamElement.getChunkDurationUs(streamElement.chunkCount - 1);
liveEdgeTimestampUs = Math.max(liveEdgeTimestampUs, elementLiveEdgeTimestampUs);
}
}
return liveEdgeTimestampUs - liveEdgeLatencyUs;
}
示例8
public SmoothStreamMediaParser(ElementParser parent, Uri baseUri) {
super(parent, baseUri, TAG);
lookAheadCount = -1;
protectionElement = null;
streamElements = new LinkedList<StreamElement>();
}
示例9
@Override
public void parseStartTag(XmlPullParser parser) throws ParserException {
int type = (Integer) getNormalizedAttribute(KEY_TYPE);
content = null;
String value;
index = parseInt(parser, KEY_INDEX, -1);
bitrate = parseRequiredInt(parser, KEY_BITRATE);
nalUnitLengthField = parseInt(parser, KEY_NAL_UNIT_LENGTH_FIELD, 4);
if (type == StreamElement.TYPE_VIDEO) {
maxHeight = parseRequiredInt(parser, KEY_MAX_HEIGHT);
maxWidth = parseRequiredInt(parser, KEY_MAX_WIDTH);
mimeType = fourCCToMimeType(parseRequiredString(parser, KEY_FOUR_CC));
} else {
maxHeight = -1;
maxWidth = -1;
String fourCC = parser.getAttributeValue(null, KEY_FOUR_CC);
// If fourCC is missing and the stream type is audio, we assume AAC.
mimeType = fourCC != null ? fourCCToMimeType(fourCC)
: type == StreamElement.TYPE_AUDIO ? MimeTypes.AUDIO_AAC : null;
}
if (type == StreamElement.TYPE_AUDIO) {
samplingRate = parseRequiredInt(parser, KEY_SAMPLING_RATE);
channels = parseRequiredInt(parser, KEY_CHANNELS);
bitPerSample = parseRequiredInt(parser, KEY_BITS_PER_SAMPLE);
packetSize = parseRequiredInt(parser, KEY_PACKET_SIZE);
audioTag = parseRequiredInt(parser, KEY_AUDIO_TAG);
} else {
samplingRate = -1;
channels = -1;
bitPerSample = -1;
packetSize = -1;
audioTag = -1;
}
value = parser.getAttributeValue(null, KEY_CODEC_PRIVATE_DATA);
if (value != null && value.length() > 0) {
byte[] codecPrivateData = hexStringToByteArray(value);
byte[][] split = CodecSpecificDataUtil.splitNalUnits(codecPrivateData);
if (split == null) {
csd.add(codecPrivateData);
} else {
for (int i = 0; i < split.length; i++) {
Pair<Integer, Integer> spsParameters = CodecSpecificDataUtil.parseSpsNalUnit(split[i]);
if (spsParameters != null) {
profile = spsParameters.first;
level = spsParameters.second;
}
csd.add(split[i]);
}
}
}
}
示例10
private SmoothStreamingChunkSource(ManifestFetcher<SmoothStreamingManifest> manifestFetcher,
SmoothStreamingManifest initialManifest, int streamElementIndex, int[] trackIndices,
DataSource dataSource, FormatEvaluator formatEvaluator, long liveEdgeLatencyMs) {
this.manifestFetcher = manifestFetcher;
this.streamElementIndex = streamElementIndex;
this.currentManifest = initialManifest;
this.dataSource = dataSource;
this.formatEvaluator = formatEvaluator;
this.liveEdgeLatencyUs = liveEdgeLatencyMs * 1000;
StreamElement streamElement = getElement(initialManifest);
trackInfo = new TrackInfo(streamElement.tracks[0].mimeType, initialManifest.durationUs);
evaluation = new Evaluation();
TrackEncryptionBox[] trackEncryptionBoxes = null;
ProtectionElement protectionElement = initialManifest.protectionElement;
if (protectionElement != null) {
byte[] keyId = getKeyId(protectionElement.data);
trackEncryptionBoxes = new TrackEncryptionBox[1];
trackEncryptionBoxes[0] = new TrackEncryptionBox(true, INITIALIZATION_VECTOR_SIZE, keyId);
psshInfo = Collections.singletonMap(protectionElement.uuid, protectionElement.data);
} else {
psshInfo = null;
}
int trackCount = trackIndices != null ? trackIndices.length : streamElement.tracks.length;
formats = new SmoothStreamingFormat[trackCount];
extractors = new SparseArray<FragmentedMp4Extractor>();
int maxWidth = 0;
int maxHeight = 0;
for (int i = 0; i < trackCount; i++) {
int trackIndex = trackIndices != null ? trackIndices[i] : i;
TrackElement trackElement = streamElement.tracks[trackIndex];
formats[i] = new SmoothStreamingFormat(String.valueOf(trackIndex), trackElement.mimeType,
trackElement.maxWidth, trackElement.maxHeight, trackElement.numChannels,
trackElement.sampleRate, trackElement.bitrate, trackIndex);
maxWidth = Math.max(maxWidth, trackElement.maxWidth);
maxHeight = Math.max(maxHeight, trackElement.maxHeight);
MediaFormat mediaFormat = getMediaFormat(streamElement, trackIndex);
int trackType = streamElement.type == StreamElement.TYPE_VIDEO ? Track.TYPE_VIDEO
: Track.TYPE_AUDIO;
FragmentedMp4Extractor extractor = new FragmentedMp4Extractor(
FragmentedMp4Extractor.WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME);
extractor.setTrack(new Track(trackIndex, trackType, streamElement.timescale,
initialManifest.durationUs, mediaFormat, trackEncryptionBoxes));
extractors.put(trackIndex, extractor);
}
this.maxHeight = maxHeight;
this.maxWidth = maxWidth;
Arrays.sort(formats, new DecreasingBandwidthComparator());
}
示例11
@Override
public final void getChunkOperation(List<? extends MediaChunk> queue, long seekPositionUs,
long playbackPositionUs, ChunkOperationHolder out) {
if (fatalError != null) {
out.chunk = null;
return;
}
evaluation.queueSize = queue.size();
formatEvaluator.evaluate(queue, playbackPositionUs, formats, evaluation);
SmoothStreamingFormat selectedFormat = (SmoothStreamingFormat) evaluation.format;
out.queueSize = evaluation.queueSize;
if (selectedFormat == null) {
out.chunk = null;
return;
} else if (out.queueSize == queue.size() && out.chunk != null
&& out.chunk.format.id.equals(evaluation.format.id)) {
// We already have a chunk, and the evaluation hasn't changed either the format or the size
// of the queue. Do nothing.
return;
}
// In all cases where we return before instantiating a new chunk at the bottom of this method,
// we want out.chunk to be null.
out.chunk = null;
StreamElement streamElement = getElement(currentManifest);
if (streamElement.chunkCount == 0) {
// The manifest is currently empty for this stream.
finishedCurrentManifest = true;
return;
}
int chunkIndex;
if (queue.isEmpty()) {
if (currentManifest.isLive) {
seekPositionUs = getLiveSeekPosition();
}
chunkIndex = streamElement.getChunkIndex(seekPositionUs);
} else {
chunkIndex = queue.get(out.queueSize - 1).nextChunkIndex - currentManifestChunkOffset;
}
if (currentManifest.isLive) {
if (chunkIndex < 0) {
// This is before the first chunk in the current manifest.
fatalError = new BehindLiveWindowException();
return;
} else if (chunkIndex >= streamElement.chunkCount) {
// This is beyond the last chunk in the current manifest.
finishedCurrentManifest = true;
return;
} else if (chunkIndex == streamElement.chunkCount - 1) {
// This is the last chunk in the current manifest. Mark the manifest as being finished,
// but continue to return the final chunk.
finishedCurrentManifest = true;
}
} else if (chunkIndex == -1) {
// We've reached the end of the stream.
return;
}
boolean isLastChunk = !currentManifest.isLive && chunkIndex == streamElement.chunkCount - 1;
long chunkStartTimeUs = streamElement.getStartTimeUs(chunkIndex);
long nextChunkStartTimeUs = isLastChunk ? -1
: chunkStartTimeUs + streamElement.getChunkDurationUs(chunkIndex);
int currentAbsoluteChunkIndex = chunkIndex + currentManifestChunkOffset;
Uri uri = streamElement.buildRequestUri(selectedFormat.trackIndex, chunkIndex);
Chunk mediaChunk = newMediaChunk(selectedFormat, uri, null,
extractors.get(Integer.parseInt(selectedFormat.id)), psshInfo, dataSource,
currentAbsoluteChunkIndex, isLastChunk, chunkStartTimeUs, nextChunkStartTimeUs, 0);
out.chunk = mediaChunk;
}
示例12
private StreamElement getElement(SmoothStreamingManifest manifest) {
return manifest.streamElements[streamElementIndex];
}