parent
e21f7801b5
commit
0d69a2eae8
@ -217,7 +217,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
|
||||
return new DashRendererBuilder(userAgent, contentUri.toString(),
|
||||
new WidevineTestMediaDrmCallback(contentId), debugTextView, audioCapabilities);
|
||||
case DemoUtil.TYPE_HLS:
|
||||
return new HlsRendererBuilder(userAgent, contentUri.toString());
|
||||
return new HlsRendererBuilder(userAgent, contentUri.toString(), debugTextView);
|
||||
case DemoUtil.TYPE_MP4:
|
||||
return new ExtractorRendererBuilder(userAgent, contentUri, debugTextView,
|
||||
new Mp4Extractor());
|
||||
|
@ -248,7 +248,7 @@ public class DashRendererBuilder implements RendererBuilder,
|
||||
videoRenderer = new MediaCodecVideoTrackRenderer(videoSampleSource, drmSessionManager, true,
|
||||
MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, null, mainHandler, player, 50);
|
||||
debugRenderer = debugTextView != null
|
||||
? new DebugTrackRenderer(debugTextView, videoRenderer, videoSampleSource) : null;
|
||||
? new DebugTrackRenderer(debugTextView, player, videoRenderer) : null;
|
||||
}
|
||||
|
||||
// Build the audio chunk sources.
|
||||
|
@ -18,7 +18,6 @@ package com.google.android.exoplayer.demo.player;
|
||||
import com.google.android.exoplayer.ExoPlaybackException;
|
||||
import com.google.android.exoplayer.MediaCodecTrackRenderer;
|
||||
import com.google.android.exoplayer.TrackRenderer;
|
||||
import com.google.android.exoplayer.chunk.ChunkSampleSource;
|
||||
import com.google.android.exoplayer.chunk.Format;
|
||||
|
||||
import android.widget.TextView;
|
||||
@ -30,21 +29,17 @@ import android.widget.TextView;
|
||||
/* package */ class DebugTrackRenderer extends TrackRenderer implements Runnable {
|
||||
|
||||
private final TextView textView;
|
||||
private final DemoPlayer player;
|
||||
private final MediaCodecTrackRenderer renderer;
|
||||
private final ChunkSampleSource videoSampleSource;
|
||||
|
||||
private volatile boolean pendingFailure;
|
||||
private volatile long currentPositionUs;
|
||||
|
||||
public DebugTrackRenderer(TextView textView, MediaCodecTrackRenderer renderer) {
|
||||
this(textView, renderer, null);
|
||||
}
|
||||
|
||||
public DebugTrackRenderer(TextView textView, MediaCodecTrackRenderer renderer,
|
||||
ChunkSampleSource videoSampleSource) {
|
||||
public DebugTrackRenderer(TextView textView, DemoPlayer player,
|
||||
MediaCodecTrackRenderer renderer) {
|
||||
this.textView = textView;
|
||||
this.player = player;
|
||||
this.renderer = renderer;
|
||||
this.videoSampleSource = videoSampleSource;
|
||||
}
|
||||
|
||||
public void injectFailure() {
|
||||
@ -82,13 +77,13 @@ import android.widget.TextView;
|
||||
}
|
||||
|
||||
private String getRenderString() {
|
||||
return "ms(" + (currentPositionUs / 1000) + "), " + getQualityString()
|
||||
+ ", " + renderer.codecCounters.getDebugString();
|
||||
return getQualityString() + " " + renderer.codecCounters.getDebugString();
|
||||
}
|
||||
|
||||
private String getQualityString() {
|
||||
Format format = videoSampleSource == null ? null : videoSampleSource.getFormat();
|
||||
return format == null ? "null" : "height(" + format.height + "), itag(" + format.id + ")";
|
||||
Format format = player.getVideoFormat();
|
||||
return format == null ? "id:? br:? h:?"
|
||||
: "id:" + format.id + " br:" + format.bitrate + " h:" + format.height;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -55,8 +55,7 @@ public class DefaultRendererBuilder implements RendererBuilder {
|
||||
|
||||
// Build the debug renderer.
|
||||
TrackRenderer debugRenderer = debugTextView != null
|
||||
? new DebugTrackRenderer(debugTextView, videoRenderer)
|
||||
: null;
|
||||
? new DebugTrackRenderer(debugTextView, player, videoRenderer) : null;
|
||||
|
||||
// Invoke the callback.
|
||||
TrackRenderer[] renderers = new TrackRenderer[DemoPlayer.RENDERER_COUNT];
|
||||
|
@ -28,6 +28,7 @@ import com.google.android.exoplayer.chunk.ChunkSampleSource;
|
||||
import com.google.android.exoplayer.chunk.Format;
|
||||
import com.google.android.exoplayer.chunk.MultiTrackChunkSource;
|
||||
import com.google.android.exoplayer.drm.StreamingDrmSessionManager;
|
||||
import com.google.android.exoplayer.hls.HlsSampleSource;
|
||||
import com.google.android.exoplayer.metadata.MetadataTrackRenderer;
|
||||
import com.google.android.exoplayer.text.TextRenderer;
|
||||
import com.google.android.exoplayer.upstream.DefaultBandwidthMeter;
|
||||
@ -48,9 +49,10 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||
* SmoothStreaming and so on).
|
||||
*/
|
||||
public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventListener,
|
||||
DefaultBandwidthMeter.EventListener, MediaCodecVideoTrackRenderer.EventListener,
|
||||
MediaCodecAudioTrackRenderer.EventListener, Ac3PassthroughAudioTrackRenderer.EventListener,
|
||||
TextRenderer, StreamingDrmSessionManager.EventListener {
|
||||
HlsSampleSource.EventListener, DefaultBandwidthMeter.EventListener,
|
||||
MediaCodecVideoTrackRenderer.EventListener, MediaCodecAudioTrackRenderer.EventListener,
|
||||
Ac3PassthroughAudioTrackRenderer.EventListener, StreamingDrmSessionManager.EventListener,
|
||||
TextRenderer {
|
||||
|
||||
/**
|
||||
* Builds renderers for the player.
|
||||
@ -181,6 +183,7 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
|
||||
private Surface surface;
|
||||
private InternalRendererBuilderCallback builderCallback;
|
||||
private TrackRenderer videoRenderer;
|
||||
private Format videoFormat;
|
||||
private int videoTrackToRestore;
|
||||
|
||||
private MultiTrackChunkSource[] multiTrackSources;
|
||||
@ -268,6 +271,10 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
|
||||
}
|
||||
}
|
||||
|
||||
public Format getVideoFormat() {
|
||||
return videoFormat;
|
||||
}
|
||||
|
||||
public void setBackgrounded(boolean backgrounded) {
|
||||
if (this.backgrounded == backgrounded) {
|
||||
return;
|
||||
@ -291,6 +298,7 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
|
||||
}
|
||||
rendererBuildingState = RENDERER_BUILDING_STATE_BUILDING;
|
||||
maybeReportPlayerState();
|
||||
videoFormat = null;
|
||||
builderCallback = new InternalRendererBuilderCallback();
|
||||
rendererBuilder.buildRenderers(this, builderCallback);
|
||||
}
|
||||
@ -437,6 +445,7 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
|
||||
return;
|
||||
}
|
||||
if (sourceId == TYPE_VIDEO) {
|
||||
videoFormat = format;
|
||||
infoListener.onVideoFormatEnabled(format, trigger, mediaTimeMs);
|
||||
} else if (sourceId == TYPE_AUDIO) {
|
||||
infoListener.onAudioFormatEnabled(format, trigger, mediaTimeMs);
|
||||
|
@ -63,8 +63,7 @@ public class ExtractorRendererBuilder implements RendererBuilder {
|
||||
|
||||
// Build the debug renderer.
|
||||
TrackRenderer debugRenderer = debugTextView != null
|
||||
? new DebugTrackRenderer(debugTextView, videoRenderer)
|
||||
: null;
|
||||
? new DebugTrackRenderer(debugTextView, player, videoRenderer) : null;
|
||||
|
||||
// Invoke the callback.
|
||||
TrackRenderer[] renderers = new TrackRenderer[DemoPlayer.RENDERER_COUNT];
|
||||
|
@ -35,6 +35,8 @@ import com.google.android.exoplayer.util.ManifestFetcher;
|
||||
import com.google.android.exoplayer.util.ManifestFetcher.ManifestCallback;
|
||||
|
||||
import android.media.MediaCodec;
|
||||
import android.os.Handler;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
@ -46,13 +48,15 @@ public class HlsRendererBuilder implements RendererBuilder, ManifestCallback<Hls
|
||||
|
||||
private final String userAgent;
|
||||
private final String url;
|
||||
private final TextView debugTextView;
|
||||
|
||||
private DemoPlayer player;
|
||||
private RendererBuilderCallback callback;
|
||||
|
||||
public HlsRendererBuilder(String userAgent, String url) {
|
||||
public HlsRendererBuilder(String userAgent, String url, TextView debugTextView) {
|
||||
this.userAgent = userAgent;
|
||||
this.url = url;
|
||||
this.debugTextView = debugTextView;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -72,28 +76,35 @@ public class HlsRendererBuilder implements RendererBuilder, ManifestCallback<Hls
|
||||
|
||||
@Override
|
||||
public void onSingleManifest(HlsPlaylist manifest) {
|
||||
Handler mainHandler = player.getMainHandler();
|
||||
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
|
||||
|
||||
DataSource dataSource = new UriDataSource(userAgent, bandwidthMeter);
|
||||
HlsChunkSource chunkSource = new HlsChunkSource(dataSource, url, manifest, bandwidthMeter, null,
|
||||
HlsChunkSource.ADAPTIVE_MODE_SPLICE);
|
||||
HlsSampleSource sampleSource = new HlsSampleSource(chunkSource, true, 3);
|
||||
HlsSampleSource sampleSource = new HlsSampleSource(chunkSource, true, 3, mainHandler, player,
|
||||
DemoPlayer.TYPE_VIDEO);
|
||||
MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(sampleSource,
|
||||
MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, player.getMainHandler(), player, 50);
|
||||
MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, mainHandler, player, 50);
|
||||
MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource);
|
||||
|
||||
MetadataTrackRenderer<Map<String, Object>> id3Renderer =
|
||||
new MetadataTrackRenderer<Map<String, Object>>(sampleSource, new Id3Parser(),
|
||||
player.getId3MetadataRenderer(), player.getMainHandler().getLooper());
|
||||
player.getId3MetadataRenderer(), mainHandler.getLooper());
|
||||
|
||||
Eia608TrackRenderer closedCaptionRenderer = new Eia608TrackRenderer(sampleSource, player,
|
||||
player.getMainHandler().getLooper());
|
||||
mainHandler.getLooper());
|
||||
|
||||
// Build the debug renderer.
|
||||
TrackRenderer debugRenderer = debugTextView != null
|
||||
? new DebugTrackRenderer(debugTextView, player, videoRenderer) : null;
|
||||
|
||||
TrackRenderer[] renderers = new TrackRenderer[DemoPlayer.RENDERER_COUNT];
|
||||
renderers[DemoPlayer.TYPE_VIDEO] = videoRenderer;
|
||||
renderers[DemoPlayer.TYPE_AUDIO] = audioRenderer;
|
||||
renderers[DemoPlayer.TYPE_TIMED_METADATA] = id3Renderer;
|
||||
renderers[DemoPlayer.TYPE_TEXT] = closedCaptionRenderer;
|
||||
renderers[DemoPlayer.TYPE_DEBUG] = debugRenderer;
|
||||
callback.onRenderers(null, null, renderers);
|
||||
}
|
||||
|
||||
|
@ -174,8 +174,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
|
||||
videoRenderer = new MediaCodecVideoTrackRenderer(videoSampleSource, drmSessionManager, true,
|
||||
MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, null, mainHandler, player, 50);
|
||||
debugRenderer = debugTextView != null
|
||||
? new DebugTrackRenderer(debugTextView, videoRenderer, videoSampleSource)
|
||||
: null;
|
||||
? new DebugTrackRenderer(debugTextView, player, videoRenderer) : null;
|
||||
}
|
||||
|
||||
// Build the audio renderer.
|
||||
|
@ -45,13 +45,13 @@ public final class CodecCounters {
|
||||
public String getDebugString() {
|
||||
ensureUpdated();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("cic(").append(codecInitCount).append(")");
|
||||
builder.append("crc(").append(codecReleaseCount).append(")");
|
||||
builder.append("ofc(").append(outputFormatChangedCount).append(")");
|
||||
builder.append("obc(").append(outputBuffersChangedCount).append(")");
|
||||
builder.append("ren(").append(renderedOutputBufferCount).append(")");
|
||||
builder.append("sob(").append(skippedOutputBufferCount).append(")");
|
||||
builder.append("dob(").append(droppedOutputBufferCount).append(")");
|
||||
builder.append("cic:").append(codecInitCount);
|
||||
builder.append(" crc:").append(codecReleaseCount);
|
||||
builder.append(" ofc:").append(outputFormatChangedCount);
|
||||
builder.append(" obc:").append(outputBuffersChangedCount);
|
||||
builder.append(" ren:").append(renderedOutputBufferCount);
|
||||
builder.append(" sob:").append(skippedOutputBufferCount);
|
||||
builder.append(" dob:").append(droppedOutputBufferCount);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
|
@ -124,6 +124,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
|
||||
*
|
||||
* @return The current downstream format.
|
||||
*/
|
||||
@Deprecated
|
||||
public Format getFormat() {
|
||||
return downstreamFormat;
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer.hls;
|
||||
|
||||
import com.google.android.exoplayer.C;
|
||||
import com.google.android.exoplayer.MediaFormat;
|
||||
import com.google.android.exoplayer.chunk.BaseChunkSampleSourceEventListener;
|
||||
import com.google.android.exoplayer.chunk.Chunk;
|
||||
import com.google.android.exoplayer.chunk.DataChunk;
|
||||
import com.google.android.exoplayer.chunk.Format;
|
||||
@ -55,6 +56,11 @@ import java.util.Locale;
|
||||
*/
|
||||
public class HlsChunkSource {
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be notified of {@link HlsChunkSource} events.
|
||||
*/
|
||||
public interface EventListener extends BaseChunkSampleSourceEventListener {}
|
||||
|
||||
/**
|
||||
* Adaptive switching is disabled.
|
||||
* <p>
|
||||
@ -349,6 +355,8 @@ public class HlsChunkSource {
|
||||
}
|
||||
long endTimeUs = startTimeUs + (long) (segment.durationSecs * C.MICROS_PER_SECOND);
|
||||
boolean isLastChunk = !mediaPlaylist.live && chunkIndex == mediaPlaylist.segments.size() - 1;
|
||||
int trigger = Chunk.TRIGGER_UNSPECIFIED;
|
||||
Format format = enabledFormats[formatIndex];
|
||||
|
||||
// Configure the extractor that will read the chunk.
|
||||
HlsExtractorWrapper extractorWrapper;
|
||||
@ -356,13 +364,14 @@ public class HlsChunkSource {
|
||||
Extractor extractor = chunkUri.getLastPathSegment().endsWith(AAC_FILE_EXTENSION)
|
||||
? new AdtsExtractor(startTimeUs)
|
||||
: new TsExtractor(startTimeUs);
|
||||
extractorWrapper = new HlsExtractorWrapper(bufferPool, extractor, switchingVariantSpliced);
|
||||
extractorWrapper = new HlsExtractorWrapper(trigger, format, startTimeUs, bufferPool,
|
||||
extractor, switchingVariantSpliced);
|
||||
} else {
|
||||
extractorWrapper = previousTsChunk.extractorWrapper;
|
||||
}
|
||||
|
||||
return new TsChunk(dataSource, dataSpec, Chunk.TRIGGER_UNSPECIFIED, enabledFormats[formatIndex],
|
||||
startTimeUs, endTimeUs, chunkMediaSequence, isLastChunk, extractorWrapper, encryptionKey,
|
||||
return new TsChunk(dataSource, dataSpec, trigger, format, startTimeUs, endTimeUs,
|
||||
chunkMediaSequence, isLastChunk, extractorWrapper, encryptionKey,
|
||||
encryptionIv);
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer.hls;
|
||||
|
||||
import com.google.android.exoplayer.MediaFormat;
|
||||
import com.google.android.exoplayer.SampleHolder;
|
||||
import com.google.android.exoplayer.chunk.Format;
|
||||
import com.google.android.exoplayer.drm.DrmInitData;
|
||||
import com.google.android.exoplayer.extractor.DefaultTrackOutput;
|
||||
import com.google.android.exoplayer.extractor.Extractor;
|
||||
@ -36,6 +37,10 @@ import java.io.IOException;
|
||||
*/
|
||||
public final class HlsExtractorWrapper implements ExtractorOutput {
|
||||
|
||||
public final int trigger;
|
||||
public final Format format;
|
||||
public final long startTimeUs;
|
||||
|
||||
private final BufferPool bufferPool;
|
||||
private final Extractor extractor;
|
||||
private final SparseArray<DefaultTrackOutput> sampleQueues;
|
||||
@ -47,7 +52,11 @@ public final class HlsExtractorWrapper implements ExtractorOutput {
|
||||
private boolean prepared;
|
||||
private boolean spliceConfigured;
|
||||
|
||||
public HlsExtractorWrapper(BufferPool bufferPool, Extractor extractor, boolean shouldSpliceIn) {
|
||||
public HlsExtractorWrapper(int trigger, Format format, long startTimeUs, BufferPool bufferPool,
|
||||
Extractor extractor, boolean shouldSpliceIn) {
|
||||
this.trigger = trigger;
|
||||
this.format = format;
|
||||
this.startTimeUs = startTimeUs;
|
||||
this.bufferPool = bufferPool;
|
||||
this.extractor = extractor;
|
||||
this.shouldSpliceIn = shouldSpliceIn;
|
||||
@ -98,14 +107,14 @@ public final class HlsExtractorWrapper implements ExtractorOutput {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the format of the specified track.
|
||||
* Gets the {@link MediaFormat} of the specified track.
|
||||
* <p>
|
||||
* This method must only be called after the extractor has been prepared.
|
||||
*
|
||||
* @param track The track index.
|
||||
* @return The corresponding format.
|
||||
*/
|
||||
public MediaFormat getFormat(int track) {
|
||||
public MediaFormat getMediaFormat(int track) {
|
||||
return sampleQueues.valueAt(track).getFormat();
|
||||
}
|
||||
|
||||
|
@ -22,11 +22,14 @@ import com.google.android.exoplayer.SampleHolder;
|
||||
import com.google.android.exoplayer.SampleSource;
|
||||
import com.google.android.exoplayer.TrackInfo;
|
||||
import com.google.android.exoplayer.TrackRenderer;
|
||||
import com.google.android.exoplayer.chunk.BaseChunkSampleSourceEventListener;
|
||||
import com.google.android.exoplayer.chunk.Chunk;
|
||||
import com.google.android.exoplayer.chunk.Format;
|
||||
import com.google.android.exoplayer.upstream.Loader;
|
||||
import com.google.android.exoplayer.upstream.Loader.Loadable;
|
||||
import com.google.android.exoplayer.util.Assertions;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -37,6 +40,11 @@ import java.util.LinkedList;
|
||||
*/
|
||||
public class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be notified of {@link HlsSampleSource} events.
|
||||
*/
|
||||
public interface EventListener extends BaseChunkSampleSourceEventListener {}
|
||||
|
||||
/**
|
||||
* The default minimum number of times to retry loading data prior to failing.
|
||||
*/
|
||||
@ -49,6 +57,10 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
private final boolean frameAccurateSeeking;
|
||||
private final int minLoadableRetryCount;
|
||||
|
||||
private final int eventSourceId;
|
||||
private final Handler eventHandler;
|
||||
private final EventListener eventListener;
|
||||
|
||||
private int remainingReleaseCount;
|
||||
private boolean prepared;
|
||||
private int trackCount;
|
||||
@ -57,6 +69,7 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
private boolean[] pendingDiscontinuities;
|
||||
private TrackInfo[] trackInfos;
|
||||
private MediaFormat[] downstreamMediaFormats;
|
||||
private Format downstreamFormat;
|
||||
|
||||
private long downstreamPositionUs;
|
||||
private long lastSeekPositionUs;
|
||||
@ -74,16 +87,26 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
|
||||
public HlsSampleSource(HlsChunkSource chunkSource, boolean frameAccurateSeeking,
|
||||
int downstreamRendererCount) {
|
||||
this(chunkSource, frameAccurateSeeking, downstreamRendererCount,
|
||||
DEFAULT_MIN_LOADABLE_RETRY_COUNT);
|
||||
this(chunkSource, frameAccurateSeeking, downstreamRendererCount, null, null, 0);
|
||||
}
|
||||
|
||||
public HlsSampleSource(HlsChunkSource chunkSource, boolean frameAccurateSeeking,
|
||||
int downstreamRendererCount, int minLoadableRetryCount) {
|
||||
int downstreamRendererCount, Handler eventHandler, EventListener eventListener,
|
||||
int eventSourceId) {
|
||||
this(chunkSource, frameAccurateSeeking, downstreamRendererCount, eventHandler, eventListener,
|
||||
eventSourceId, DEFAULT_MIN_LOADABLE_RETRY_COUNT);
|
||||
}
|
||||
|
||||
public HlsSampleSource(HlsChunkSource chunkSource, boolean frameAccurateSeeking,
|
||||
int downstreamRendererCount, Handler eventHandler, EventListener eventListener,
|
||||
int eventSourceId, int minLoadableRetryCount) {
|
||||
this.chunkSource = chunkSource;
|
||||
this.frameAccurateSeeking = frameAccurateSeeking;
|
||||
this.remainingReleaseCount = downstreamRendererCount;
|
||||
this.minLoadableRetryCount = minLoadableRetryCount;
|
||||
this.eventHandler = eventHandler;
|
||||
this.eventListener = eventListener;
|
||||
this.eventSourceId = eventSourceId;
|
||||
this.pendingResetPositionUs = NO_RESET_PENDING;
|
||||
extractors = new LinkedList<HlsExtractorWrapper>();
|
||||
}
|
||||
@ -106,7 +129,7 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
downstreamMediaFormats = new MediaFormat[trackCount];
|
||||
trackInfos = new TrackInfo[trackCount];
|
||||
for (int i = 0; i < trackCount; i++) {
|
||||
MediaFormat format = extractor.getFormat(i);
|
||||
MediaFormat format = extractor.getMediaFormat(i);
|
||||
trackInfos[i] = new TrackInfo(format.mimeType, chunkSource.getDurationUs());
|
||||
}
|
||||
prepared = true;
|
||||
@ -137,6 +160,7 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
enabledTrackCount++;
|
||||
trackEnabledStates[track] = true;
|
||||
downstreamMediaFormats[track] = null;
|
||||
downstreamFormat = null;
|
||||
if (enabledTrackCount == 1) {
|
||||
seekToUs(positionUs);
|
||||
}
|
||||
@ -202,6 +226,13 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
}
|
||||
|
||||
HlsExtractorWrapper extractor = getCurrentExtractor();
|
||||
|
||||
if (downstreamFormat == null || !downstreamFormat.equals(extractor.format)) {
|
||||
// Notify a change in the downstream format.
|
||||
notifyDownstreamFormatChanged(extractor.format, extractor.trigger, extractor.startTimeUs);
|
||||
downstreamFormat = extractor.format;
|
||||
}
|
||||
|
||||
if (extractors.size() > 1) {
|
||||
// If there's more than one extractor, attempt to configure a seamless splice from the
|
||||
// current one to the next one.
|
||||
@ -220,7 +251,7 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
return NOTHING_READ;
|
||||
}
|
||||
|
||||
MediaFormat mediaFormat = extractor.getFormat(track);
|
||||
MediaFormat mediaFormat = extractor.getMediaFormat(track);
|
||||
if (mediaFormat != null && !mediaFormat.equals(downstreamMediaFormats[track], true)) {
|
||||
chunkSource.getMaxVideoDimensions(mediaFormat);
|
||||
formatHolder.format = mediaFormat;
|
||||
@ -286,6 +317,7 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
@Override
|
||||
public void onLoadCompleted(Loadable loadable) {
|
||||
chunkSource.onChunkLoadCompleted(currentLoadable);
|
||||
notifyLoadCompleted(currentLoadable.bytesLoaded());
|
||||
if (isTsChunk(currentLoadable)) {
|
||||
TsChunk tsChunk = (TsChunk) loadable;
|
||||
loadingFinished = tsChunk.isLastChunk;
|
||||
@ -298,6 +330,7 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
|
||||
@Override
|
||||
public void onLoadCanceled(Loadable loadable) {
|
||||
notifyLoadCanceled(currentLoadable.bytesLoaded());
|
||||
if (enabledTrackCount > 0) {
|
||||
restartFrom(pendingResetPositionUs);
|
||||
} else {
|
||||
@ -315,6 +348,7 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
currentLoadableExceptionCount++;
|
||||
currentLoadableExceptionTimestamp = SystemClock.elapsedRealtime();
|
||||
}
|
||||
notifyLoadError(e);
|
||||
maybeStartLoading();
|
||||
}
|
||||
|
||||
@ -418,13 +452,19 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
|
||||
currentLoadable = nextLoadable;
|
||||
if (isTsChunk(currentLoadable)) {
|
||||
previousTsLoadable = (TsChunk) currentLoadable;
|
||||
TsChunk tsChunk = (TsChunk) currentLoadable;
|
||||
if (isPendingReset()) {
|
||||
pendingResetPositionUs = NO_RESET_PENDING;
|
||||
}
|
||||
if (extractors.isEmpty() || extractors.getLast() != previousTsLoadable.extractorWrapper) {
|
||||
extractors.addLast(previousTsLoadable.extractorWrapper);
|
||||
if (extractors.isEmpty() || extractors.getLast() != tsChunk.extractorWrapper) {
|
||||
extractors.addLast(tsChunk.extractorWrapper);
|
||||
}
|
||||
notifyLoadStarted(tsChunk.dataSpec.length, tsChunk.type, tsChunk.trigger, tsChunk.format,
|
||||
tsChunk.startTimeUs, tsChunk.endTimeUs);
|
||||
previousTsLoadable = tsChunk;
|
||||
} else {
|
||||
notifyLoadStarted(currentLoadable.dataSpec.length, currentLoadable.type,
|
||||
currentLoadable.trigger, currentLoadable.format, -1, -1);
|
||||
}
|
||||
loader.startLoading(currentLoadable, this);
|
||||
}
|
||||
@ -445,4 +485,63 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
return (int) (timeUs / 1000);
|
||||
}
|
||||
|
||||
private void notifyLoadStarted(final long length, final int type, final int trigger,
|
||||
final Format format, final long mediaStartTimeUs, final long mediaEndTimeUs) {
|
||||
if (eventHandler != null && eventListener != null) {
|
||||
eventHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
eventListener.onLoadStarted(eventSourceId, length, type, trigger, format,
|
||||
usToMs(mediaStartTimeUs), usToMs(mediaEndTimeUs));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyLoadCompleted(final long bytesLoaded) {
|
||||
if (eventHandler != null && eventListener != null) {
|
||||
eventHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
eventListener.onLoadCompleted(eventSourceId, bytesLoaded);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyLoadCanceled(final long bytesLoaded) {
|
||||
if (eventHandler != null && eventListener != null) {
|
||||
eventHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
eventListener.onLoadCanceled(eventSourceId, bytesLoaded);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyLoadError(final IOException e) {
|
||||
if (eventHandler != null && eventListener != null) {
|
||||
eventHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
eventListener.onLoadError(eventSourceId, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyDownstreamFormatChanged(final Format format, final int trigger,
|
||||
final long positionUs) {
|
||||
if (eventHandler != null && eventListener != null) {
|
||||
eventHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
eventListener.onDownstreamFormatChanged(eventSourceId, format, trigger,
|
||||
usToMs(positionUs));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user