mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
parent
4adf8f77f4
commit
dd30632aa1
@ -45,7 +45,7 @@ public class DemoUtil {
|
||||
public static final String CONTENT_ID_EXTRA = "content_id";
|
||||
|
||||
public static final int TYPE_DASH_VOD = 0;
|
||||
public static final int TYPE_SS_VOD = 1;
|
||||
public static final int TYPE_SS = 1;
|
||||
public static final int TYPE_OTHER = 2;
|
||||
|
||||
public static final boolean EXPOSE_EXPERIMENTAL_FEATURES = false;
|
||||
|
@ -56,7 +56,7 @@ package com.google.android.exoplayer.demo;
|
||||
false),
|
||||
new Sample("Super speed (SmoothStreaming)", "uid:ss:superspeed",
|
||||
"http://playready.directtaps.net/smoothstreaming/SSWSS720H264/SuperSpeedway_720.ism",
|
||||
DemoUtil.TYPE_SS_VOD, false, false),
|
||||
DemoUtil.TYPE_SS, false, false),
|
||||
new Sample("Dizzy (Misc)", "uid:misc:dizzy",
|
||||
"http://html5demos.com/assets/dizzy.mp4", DemoUtil.TYPE_OTHER, false, false),
|
||||
};
|
||||
@ -92,10 +92,10 @@ package com.google.android.exoplayer.demo;
|
||||
public static final Sample[] SMOOTHSTREAMING = new Sample[] {
|
||||
new Sample("Super speed", "uid:ss:superspeed",
|
||||
"http://playready.directtaps.net/smoothstreaming/SSWSS720H264/SuperSpeedway_720.ism",
|
||||
DemoUtil.TYPE_SS_VOD, false, true),
|
||||
DemoUtil.TYPE_SS, false, true),
|
||||
new Sample("Super speed (PlayReady)", "uid:ss:pr:superspeed",
|
||||
"http://playready.directtaps.net/smoothstreaming/SSWSS720H264PR/SuperSpeedway_720.ism",
|
||||
DemoUtil.TYPE_SS_VOD, true, true),
|
||||
DemoUtil.TYPE_SS, true, true),
|
||||
};
|
||||
|
||||
public static final Sample[] WIDEVINE_GTS = new Sample[] {
|
||||
|
@ -167,7 +167,7 @@ public class FullPlayerActivity extends Activity implements SurfaceHolder.Callba
|
||||
private RendererBuilder getRendererBuilder() {
|
||||
String userAgent = DemoUtil.getUserAgent(this);
|
||||
switch (contentType) {
|
||||
case DemoUtil.TYPE_SS_VOD:
|
||||
case DemoUtil.TYPE_SS:
|
||||
return new SmoothStreamingRendererBuilder(userAgent, contentUri.toString(), contentId,
|
||||
new SmoothStreamingTestMediaDrmCallback(), debugTextView);
|
||||
case DemoUtil.TYPE_DASH_VOD:
|
||||
|
@ -65,6 +65,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
|
||||
private static final int VIDEO_BUFFER_SEGMENTS = 200;
|
||||
private static final int AUDIO_BUFFER_SEGMENTS = 60;
|
||||
private static final int TTML_BUFFER_SEGMENTS = 2;
|
||||
private static final int LIVE_EDGE_LATENCY_MS = 30000;
|
||||
|
||||
private final String userAgent;
|
||||
private final String url;
|
||||
@ -74,6 +75,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
|
||||
|
||||
private DemoPlayer player;
|
||||
private RendererBuilderCallback callback;
|
||||
private ManifestFetcher<SmoothStreamingManifest> manifestFetcher;
|
||||
|
||||
public SmoothStreamingRendererBuilder(String userAgent, String url, String contentId,
|
||||
MediaDrmCallback drmCallback, TextView debugTextView) {
|
||||
@ -89,8 +91,8 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
|
||||
this.player = player;
|
||||
this.callback = callback;
|
||||
SmoothStreamingManifestParser parser = new SmoothStreamingManifestParser();
|
||||
ManifestFetcher<SmoothStreamingManifest> manifestFetcher =
|
||||
new ManifestFetcher<SmoothStreamingManifest>(parser, contentId, url + "/Manifest");
|
||||
manifestFetcher = new ManifestFetcher<SmoothStreamingManifest>(parser, contentId,
|
||||
url + "/Manifest");
|
||||
manifestFetcher.singleLoad(player.getMainHandler().getLooper(), this);
|
||||
}
|
||||
|
||||
@ -154,9 +156,9 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
|
||||
|
||||
// Build the video renderer.
|
||||
DataSource videoDataSource = new HttpDataSource(userAgent, null, bandwidthMeter);
|
||||
ChunkSource videoChunkSource = new SmoothStreamingChunkSource(manifest,
|
||||
ChunkSource videoChunkSource = new SmoothStreamingChunkSource(manifestFetcher,
|
||||
videoStreamElementIndex, videoTrackIndices, videoDataSource,
|
||||
new AdaptiveEvaluator(bandwidthMeter));
|
||||
new AdaptiveEvaluator(bandwidthMeter), LIVE_EDGE_LATENCY_MS);
|
||||
ChunkSampleSource videoSampleSource = new ChunkSampleSource(videoChunkSource, loadControl,
|
||||
VIDEO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, true, mainHandler, player,
|
||||
DemoPlayer.TYPE_VIDEO);
|
||||
@ -181,8 +183,9 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
|
||||
for (int i = 0; i < manifest.streamElements.length; i++) {
|
||||
if (manifest.streamElements[i].type == StreamElement.TYPE_AUDIO) {
|
||||
audioTrackNames[audioStreamElementCount] = manifest.streamElements[i].name;
|
||||
audioChunkSources[audioStreamElementCount] = new SmoothStreamingChunkSource(manifest,
|
||||
i, new int[] {0}, audioDataSource, audioFormatEvaluator);
|
||||
audioChunkSources[audioStreamElementCount] = new SmoothStreamingChunkSource(
|
||||
manifestFetcher, i, new int[] {0}, audioDataSource, audioFormatEvaluator,
|
||||
LIVE_EDGE_LATENCY_MS);
|
||||
audioStreamElementCount++;
|
||||
}
|
||||
}
|
||||
@ -211,8 +214,8 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
|
||||
for (int i = 0; i < manifest.streamElements.length; i++) {
|
||||
if (manifest.streamElements[i].type == StreamElement.TYPE_TEXT) {
|
||||
textTrackNames[textStreamElementCount] = manifest.streamElements[i].language;
|
||||
textChunkSources[textStreamElementCount] = new SmoothStreamingChunkSource(manifest,
|
||||
i, new int[] {0}, ttmlDataSource, ttmlFormatEvaluator);
|
||||
textChunkSources[textStreamElementCount] = new SmoothStreamingChunkSource(manifestFetcher,
|
||||
i, new int[] {0}, ttmlDataSource, ttmlFormatEvaluator, LIVE_EDGE_LATENCY_MS);
|
||||
textStreamElementCount++;
|
||||
}
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ import java.util.ArrayList;
|
||||
private static final int BUFFER_SEGMENT_SIZE = 64 * 1024;
|
||||
private static final int VIDEO_BUFFER_SEGMENTS = 200;
|
||||
private static final int AUDIO_BUFFER_SEGMENTS = 60;
|
||||
private static final int LIVE_EDGE_LATENCY_MS = 30000;
|
||||
|
||||
private final SimplePlayerActivity playerActivity;
|
||||
private final String userAgent;
|
||||
@ -61,6 +62,7 @@ import java.util.ArrayList;
|
||||
private final String contentId;
|
||||
|
||||
private RendererBuilderCallback callback;
|
||||
private ManifestFetcher<SmoothStreamingManifest> manifestFetcher;
|
||||
|
||||
public SmoothStreamingRendererBuilder(SimplePlayerActivity playerActivity, String userAgent,
|
||||
String url, String contentId) {
|
||||
@ -74,8 +76,8 @@ import java.util.ArrayList;
|
||||
public void buildRenderers(RendererBuilderCallback callback) {
|
||||
this.callback = callback;
|
||||
SmoothStreamingManifestParser parser = new SmoothStreamingManifestParser();
|
||||
ManifestFetcher<SmoothStreamingManifest> manifestFetcher =
|
||||
new ManifestFetcher<SmoothStreamingManifest>(parser, contentId, url + "/Manifest");
|
||||
manifestFetcher = new ManifestFetcher<SmoothStreamingManifest>(parser, contentId,
|
||||
url + "/Manifest");
|
||||
manifestFetcher.singleLoad(playerActivity.getMainLooper(), this);
|
||||
}
|
||||
|
||||
@ -120,8 +122,9 @@ import java.util.ArrayList;
|
||||
|
||||
// Build the video renderer.
|
||||
DataSource videoDataSource = new HttpDataSource(userAgent, null, bandwidthMeter);
|
||||
ChunkSource videoChunkSource = new SmoothStreamingChunkSource(manifest, videoStreamElementIndex,
|
||||
videoTrackIndices, videoDataSource, new AdaptiveEvaluator(bandwidthMeter));
|
||||
ChunkSource videoChunkSource = new SmoothStreamingChunkSource(manifestFetcher,
|
||||
videoStreamElementIndex, videoTrackIndices, videoDataSource,
|
||||
new AdaptiveEvaluator(bandwidthMeter), LIVE_EDGE_LATENCY_MS);
|
||||
ChunkSampleSource videoSampleSource = new ChunkSampleSource(videoChunkSource, loadControl,
|
||||
VIDEO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, true);
|
||||
MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(videoSampleSource,
|
||||
@ -129,8 +132,9 @@ import java.util.ArrayList;
|
||||
|
||||
// Build the audio renderer.
|
||||
DataSource audioDataSource = new HttpDataSource(userAgent, null, bandwidthMeter);
|
||||
ChunkSource audioChunkSource = new SmoothStreamingChunkSource(manifest, audioStreamElementIndex,
|
||||
new int[] {0}, audioDataSource, new FormatEvaluator.FixedEvaluator());
|
||||
ChunkSource audioChunkSource = new SmoothStreamingChunkSource(manifestFetcher,
|
||||
audioStreamElementIndex, new int[] {0}, audioDataSource,
|
||||
new FormatEvaluator.FixedEvaluator(), LIVE_EDGE_LATENCY_MS);
|
||||
SampleSource audioSampleSource = new ChunkSampleSource(audioChunkSource, loadControl,
|
||||
AUDIO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, true);
|
||||
MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(
|
||||
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Thrown when a live playback falls behind the available media window.
|
||||
*/
|
||||
public class BehindLiveWindowException extends IOException {
|
||||
|
||||
public BehindLiveWindowException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public BehindLiveWindowException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.google.android.exoplayer.smoothstreaming;
|
||||
|
||||
import com.google.android.exoplayer.BehindLiveWindowException;
|
||||
import com.google.android.exoplayer.MediaFormat;
|
||||
import com.google.android.exoplayer.TrackInfo;
|
||||
import com.google.android.exoplayer.chunk.Chunk;
|
||||
@ -36,8 +37,10 @@ import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.Trac
|
||||
import com.google.android.exoplayer.upstream.DataSource;
|
||||
import com.google.android.exoplayer.upstream.DataSpec;
|
||||
import com.google.android.exoplayer.util.CodecSpecificDataUtil;
|
||||
import com.google.android.exoplayer.util.ManifestFetcher;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Base64;
|
||||
import android.util.SparseArray;
|
||||
|
||||
@ -51,13 +54,16 @@ import java.util.List;
|
||||
*/
|
||||
public class SmoothStreamingChunkSource implements ChunkSource {
|
||||
|
||||
private static final int MINIMUM_MANIFEST_REFRESH_PERIOD_MS = 5000;
|
||||
private static final int INITIALIZATION_VECTOR_SIZE = 8;
|
||||
|
||||
private final StreamElement streamElement;
|
||||
private final ManifestFetcher<SmoothStreamingManifest> manifestFetcher;
|
||||
private final int streamElementIndex;
|
||||
private final TrackInfo trackInfo;
|
||||
private final DataSource dataSource;
|
||||
private final FormatEvaluator formatEvaluator;
|
||||
private final Evaluation evaluation;
|
||||
private final long liveEdgeLatencyUs;
|
||||
|
||||
private final int maxWidth;
|
||||
private final int maxHeight;
|
||||
@ -65,7 +71,42 @@ public class SmoothStreamingChunkSource implements ChunkSource {
|
||||
private final SparseArray<FragmentedMp4Extractor> extractors;
|
||||
private final SmoothStreamingFormat[] formats;
|
||||
|
||||
private SmoothStreamingManifest currentManifest;
|
||||
private int currentManifestChunkOffset;
|
||||
private boolean finishedCurrentManifest;
|
||||
|
||||
private IOException fatalError;
|
||||
|
||||
/**
|
||||
* Constructor to use for live streaming.
|
||||
* <p>
|
||||
* May also be used for fixed duration content, in which case the call is equivalent to calling
|
||||
* the other constructor, passing {@code manifestFetcher.getManifest()} is the first argument.
|
||||
*
|
||||
* @param manifestFetcher A fetcher for the manifest, which must have already successfully
|
||||
* completed an initial load.
|
||||
* @param streamElementIndex The index of the stream element in the manifest to be provided by
|
||||
* the source.
|
||||
* @param trackIndices The indices of the tracks within the stream element to be considered by
|
||||
* the source. May be null if all tracks within the element should be considered.
|
||||
* @param dataSource A {@link DataSource} suitable for loading the media data.
|
||||
* @param formatEvaluator Selects from the available formats.
|
||||
* @param liveEdgeLatencyMs For live streams, the number of milliseconds that the playback should
|
||||
* lag behind the "live edge" (i.e. the end of the most recently defined media in the
|
||||
* manifest). Choosing a small value will minimize latency introduced by the player, however
|
||||
* note that the value sets an upper bound on the length of media that the player can buffer.
|
||||
* Hence a small value may increase the probability of rebuffering and playback failures.
|
||||
*/
|
||||
public SmoothStreamingChunkSource(ManifestFetcher<SmoothStreamingManifest> manifestFetcher,
|
||||
int streamElementIndex, int[] trackIndices, DataSource dataSource,
|
||||
FormatEvaluator formatEvaluator, long liveEdgeLatencyMs) {
|
||||
this(manifestFetcher, manifestFetcher.getManifest(), streamElementIndex, trackIndices,
|
||||
dataSource, formatEvaluator, liveEdgeLatencyMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor to use for fixed duration content.
|
||||
*
|
||||
* @param manifest The manifest parsed from {@code baseUrl + "/Manifest"}.
|
||||
* @param streamElementIndex The index of the stream element in the manifest to be provided by
|
||||
* the source.
|
||||
@ -76,14 +117,25 @@ public class SmoothStreamingChunkSource implements ChunkSource {
|
||||
*/
|
||||
public SmoothStreamingChunkSource(SmoothStreamingManifest manifest, int streamElementIndex,
|
||||
int[] trackIndices, DataSource dataSource, FormatEvaluator formatEvaluator) {
|
||||
this.streamElement = manifest.streamElements[streamElementIndex];
|
||||
this.trackInfo = new TrackInfo(streamElement.tracks[0].mimeType, manifest.durationUs);
|
||||
this(null, manifest, streamElementIndex, trackIndices, dataSource, formatEvaluator, 0);
|
||||
}
|
||||
|
||||
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.evaluation = new Evaluation();
|
||||
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 = manifest.protectionElement;
|
||||
ProtectionElement protectionElement = initialManifest.protectionElement;
|
||||
if (protectionElement != null) {
|
||||
byte[] keyId = getKeyId(protectionElement.data);
|
||||
trackEncryptionBoxes = new TrackEncryptionBox[1];
|
||||
@ -135,22 +187,52 @@ public class SmoothStreamingChunkSource implements ChunkSource {
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
// Do nothing.
|
||||
fatalError = null;
|
||||
if (manifestFetcher != null) {
|
||||
manifestFetcher.enable();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable(List<? extends MediaChunk> queue) {
|
||||
// Do nothing.
|
||||
if (manifestFetcher != null) {
|
||||
manifestFetcher.disable();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void continueBuffering(long playbackPositionUs) {
|
||||
// Do nothing
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
@ -166,30 +248,63 @@ public class SmoothStreamingChunkSource implements ChunkSource {
|
||||
return;
|
||||
}
|
||||
|
||||
int nextChunkIndex;
|
||||
if (queue.isEmpty()) {
|
||||
nextChunkIndex = streamElement.getChunkIndex(seekPositionUs);
|
||||
} else {
|
||||
nextChunkIndex = queue.get(out.queueSize - 1).nextChunkIndex;
|
||||
}
|
||||
// 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;
|
||||
|
||||
if (nextChunkIndex == -1) {
|
||||
out.chunk = null;
|
||||
StreamElement streamElement = getElement(currentManifest);
|
||||
if (streamElement.chunkCount == 0) {
|
||||
// The manifest is currently empty for this stream.
|
||||
finishedCurrentManifest = true;
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isLastChunk = nextChunkIndex == streamElement.chunkCount - 1;
|
||||
Uri uri = streamElement.buildRequestUri(selectedFormat.trackIndex, nextChunkIndex);
|
||||
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)), dataSource, nextChunkIndex,
|
||||
isLastChunk, streamElement.getStartTimeUs(nextChunkIndex),
|
||||
isLastChunk ? -1 : streamElement.getStartTimeUs(nextChunkIndex + 1), 0);
|
||||
extractors.get(Integer.parseInt(selectedFormat.id)), dataSource, currentAbsoluteChunkIndex,
|
||||
isLastChunk, chunkStartTimeUs, nextChunkStartTimeUs, 0);
|
||||
out.chunk = mediaChunk;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IOException getError() {
|
||||
return null;
|
||||
return fatalError != null ? fatalError
|
||||
: (manifestFetcher != null ? manifestFetcher.getError() : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -197,6 +312,30 @@ public class SmoothStreamingChunkSource implements ChunkSource {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
private StreamElement getElement(SmoothStreamingManifest manifest) {
|
||||
return manifest.streamElements[streamElementIndex];
|
||||
}
|
||||
|
||||
private static MediaFormat getMediaFormat(StreamElement streamElement, int trackIndex) {
|
||||
TrackElement trackElement = streamElement.tracks[trackIndex];
|
||||
String mimeType = trackElement.mimeType;
|
||||
|
Loading…
x
Reference in New Issue
Block a user