Implement event reporting from HlsSampleSource.

Issue: #275
This commit is contained in:
Oliver Woodman 2015-04-13 19:03:04 +01:00
parent e21f7801b5
commit 0d69a2eae8
13 changed files with 180 additions and 50 deletions

View File

@ -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());

View File

@ -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.

View File

@ -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

View File

@ -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];

View File

@ -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);

View File

@ -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];

View File

@ -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);
}

View File

@ -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.

View File

@ -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();
}

View File

@ -124,6 +124,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
*
* @return The current downstream format.
*/
@Deprecated
public Format getFormat() {
return downstreamFormat;
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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));
}
});
}
}
}