Merge branch 'dev-v2' into dev-v2-id3

This commit is contained in:
Oliver Woodman 2016-10-27 12:37:29 +01:00
commit 0c8f3c7270
35 changed files with 668 additions and 376 deletions

View File

@ -40,10 +40,10 @@ import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener;
import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo; import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo;
import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelections; import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.video.VideoRendererEventListener; import com.google.android.exoplayer2.video.VideoRendererEventListener;
import java.io.IOException; import java.io.IOException;
@ -56,7 +56,7 @@ import java.util.Locale;
/* package */ final class EventLogger implements ExoPlayer.EventListener, /* package */ final class EventLogger implements ExoPlayer.EventListener,
AudioRendererEventListener, VideoRendererEventListener, AdaptiveMediaSourceEventListener, AudioRendererEventListener, VideoRendererEventListener, AdaptiveMediaSourceEventListener,
ExtractorMediaSource.EventListener, StreamingDrmSessionManager.EventListener, ExtractorMediaSource.EventListener, StreamingDrmSessionManager.EventListener,
TrackSelector.EventListener<MappedTrackInfo>, MetadataRenderer.Output { MetadataRenderer.Output {
private static final String TAG = "EventLogger"; private static final String TAG = "EventLogger";
private static final int MAX_TIMELINE_ITEM_LINES = 3; private static final int MAX_TIMELINE_ITEM_LINES = 3;
@ -68,11 +68,13 @@ import java.util.Locale;
TIME_FORMAT.setGroupingUsed(false); TIME_FORMAT.setGroupingUsed(false);
} }
private final MappingTrackSelector trackSelector;
private final Timeline.Window window; private final Timeline.Window window;
private final Timeline.Period period; private final Timeline.Period period;
private final long startTimeMs; private final long startTimeMs;
public EventLogger() { public EventLogger(MappingTrackSelector trackSelector) {
this.trackSelector = trackSelector;
window = new Timeline.Window(); window = new Timeline.Window();
period = new Timeline.Period(); period = new Timeline.Period();
startTimeMs = SystemClock.elapsedRealtime(); startTimeMs = SystemClock.elapsedRealtime();
@ -127,27 +129,29 @@ import java.util.Locale;
Log.e(TAG, "playerFailed [" + getSessionTimeString() + "]", e); Log.e(TAG, "playerFailed [" + getSessionTimeString() + "]", e);
} }
// MappingTrackSelector.EventListener
@Override @Override
public void onTrackSelectionsChanged(TrackSelections<? extends MappedTrackInfo> trackSelections) { public void onTracksChanged(TrackGroupArray ignored, TrackSelectionArray trackSelections) {
MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo();
if (mappedTrackInfo == null) {
Log.d(TAG, "Tracks []");
return;
}
Log.d(TAG, "Tracks ["); Log.d(TAG, "Tracks [");
// Log tracks associated to renderers. // Log tracks associated to renderers.
MappedTrackInfo info = trackSelections.info; for (int rendererIndex = 0; rendererIndex < mappedTrackInfo.length; rendererIndex++) {
for (int rendererIndex = 0; rendererIndex < trackSelections.length; rendererIndex++) { TrackGroupArray rendererTrackGroups = mappedTrackInfo.getTrackGroups(rendererIndex);
TrackGroupArray trackGroups = info.getTrackGroups(rendererIndex);
TrackSelection trackSelection = trackSelections.get(rendererIndex); TrackSelection trackSelection = trackSelections.get(rendererIndex);
if (trackGroups.length > 0) { if (rendererTrackGroups.length > 0) {
Log.d(TAG, " Renderer:" + rendererIndex + " ["); Log.d(TAG, " Renderer:" + rendererIndex + " [");
for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) { for (int groupIndex = 0; groupIndex < rendererTrackGroups.length; groupIndex++) {
TrackGroup trackGroup = trackGroups.get(groupIndex); TrackGroup trackGroup = rendererTrackGroups.get(groupIndex);
String adaptiveSupport = getAdaptiveSupportString( String adaptiveSupport = getAdaptiveSupportString(trackGroup.length,
trackGroup.length, info.getAdaptiveSupport(rendererIndex, groupIndex, false)); mappedTrackInfo.getAdaptiveSupport(rendererIndex, groupIndex, false));
Log.d(TAG, " Group:" + groupIndex + ", adaptive_supported=" + adaptiveSupport + " ["); Log.d(TAG, " Group:" + groupIndex + ", adaptive_supported=" + adaptiveSupport + " [");
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
String status = getTrackStatusString(trackSelection, trackGroup, trackIndex); String status = getTrackStatusString(trackSelection, trackGroup, trackIndex);
String formatSupport = getFormatSupportString( String formatSupport = getFormatSupportString(
info.getTrackFormatSupport(rendererIndex, groupIndex, trackIndex)); mappedTrackInfo.getTrackFormatSupport(rendererIndex, groupIndex, trackIndex));
Log.d(TAG, " " + status + " Track:" + trackIndex + ", " Log.d(TAG, " " + status + " Track:" + trackIndex + ", "
+ getFormatString(trackGroup.getFormat(trackIndex)) + getFormatString(trackGroup.getFormat(trackIndex))
+ ", supported=" + formatSupport); + ", supported=" + formatSupport);
@ -170,12 +174,12 @@ import java.util.Locale;
} }
} }
// Log tracks not associated with a renderer. // Log tracks not associated with a renderer.
TrackGroupArray trackGroups = info.getUnassociatedTrackGroups(); TrackGroupArray unassociatedTrackGroups = mappedTrackInfo.getUnassociatedTrackGroups();
if (trackGroups.length > 0) { if (unassociatedTrackGroups.length > 0) {
Log.d(TAG, " Renderer:None ["); Log.d(TAG, " Renderer:None [");
for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) { for (int groupIndex = 0; groupIndex < unassociatedTrackGroups.length; groupIndex++) {
Log.d(TAG, " Group:" + groupIndex + " ["); Log.d(TAG, " Group:" + groupIndex + " [");
TrackGroup trackGroup = trackGroups.get(groupIndex); TrackGroup trackGroup = unassociatedTrackGroups.get(groupIndex);
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
String status = getTrackStatusString(false); String status = getTrackStatusString(false);
String formatSupport = getFormatSupportString( String formatSupport = getFormatSupportString(

View File

@ -58,8 +58,7 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.MappingTrackSelector; import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo; import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo;
import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelections; import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.ui.DebugTextViewHelper; import com.google.android.exoplayer2.ui.DebugTextViewHelper;
import com.google.android.exoplayer2.ui.PlaybackControlView; import com.google.android.exoplayer2.ui.PlaybackControlView;
import com.google.android.exoplayer2.ui.SimpleExoPlayerView; import com.google.android.exoplayer2.ui.SimpleExoPlayerView;
@ -78,7 +77,7 @@ import java.util.UUID;
* An activity that plays media using {@link SimpleExoPlayer}. * An activity that plays media using {@link SimpleExoPlayer}.
*/ */
public class PlayerActivity extends Activity implements OnClickListener, ExoPlayer.EventListener, public class PlayerActivity extends Activity implements OnClickListener, ExoPlayer.EventListener,
TrackSelector.EventListener<MappedTrackInfo>, PlaybackControlView.VisibilityListener { PlaybackControlView.VisibilityListener {
public static final String DRM_SCHEME_UUID_EXTRA = "drm_scheme_uuid"; public static final String DRM_SCHEME_UUID_EXTRA = "drm_scheme_uuid";
public static final String DRM_LICENSE_URL = "drm_license_url"; public static final String DRM_LICENSE_URL = "drm_license_url";
@ -203,8 +202,11 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
if (view == retryButton) { if (view == retryButton) {
initializePlayer(); initializePlayer();
} else if (view.getParent() == debugRootView) { } else if (view.getParent() == debugRootView) {
trackSelectionHelper.showSelectionDialog(this, ((Button) view).getText(), MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo();
trackSelector.getCurrentSelections().info, (int) view.getTag()); if (mappedTrackInfo != null) {
trackSelectionHelper.showSelectionDialog(this, ((Button) view).getText(),
trackSelector.getCurrentMappedTrackInfo(), (int) view.getTag());
}
} }
} }
@ -249,20 +251,20 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
} }
} }
eventLogger = new EventLogger();
TrackSelection.Factory videoTrackSelectionFactory = TrackSelection.Factory videoTrackSelectionFactory =
new AdaptiveVideoTrackSelection.Factory(BANDWIDTH_METER); new AdaptiveVideoTrackSelection.Factory(BANDWIDTH_METER);
trackSelector = new DefaultTrackSelector(mainHandler, videoTrackSelectionFactory); trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
trackSelector.addListener(this);
trackSelector.addListener(eventLogger);
trackSelectionHelper = new TrackSelectionHelper(trackSelector, videoTrackSelectionFactory); trackSelectionHelper = new TrackSelectionHelper(trackSelector, videoTrackSelectionFactory);
player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, new DefaultLoadControl(), player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, new DefaultLoadControl(),
drmSessionManager, preferExtensionDecoders); drmSessionManager, preferExtensionDecoders);
player.addListener(this); player.addListener(this);
eventLogger = new EventLogger(trackSelector);
player.addListener(eventLogger); player.addListener(eventLogger);
player.setAudioDebugListener(eventLogger); player.setAudioDebugListener(eventLogger);
player.setVideoDebugListener(eventLogger); player.setVideoDebugListener(eventLogger);
player.setId3Output(eventLogger); player.setId3Output(eventLogger);
simpleExoPlayerView.setPlayer(player); simpleExoPlayerView.setPlayer(player);
if (isTimelineStatic) { if (isTimelineStatic) {
if (playerPosition == C.TIME_UNSET) { if (playerPosition == C.TIME_UNSET) {
@ -447,17 +449,17 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
showControls(); showControls();
} }
// MappingTrackSelector.EventListener implementation
@Override @Override
public void onTrackSelectionsChanged(TrackSelections<? extends MappedTrackInfo> trackSelections) { public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
updateButtonVisibilities(); updateButtonVisibilities();
MappedTrackInfo trackInfo = trackSelections.info; MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo();
if (trackInfo.hasOnlyUnplayableTracks(C.TRACK_TYPE_VIDEO)) { if (mappedTrackInfo != null) {
showToast(R.string.error_unsupported_video); if (mappedTrackInfo.hasOnlyUnplayableTracks(C.TRACK_TYPE_VIDEO)) {
} showToast(R.string.error_unsupported_video);
if (trackInfo.hasOnlyUnplayableTracks(C.TRACK_TYPE_AUDIO)) { }
showToast(R.string.error_unsupported_audio); if (mappedTrackInfo.hasOnlyUnplayableTracks(C.TRACK_TYPE_AUDIO)) {
showToast(R.string.error_unsupported_audio);
}
} }
} }
@ -473,14 +475,13 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
return; return;
} }
TrackSelections<MappedTrackInfo> trackSelections = trackSelector.getCurrentSelections(); MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo();
if (trackSelections == null) { if (mappedTrackInfo == null) {
return; return;
} }
int rendererCount = trackSelections.length; for (int i = 0; i < mappedTrackInfo.length; i++) {
for (int i = 0; i < rendererCount; i++) { TrackGroupArray trackGroups = mappedTrackInfo.getTrackGroups(i);
TrackGroupArray trackGroups = trackSelections.info.getTrackGroups(i);
if (trackGroups.length != 0) { if (trackGroups.length != 0) {
Button button = new Button(this); Button button = new Button(this);
int label; int label;

View File

@ -17,7 +17,6 @@ package com.google.android.exoplayer2.ext.flac;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.test.InstrumentationTestCase; import android.test.InstrumentationTestCase;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
@ -27,7 +26,9 @@ import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor; import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor;
import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
/** /**
@ -72,7 +73,7 @@ public class FlacPlaybackTest extends InstrumentationTestCase {
public void run() { public void run() {
Looper.prepare(); Looper.prepare();
LibflacAudioRenderer audioRenderer = new LibflacAudioRenderer(); LibflacAudioRenderer audioRenderer = new LibflacAudioRenderer();
DefaultTrackSelector trackSelector = new DefaultTrackSelector(new Handler()); DefaultTrackSelector trackSelector = new DefaultTrackSelector();
player = ExoPlayerFactory.newInstance(new Renderer[] {audioRenderer}, trackSelector); player = ExoPlayerFactory.newInstance(new Renderer[] {audioRenderer}, trackSelector);
player.addListener(this); player.addListener(this);
ExtractorMediaSource mediaSource = new ExtractorMediaSource( ExtractorMediaSource mediaSource = new ExtractorMediaSource(
@ -91,6 +92,11 @@ public class FlacPlaybackTest extends InstrumentationTestCase {
// Do nothing. // Do nothing.
} }
@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
// Do nothing.
}
@Override @Override
public void onPositionDiscontinuity() { public void onPositionDiscontinuity() {
// Do nothing. // Do nothing.

View File

@ -17,7 +17,6 @@ package com.google.android.exoplayer2.ext.opus;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.test.InstrumentationTestCase; import android.test.InstrumentationTestCase;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
@ -27,7 +26,9 @@ import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor; import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor;
import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
/** /**
@ -72,7 +73,7 @@ public class OpusPlaybackTest extends InstrumentationTestCase {
public void run() { public void run() {
Looper.prepare(); Looper.prepare();
LibopusAudioRenderer audioRenderer = new LibopusAudioRenderer(); LibopusAudioRenderer audioRenderer = new LibopusAudioRenderer();
DefaultTrackSelector trackSelector = new DefaultTrackSelector(new Handler()); DefaultTrackSelector trackSelector = new DefaultTrackSelector();
player = ExoPlayerFactory.newInstance(new Renderer[] {audioRenderer}, trackSelector); player = ExoPlayerFactory.newInstance(new Renderer[] {audioRenderer}, trackSelector);
player.addListener(this); player.addListener(this);
ExtractorMediaSource mediaSource = new ExtractorMediaSource( ExtractorMediaSource mediaSource = new ExtractorMediaSource(
@ -91,6 +92,11 @@ public class OpusPlaybackTest extends InstrumentationTestCase {
// Do nothing. // Do nothing.
} }
@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
// Do nothing.
}
@Override @Override
public void onPositionDiscontinuity() { public void onPositionDiscontinuity() {
// Do nothing. // Do nothing.

View File

@ -17,7 +17,6 @@ package com.google.android.exoplayer2.ext.vp9;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.test.InstrumentationTestCase; import android.test.InstrumentationTestCase;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
@ -27,7 +26,9 @@ import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor; import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor;
import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
/** /**
@ -88,7 +89,7 @@ public class VpxPlaybackTest extends InstrumentationTestCase {
public void run() { public void run() {
Looper.prepare(); Looper.prepare();
LibvpxVideoRenderer videoRenderer = new LibvpxVideoRenderer(true, 0); LibvpxVideoRenderer videoRenderer = new LibvpxVideoRenderer(true, 0);
DefaultTrackSelector trackSelector = new DefaultTrackSelector(new Handler()); DefaultTrackSelector trackSelector = new DefaultTrackSelector();
player = ExoPlayerFactory.newInstance(new Renderer[] {videoRenderer}, trackSelector); player = ExoPlayerFactory.newInstance(new Renderer[] {videoRenderer}, trackSelector);
player.addListener(this); player.addListener(this);
ExtractorMediaSource mediaSource = new ExtractorMediaSource( ExtractorMediaSource mediaSource = new ExtractorMediaSource(
@ -110,6 +111,11 @@ public class VpxPlaybackTest extends InstrumentationTestCase {
// Do nothing. // Do nothing.
} }
@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
// Do nothing.
}
@Override @Override
public void onPositionDiscontinuity() { public void onPositionDiscontinuity() {
// Do nothing. // Do nothing.

Binary file not shown.

View File

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.extractor.ts; package com.google.android.exoplayer2.extractor.ts;
import android.test.InstrumentationTestCase; import android.test.InstrumentationTestCase;
import android.util.SparseArray;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
@ -73,7 +74,7 @@ public final class TsExtractorTest extends InstrumentationTestCase {
} }
public void testCustomPesReader() throws Exception { public void testCustomPesReader() throws Exception {
CustomEsReaderFactory factory = new CustomEsReaderFactory(); CustomTsPayloadReaderFactory factory = new CustomTsPayloadReaderFactory(true, false);
TsExtractor tsExtractor = new TsExtractor(new TimestampAdjuster(0), factory, false); TsExtractor tsExtractor = new TsExtractor(new TimestampAdjuster(0), factory, false);
FakeExtractorInput input = new FakeExtractorInput.Builder() FakeExtractorInput input = new FakeExtractorInput.Builder()
.setData(TestUtil.getByteArray(getInstrumentation(), "ts/sample.ts")) .setData(TestUtil.getByteArray(getInstrumentation(), "ts/sample.ts"))
@ -82,13 +83,12 @@ public final class TsExtractorTest extends InstrumentationTestCase {
.setSimulatePartialReads(false).build(); .setSimulatePartialReads(false).build();
FakeExtractorOutput output = new FakeExtractorOutput(); FakeExtractorOutput output = new FakeExtractorOutput();
tsExtractor.init(output); tsExtractor.init(output);
tsExtractor.seek(input.getPosition());
PositionHolder seekPositionHolder = new PositionHolder(); PositionHolder seekPositionHolder = new PositionHolder();
int readResult = Extractor.RESULT_CONTINUE; int readResult = Extractor.RESULT_CONTINUE;
while (readResult != Extractor.RESULT_END_OF_INPUT) { while (readResult != Extractor.RESULT_END_OF_INPUT) {
readResult = tsExtractor.read(input, seekPositionHolder); readResult = tsExtractor.read(input, seekPositionHolder);
} }
CustomEsReader reader = factory.reader; CustomEsReader reader = factory.esReader;
assertEquals(2, reader.packetsRead); assertEquals(2, reader.packetsRead);
TrackOutput trackOutput = reader.getTrackOutput(); TrackOutput trackOutput = reader.getTrackOutput();
assertTrue(trackOutput == output.trackOutputs.get(257 /* PID of audio track. */)); assertTrue(trackOutput == output.trackOutputs.get(257 /* PID of audio track. */));
@ -97,6 +97,23 @@ public final class TsExtractorTest extends InstrumentationTestCase {
((FakeTrackOutput) trackOutput).format); ((FakeTrackOutput) trackOutput).format);
} }
public void testCustomInitialSectionReader() throws Exception {
CustomTsPayloadReaderFactory factory = new CustomTsPayloadReaderFactory(false, true);
TsExtractor tsExtractor = new TsExtractor(new TimestampAdjuster(0), factory, false);
FakeExtractorInput input = new FakeExtractorInput.Builder()
.setData(TestUtil.getByteArray(getInstrumentation(), "ts/sample_with_sdt.ts"))
.setSimulateIOErrors(false)
.setSimulateUnknownLength(false)
.setSimulatePartialReads(false).build();
tsExtractor.init(new FakeExtractorOutput());
PositionHolder seekPositionHolder = new PositionHolder();
int readResult = Extractor.RESULT_CONTINUE;
while (readResult != Extractor.RESULT_END_OF_INPUT) {
readResult = tsExtractor.read(input, seekPositionHolder);
}
assertEquals(1, factory.sdtReader.consumedSdts);
}
private static void writeJunkData(ByteArrayOutputStream out, int length) throws IOException { private static void writeJunkData(ByteArrayOutputStream out, int length) throws IOException {
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
if (((byte) i) == TS_SYNC_BYTE) { if (((byte) i) == TS_SYNC_BYTE) {
@ -107,6 +124,45 @@ public final class TsExtractorTest extends InstrumentationTestCase {
} }
} }
private static final class CustomTsPayloadReaderFactory implements TsPayloadReader.Factory {
private final boolean provideSdtReader;
private final boolean provideCustomEsReader;
private final TsPayloadReader.Factory defaultFactory;
private CustomEsReader esReader;
private SdtSectionReader sdtReader;
public CustomTsPayloadReaderFactory(boolean provideCustomEsReader, boolean provideSdtReader) {
this.provideCustomEsReader = provideCustomEsReader;
this.provideSdtReader = provideSdtReader;
defaultFactory = new DefaultTsPayloadReaderFactory();
}
@Override
public SparseArray<TsPayloadReader> createInitialPayloadReaders() {
if (provideSdtReader) {
assertNull(sdtReader);
SparseArray<TsPayloadReader> mapping = new SparseArray<>();
sdtReader = new SdtSectionReader();
mapping.put(17, new SectionReader(sdtReader));
return mapping;
} else {
return defaultFactory.createInitialPayloadReaders();
}
}
@Override
public TsPayloadReader createPayloadReader(int streamType, EsInfo esInfo) {
if (provideCustomEsReader && streamType == 3) {
esReader = new CustomEsReader(esInfo.language);
return new PesReader(esReader);
} else {
return defaultFactory.createPayloadReader(streamType, esInfo);
}
}
}
private static final class CustomEsReader implements ElementaryStreamReader { private static final class CustomEsReader implements ElementaryStreamReader {
private final String language; private final String language;
@ -147,23 +203,44 @@ public final class TsExtractorTest extends InstrumentationTestCase {
} }
private static final class CustomEsReaderFactory implements TsPayloadReader.Factory { private static final class SdtSectionReader implements SectionPayloadReader {
private final TsPayloadReader.Factory defaultFactory; private int consumedSdts;
private CustomEsReader reader;
public CustomEsReaderFactory() { @Override
defaultFactory = new DefaultTsPayloadReaderFactory(); public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput,
TrackIdGenerator idGenerator) {
// Do nothing.
} }
@Override @Override
public TsPayloadReader createPayloadReader(int streamType, EsInfo esInfo) { public void consume(ParsableByteArray sectionData) {
if (streamType == 3) { // table_id(8), section_syntax_indicator(1), reserved_future_use(1), reserved(2),
reader = new CustomEsReader(esInfo.language); // section_length(12), transport_stream_id(16), reserved(2), version_number(5),
return new PesReader(reader); // current_next_indicator(1), section_number(8), last_section_number(8),
} else { // original_network_id(16), reserved_future_use(8)
return defaultFactory.createPayloadReader(streamType, esInfo); sectionData.skipBytes(11);
// Start of the service loop.
assertEquals(0x5566 /* arbitrary service id */, sectionData.readUnsignedShort());
// reserved_future_use(6), EIT_schedule_flag(1), EIT_present_following_flag(1)
sectionData.skipBytes(1);
// Assert there is only one service.
// Remove running_status(3), free_CA_mode(1) from the descriptors_loop_length with the mask.
assertEquals(sectionData.readUnsignedShort() & 0xFFF, sectionData.bytesLeft());
while (sectionData.bytesLeft() > 0) {
int descriptorTag = sectionData.readUnsignedByte();
int descriptorLength = sectionData.readUnsignedByte();
if (descriptorTag == 72 /* service descriptor */) {
assertEquals(1, sectionData.readUnsignedByte()); // Service type: Digital TV.
int serviceProviderNameLength = sectionData.readUnsignedByte();
assertEquals("Some provider", sectionData.readString(serviceProviderNameLength));
int serviceNameLength = sectionData.readUnsignedByte();
assertEquals("Some Channel", sectionData.readString(serviceNameLength));
} else {
sectionData.skipBytes(descriptorLength);
}
} }
consumedSdts++;
} }
} }

View File

@ -16,7 +16,7 @@
package com.google.android.exoplayer2; package com.google.android.exoplayer2;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelections; import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DefaultAllocator; import com.google.android.exoplayer2.upstream.DefaultAllocator;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
@ -111,7 +111,7 @@ public final class DefaultLoadControl implements LoadControl {
@Override @Override
public void onTracksSelected(Renderer[] renderers, TrackGroupArray trackGroups, public void onTracksSelected(Renderer[] renderers, TrackGroupArray trackGroups,
TrackSelections<?> trackSelections) { TrackSelectionArray trackSelections) {
targetBufferSize = 0; targetBufferSize = 0;
for (int i = 0; i < renderers.length; i++) { for (int i = 0; i < renderers.length; i++) {
if (trackSelections.get(i) != null) { if (trackSelections.get(i) != null) {

View File

@ -22,11 +22,13 @@ import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MergingMediaSource; import com.google.android.exoplayer2.source.MergingMediaSource;
import com.google.android.exoplayer2.source.SingleSampleMediaSource; import com.google.android.exoplayer2.source.SingleSampleMediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.dash.DashMediaSource; import com.google.android.exoplayer2.source.dash.DashMediaSource;
import com.google.android.exoplayer2.source.hls.HlsMediaSource; import com.google.android.exoplayer2.source.hls.HlsMediaSource;
import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource;
import com.google.android.exoplayer2.text.TextRenderer; import com.google.android.exoplayer2.text.TextRenderer;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.video.MediaCodecVideoRenderer; import com.google.android.exoplayer2.video.MediaCodecVideoRenderer;
@ -110,6 +112,15 @@ public interface ExoPlayer {
*/ */
interface EventListener { interface EventListener {
/**
* Called when the available or selected tracks change.
*
* @param trackGroups The available tracks. Never null, but may be of length zero.
* @param trackSelections The track selections for each {@link Renderer}. Never null and always
* of length {@link #getRendererCount()}, but may contain null elements.
*/
void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections);
/** /**
* Called when the player starts or stops loading the source. * Called when the player starts or stops loading the source.
* *
@ -259,11 +270,11 @@ public interface ExoPlayer {
* @param resetPosition Whether the playback position should be reset to the default position in * @param resetPosition Whether the playback position should be reset to the default position in
* the first {@link Timeline.Window}. If false, playback will start from the position defined * the first {@link Timeline.Window}. If false, playback will start from the position defined
* by {@link #getCurrentWindowIndex()} and {@link #getCurrentPosition()}. * by {@link #getCurrentWindowIndex()} and {@link #getCurrentPosition()}.
* @param resetTimeline Whether the timeline and manifest should be reset. Should be true unless * @param resetState Whether the timeline, manifest, tracks and track selections should be reset.
* the player is being prepared to play the same media as it was playing previously (e.g. if * Should be true unless the player is being prepared to play the same media as it was playing
* playback failed and is being retried). * previously (e.g. if playback failed and is being retried).
*/ */
void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetTimeline); void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState);
/** /**
* Sets whether playback should proceed when {@link #getPlaybackState()} == {@link #STATE_READY}. * Sets whether playback should proceed when {@link #getPlaybackState()} == {@link #STATE_READY}.
@ -356,6 +367,30 @@ public interface ExoPlayer {
*/ */
void blockingSendMessages(ExoPlayerMessage... messages); void blockingSendMessages(ExoPlayerMessage... messages);
/**
* Returns the number of renderers.
*/
int getRendererCount();
/**
* Returns the track type that the renderer at a given index handles.
*
* @see Renderer#getTrackType()
* @param index The index of the renderer.
* @return One of the {@code TRACK_TYPE_*} constants defined in {@link C}.
*/
int getRendererType(int index);
/**
* Returns the available track groups.
*/
TrackGroupArray getCurrentTrackGroups();
/**
* Returns the current track selections for each renderer.
*/
TrackSelectionArray getCurrentTrackSelections();
/** /**
* Returns the current manifest. The type depends on the {@link MediaSource} passed to * Returns the current manifest. The type depends on the {@link MediaSource} passed to
* {@link #prepare}. * {@link #prepare}.

View File

@ -42,7 +42,7 @@ public final class ExoPlayerFactory {
* @param trackSelector The {@link TrackSelector} that will be used by the instance. * @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param loadControl The {@link LoadControl} that will be used by the instance. * @param loadControl The {@link LoadControl} that will be used by the instance.
*/ */
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector<?> trackSelector, public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
LoadControl loadControl) { LoadControl loadControl) {
return newSimpleInstance(context, trackSelector, loadControl, null); return newSimpleInstance(context, trackSelector, loadControl, null);
} }
@ -57,7 +57,7 @@ public final class ExoPlayerFactory {
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the instance * @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the instance
* will not be used for DRM protected playbacks. * will not be used for DRM protected playbacks.
*/ */
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector<?> trackSelector, public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
LoadControl loadControl, DrmSessionManager<FrameworkMediaCrypto> drmSessionManager) { LoadControl loadControl, DrmSessionManager<FrameworkMediaCrypto> drmSessionManager) {
return newSimpleInstance(context, trackSelector, loadControl, drmSessionManager, false); return newSimpleInstance(context, trackSelector, loadControl, drmSessionManager, false);
} }
@ -75,7 +75,7 @@ public final class ExoPlayerFactory {
* available extensions over those defined in the core library. Note that extensions must be * available extensions over those defined in the core library. Note that extensions must be
* included in the application build for setting this flag to have any effect. * included in the application build for setting this flag to have any effect.
*/ */
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector<?> trackSelector, public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
LoadControl loadControl, DrmSessionManager<FrameworkMediaCrypto> drmSessionManager, LoadControl loadControl, DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
boolean preferExtensionDecoders) { boolean preferExtensionDecoders) {
return newSimpleInstance(context, trackSelector, loadControl, drmSessionManager, return newSimpleInstance(context, trackSelector, loadControl, drmSessionManager,
@ -97,7 +97,7 @@ public final class ExoPlayerFactory {
* @param allowedVideoJoiningTimeMs The maximum duration for which a video renderer can attempt to * @param allowedVideoJoiningTimeMs The maximum duration for which a video renderer can attempt to
* seamlessly join an ongoing playback. * seamlessly join an ongoing playback.
*/ */
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector<?> trackSelector, public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
LoadControl loadControl, DrmSessionManager<FrameworkMediaCrypto> drmSessionManager, LoadControl loadControl, DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
boolean preferExtensionDecoders, long allowedVideoJoiningTimeMs) { boolean preferExtensionDecoders, long allowedVideoJoiningTimeMs) {
return new SimpleExoPlayer(context, trackSelector, loadControl, drmSessionManager, return new SimpleExoPlayer(context, trackSelector, loadControl, drmSessionManager,
@ -111,7 +111,7 @@ public final class ExoPlayerFactory {
* @param renderers The {@link Renderer}s that will be used by the instance. * @param renderers The {@link Renderer}s that will be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance. * @param trackSelector The {@link TrackSelector} that will be used by the instance.
*/ */
public static ExoPlayer newInstance(Renderer[] renderers, TrackSelector<?> trackSelector) { public static ExoPlayer newInstance(Renderer[] renderers, TrackSelector trackSelector) {
return newInstance(renderers, trackSelector, new DefaultLoadControl()); return newInstance(renderers, trackSelector, new DefaultLoadControl());
} }
@ -123,7 +123,7 @@ public final class ExoPlayerFactory {
* @param trackSelector The {@link TrackSelector} that will be used by the instance. * @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param loadControl The {@link LoadControl} that will be used by the instance. * @param loadControl The {@link LoadControl} that will be used by the instance.
*/ */
public static ExoPlayer newInstance(Renderer[] renderers, TrackSelector<?> trackSelector, public static ExoPlayer newInstance(Renderer[] renderers, TrackSelector trackSelector,
LoadControl loadControl) { LoadControl loadControl) {
return new ExoPlayerImpl(renderers, trackSelector, loadControl); return new ExoPlayerImpl(renderers, trackSelector, loadControl);
} }

View File

@ -22,7 +22,11 @@ import android.os.Message;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
import com.google.android.exoplayer2.ExoPlayerImplInternal.PlaybackInfo; import com.google.android.exoplayer2.ExoPlayerImplInternal.PlaybackInfo;
import com.google.android.exoplayer2.ExoPlayerImplInternal.TrackInfo;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
@ -34,12 +38,16 @@ import java.util.concurrent.CopyOnWriteArraySet;
private static final String TAG = "ExoPlayerImpl"; private static final String TAG = "ExoPlayerImpl";
private final Renderer[] renderers;
private final TrackSelector trackSelector;
private final TrackSelectionArray emptyTrackSelections;
private final Handler eventHandler; private final Handler eventHandler;
private final ExoPlayerImplInternal<?> internalPlayer; private final ExoPlayerImplInternal internalPlayer;
private final CopyOnWriteArraySet<EventListener> listeners; private final CopyOnWriteArraySet<EventListener> listeners;
private final Timeline.Window window; private final Timeline.Window window;
private final Timeline.Period period; private final Timeline.Period period;
private boolean tracksSelected;
private boolean pendingInitialSeek; private boolean pendingInitialSeek;
private boolean playWhenReady; private boolean playWhenReady;
private int playbackState; private int playbackState;
@ -47,6 +55,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
private boolean isLoading; private boolean isLoading;
private Timeline timeline; private Timeline timeline;
private Object manifest; private Object manifest;
private TrackGroupArray trackGroups;
private TrackSelectionArray trackSelections;
// Playback information when there is no pending seek/set source operation. // Playback information when there is no pending seek/set source operation.
private PlaybackInfo playbackInfo; private PlaybackInfo playbackInfo;
@ -63,16 +73,19 @@ import java.util.concurrent.CopyOnWriteArraySet;
* @param loadControl The {@link LoadControl} that will be used by the instance. * @param loadControl The {@link LoadControl} that will be used by the instance.
*/ */
@SuppressLint("HandlerLeak") @SuppressLint("HandlerLeak")
public ExoPlayerImpl(Renderer[] renderers, TrackSelector<?> trackSelector, public ExoPlayerImpl(Renderer[] renderers, TrackSelector trackSelector, LoadControl loadControl) {
LoadControl loadControl) {
Log.i(TAG, "Init " + ExoPlayerLibraryInfo.VERSION); Log.i(TAG, "Init " + ExoPlayerLibraryInfo.VERSION);
Assertions.checkNotNull(renderers);
Assertions.checkState(renderers.length > 0); Assertions.checkState(renderers.length > 0);
this.renderers = Assertions.checkNotNull(renderers);
this.trackSelector = Assertions.checkNotNull(trackSelector);
this.playWhenReady = false; this.playWhenReady = false;
this.playbackState = STATE_IDLE; this.playbackState = STATE_IDLE;
this.listeners = new CopyOnWriteArraySet<>(); this.listeners = new CopyOnWriteArraySet<>();
emptyTrackSelections = new TrackSelectionArray(new TrackSelection[renderers.length]);
window = new Timeline.Window(); window = new Timeline.Window();
period = new Timeline.Period(); period = new Timeline.Period();
trackGroups = TrackGroupArray.EMPTY;
trackSelections = emptyTrackSelections;
eventHandler = new Handler() { eventHandler = new Handler() {
@Override @Override
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
@ -80,8 +93,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
} }
}; };
playbackInfo = new ExoPlayerImplInternal.PlaybackInfo(0, 0); playbackInfo = new ExoPlayerImplInternal.PlaybackInfo(0, 0);
internalPlayer = new ExoPlayerImplInternal<>(renderers, trackSelector, loadControl, internalPlayer = new ExoPlayerImplInternal(renderers, trackSelector, loadControl, playWhenReady,
playWhenReady, eventHandler, playbackInfo); eventHandler, playbackInfo);
} }
@Override @Override
@ -105,12 +118,23 @@ import java.util.concurrent.CopyOnWriteArraySet;
} }
@Override @Override
public void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetTimeline) { public void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState) {
if (resetTimeline && (timeline != null || manifest != null)) { if (resetState) {
timeline = null; if (timeline != null || manifest != null) {
manifest = null; timeline = null;
for (EventListener listener : listeners) { manifest = null;
listener.onTimelineChanged(null, null); for (EventListener listener : listeners) {
listener.onTimelineChanged(null, null);
}
}
if (tracksSelected) {
tracksSelected = false;
trackGroups = TrackGroupArray.EMPTY;
trackSelections = emptyTrackSelections;
trackSelector.onSelectionActivated(null);
for (EventListener listener : listeners) {
listener.onTracksChanged(trackGroups, trackSelections);
}
} }
} }
internalPlayer.prepare(mediaSource, resetPosition); internalPlayer.prepare(mediaSource, resetPosition);
@ -266,6 +290,26 @@ import java.util.concurrent.CopyOnWriteArraySet;
: (int) (duration == 0 ? 100 : (bufferedPosition * 100) / duration); : (int) (duration == 0 ? 100 : (bufferedPosition * 100) / duration);
} }
@Override
public int getRendererCount() {
return renderers.length;
}
@Override
public int getRendererType(int index) {
return renderers[index].getTrackType();
}
@Override
public TrackGroupArray getCurrentTrackGroups() {
return trackGroups;
}
@Override
public TrackSelectionArray getCurrentTrackSelections() {
return trackSelections;
}
@Override @Override
public Timeline getCurrentTimeline() { public Timeline getCurrentTimeline() {
return timeline; return timeline;
@ -293,6 +337,17 @@ import java.util.concurrent.CopyOnWriteArraySet;
} }
break; break;
} }
case ExoPlayerImplInternal.MSG_TRACKS_CHANGED: {
TrackInfo trackInfo = (TrackInfo) msg.obj;
tracksSelected = true;
trackGroups = trackInfo.groups;
trackSelections = trackInfo.selections;
trackSelector.onSelectionActivated(trackInfo.info);
for (EventListener listener : listeners) {
listener.onTracksChanged(trackGroups, trackSelections);
}
break;
}
case ExoPlayerImplInternal.MSG_SEEK_ACK: { case ExoPlayerImplInternal.MSG_SEEK_ACK: {
if (--pendingSeekAcks == 0) { if (--pendingSeekAcks == 0) {
playbackInfo = (ExoPlayerImplInternal.PlaybackInfo) msg.obj; playbackInfo = (ExoPlayerImplInternal.PlaybackInfo) msg.obj;

View File

@ -26,8 +26,9 @@ import com.google.android.exoplayer2.ExoPlayer.ExoPlayerMessage;
import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.SampleStream; import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelections; import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MediaClock; import com.google.android.exoplayer2.util.MediaClock;
@ -40,7 +41,7 @@ import java.io.IOException;
/** /**
* Implements the internal behavior of {@link ExoPlayerImpl}. * Implements the internal behavior of {@link ExoPlayerImpl}.
*/ */
/* package */ final class ExoPlayerImplInternal<T> implements Handler.Callback, /* package */ final class ExoPlayerImplInternal implements Handler.Callback,
MediaPeriod.Callback, TrackSelector.InvalidationListener, MediaSource.Listener { MediaPeriod.Callback, TrackSelector.InvalidationListener, MediaSource.Listener {
/** /**
@ -64,15 +65,30 @@ import java.io.IOException;
} }
public static final class TrackInfo {
public final TrackGroupArray groups;
public final TrackSelectionArray selections;
public final Object info;
public TrackInfo(TrackGroupArray groups, TrackSelectionArray selections, Object info) {
this.groups = groups;
this.selections = selections;
this.info = info;
}
}
private static final String TAG = "ExoPlayerImplInternal"; private static final String TAG = "ExoPlayerImplInternal";
// External messages // External messages
public static final int MSG_STATE_CHANGED = 1; public static final int MSG_STATE_CHANGED = 1;
public static final int MSG_LOADING_CHANGED = 2; public static final int MSG_LOADING_CHANGED = 2;
public static final int MSG_SEEK_ACK = 3; public static final int MSG_TRACKS_CHANGED = 3;
public static final int MSG_POSITION_DISCONTINUITY = 4; public static final int MSG_SEEK_ACK = 4;
public static final int MSG_SOURCE_INFO_REFRESHED = 5; public static final int MSG_POSITION_DISCONTINUITY = 5;
public static final int MSG_ERROR = 6; public static final int MSG_SOURCE_INFO_REFRESHED = 6;
public static final int MSG_ERROR = 7;
// Internal messages // Internal messages
private static final int MSG_PREPARE = 0; private static final int MSG_PREPARE = 0;
@ -100,7 +116,7 @@ import java.io.IOException;
private final Renderer[] renderers; private final Renderer[] renderers;
private final RendererCapabilities[] rendererCapabilities; private final RendererCapabilities[] rendererCapabilities;
private final TrackSelector<T> trackSelector; private final TrackSelector trackSelector;
private final LoadControl loadControl; private final LoadControl loadControl;
private final StandaloneMediaClock standaloneMediaClock; private final StandaloneMediaClock standaloneMediaClock;
private final Handler handler; private final Handler handler;
@ -128,13 +144,13 @@ import java.io.IOException;
private boolean isTimelineReady; private boolean isTimelineReady;
private boolean isTimelineEnded; private boolean isTimelineEnded;
private int bufferAheadPeriodCount; private int bufferAheadPeriodCount;
private MediaPeriodHolder<T> playingPeriodHolder; private MediaPeriodHolder playingPeriodHolder;
private MediaPeriodHolder<T> readingPeriodHolder; private MediaPeriodHolder readingPeriodHolder;
private MediaPeriodHolder<T> loadingPeriodHolder; private MediaPeriodHolder loadingPeriodHolder;
private Timeline timeline; private Timeline timeline;
public ExoPlayerImplInternal(Renderer[] renderers, TrackSelector<T> trackSelector, public ExoPlayerImplInternal(Renderer[] renderers, TrackSelector trackSelector,
LoadControl loadControl, boolean playWhenReady, Handler eventHandler, LoadControl loadControl, boolean playWhenReady, Handler eventHandler,
PlaybackInfo playbackInfo) { PlaybackInfo playbackInfo) {
this.renderers = renderers; this.renderers = renderers;
@ -538,7 +554,7 @@ import java.io.IOException;
periodIndex = C.INDEX_UNSET; periodIndex = C.INDEX_UNSET;
} }
MediaPeriodHolder<T> newPlayingPeriodHolder = null; MediaPeriodHolder newPlayingPeriodHolder = null;
if (playingPeriodHolder == null) { if (playingPeriodHolder == null) {
// We're still waiting for the first period to be prepared. // We're still waiting for the first period to be prepared.
if (loadingPeriodHolder != null) { if (loadingPeriodHolder != null) {
@ -546,7 +562,7 @@ import java.io.IOException;
} }
} else { } else {
// Clear the timeline, but keep the requested period if it is already prepared. // Clear the timeline, but keep the requested period if it is already prepared.
MediaPeriodHolder<T> periodHolder = playingPeriodHolder; MediaPeriodHolder periodHolder = playingPeriodHolder;
while (periodHolder != null) { while (periodHolder != null) {
if (periodHolder.index == periodIndex && periodHolder.prepared) { if (periodHolder.index == periodIndex && periodHolder.prepared) {
newPlayingPeriodHolder = periodHolder; newPlayingPeriodHolder = periodHolder;
@ -680,7 +696,7 @@ import java.io.IOException;
return; return;
} }
// Reselect tracks on each period in turn, until the selection changes. // Reselect tracks on each period in turn, until the selection changes.
MediaPeriodHolder<T> periodHolder = playingPeriodHolder; MediaPeriodHolder periodHolder = playingPeriodHolder;
boolean selectionsChangedForReadPeriod = true; boolean selectionsChangedForReadPeriod = true;
while (true) { while (true) {
if (periodHolder == null || !periodHolder.prepared) { if (periodHolder == null || !periodHolder.prepared) {
@ -745,7 +761,7 @@ import java.io.IOException;
} }
} }
} }
trackSelector.onSelectionActivated(playingPeriodHolder.trackSelections); eventHandler.obtainMessage(MSG_TRACKS_CHANGED, periodHolder.getTrackInfo()).sendToTarget();
enableRenderers(rendererWasEnabledFlags, enabledRendererCount); enableRenderers(rendererWasEnabledFlags, enabledRendererCount);
} else { } else {
// Release and re-prepare/buffer periods after the one whose selection changed. // Release and re-prepare/buffer periods after the one whose selection changed.
@ -817,11 +833,11 @@ import java.io.IOException;
playingPeriodHolder.setIndex(timeline, timeline.getWindow(period.windowIndex, window), playingPeriodHolder.setIndex(timeline, timeline.getWindow(period.windowIndex, window),
index); index);
MediaPeriodHolder<T> previousPeriodHolder = playingPeriodHolder; MediaPeriodHolder previousPeriodHolder = playingPeriodHolder;
boolean seenReadingPeriod = false; boolean seenReadingPeriod = false;
bufferAheadPeriodCount = 0; bufferAheadPeriodCount = 0;
while (previousPeriodHolder.next != null) { while (previousPeriodHolder.next != null) {
MediaPeriodHolder<T> periodHolder = previousPeriodHolder.next; MediaPeriodHolder periodHolder = previousPeriodHolder.next;
index++; index++;
timeline.getPeriod(index, period, true); timeline.getPeriod(index, period, true);
if (!periodHolder.uid.equals(period.uid)) { if (!periodHolder.uid.equals(period.uid)) {
@ -962,9 +978,8 @@ import java.io.IOException;
MediaPeriod newMediaPeriod = mediaSource.createPeriod(newLoadingPeriodIndex, MediaPeriod newMediaPeriod = mediaSource.createPeriod(newLoadingPeriodIndex,
loadControl.getAllocator(), periodStartPositionUs); loadControl.getAllocator(), periodStartPositionUs);
newMediaPeriod.prepare(this); newMediaPeriod.prepare(this);
MediaPeriodHolder<T> newPeriodHolder = new MediaPeriodHolder<>(renderers, MediaPeriodHolder newPeriodHolder = new MediaPeriodHolder(renderers, rendererCapabilities,
rendererCapabilities, trackSelector, mediaSource, newMediaPeriod, newPeriodUid, trackSelector, mediaSource, newMediaPeriod, newPeriodUid, periodStartPositionUs);
periodStartPositionUs);
timeline.getWindow(windowIndex, window); timeline.getWindow(windowIndex, window);
newPeriodHolder.setIndex(timeline, window, newLoadingPeriodIndex); newPeriodHolder.setIndex(timeline, window, newLoadingPeriodIndex);
if (loadingPeriodHolder != null) { if (loadingPeriodHolder != null) {
@ -1018,9 +1033,9 @@ import java.io.IOException;
} }
} }
if (readingPeriodHolder.next != null && readingPeriodHolder.next.prepared) { if (readingPeriodHolder.next != null && readingPeriodHolder.next.prepared) {
TrackSelections<T> oldTrackSelections = readingPeriodHolder.trackSelections; TrackSelectionArray oldTrackSelections = readingPeriodHolder.trackSelections;
readingPeriodHolder = readingPeriodHolder.next; readingPeriodHolder = readingPeriodHolder.next;
TrackSelections<T> newTrackSelections = readingPeriodHolder.trackSelections; TrackSelectionArray newTrackSelections = readingPeriodHolder.trackSelections;
for (int i = 0; i < renderers.length; i++) { for (int i = 0; i < renderers.length; i++) {
Renderer renderer = renderers[i]; Renderer renderer = renderers[i];
TrackSelection oldSelection = oldTrackSelections.get(i); TrackSelection oldSelection = oldTrackSelections.get(i);
@ -1094,14 +1109,14 @@ import java.io.IOException;
} }
} }
private void releasePeriodHoldersFrom(MediaPeriodHolder<T> periodHolder) { private void releasePeriodHoldersFrom(MediaPeriodHolder periodHolder) {
while (periodHolder != null) { while (periodHolder != null) {
periodHolder.release(); periodHolder.release();
periodHolder = periodHolder.next; periodHolder = periodHolder.next;
} }
} }
private void setPlayingPeriodHolder(MediaPeriodHolder<T> periodHolder) private void setPlayingPeriodHolder(MediaPeriodHolder periodHolder)
throws ExoPlaybackException { throws ExoPlaybackException {
int enabledRendererCount = 0; int enabledRendererCount = 0;
boolean[] rendererWasEnabledFlags = new boolean[renderers.length]; boolean[] rendererWasEnabledFlags = new boolean[renderers.length];
@ -1125,7 +1140,7 @@ import java.io.IOException;
} }
} }
trackSelector.onSelectionActivated(periodHolder.trackSelections); eventHandler.obtainMessage(MSG_TRACKS_CHANGED, periodHolder.getTrackInfo()).sendToTarget();
playingPeriodHolder = periodHolder; playingPeriodHolder = periodHolder;
enableRenderers(rendererWasEnabledFlags, enabledRendererCount); enableRenderers(rendererWasEnabledFlags, enabledRendererCount);
} }
@ -1182,7 +1197,7 @@ import java.io.IOException;
/** /**
* Holds a {@link MediaPeriod} with information required to play it as part of a timeline. * Holds a {@link MediaPeriod} with information required to play it as part of a timeline.
*/ */
private static final class MediaPeriodHolder<T> { private static final class MediaPeriodHolder {
public final MediaPeriod mediaPeriod; public final MediaPeriod mediaPeriod;
public final Object uid; public final Object uid;
@ -1196,19 +1211,21 @@ import java.io.IOException;
public boolean prepared; public boolean prepared;
public boolean hasEnabledTracks; public boolean hasEnabledTracks;
public long rendererPositionOffsetUs; public long rendererPositionOffsetUs;
public MediaPeriodHolder<T> next; public MediaPeriodHolder next;
public boolean needsContinueLoading; public boolean needsContinueLoading;
private final Renderer[] renderers; private final Renderer[] renderers;
private final RendererCapabilities[] rendererCapabilities; private final RendererCapabilities[] rendererCapabilities;
private final TrackSelector<T> trackSelector; private final TrackSelector trackSelector;
private final MediaSource mediaSource; private final MediaSource mediaSource;
private TrackSelections<T> trackSelections; private Object trackSelectionsInfo;
private TrackSelections<T> periodTrackSelections; private TrackGroupArray trackGroups;
private TrackSelectionArray trackSelections;
private TrackSelectionArray periodTrackSelections;
public MediaPeriodHolder(Renderer[] renderers, RendererCapabilities[] rendererCapabilities, public MediaPeriodHolder(Renderer[] renderers, RendererCapabilities[] rendererCapabilities,
TrackSelector<T> trackSelector, MediaSource mediaSource, MediaPeriod mediaPeriod, TrackSelector trackSelector, MediaSource mediaSource, MediaPeriod mediaPeriod,
Object uid, long positionUs) { Object uid, long positionUs) {
this.renderers = renderers; this.renderers = renderers;
this.rendererCapabilities = rendererCapabilities; this.rendererCapabilities = rendererCapabilities;
@ -1221,7 +1238,7 @@ import java.io.IOException;
startPositionUs = positionUs; startPositionUs = positionUs;
} }
public void setNext(MediaPeriodHolder<T> next) { public void setNext(MediaPeriodHolder next) {
this.next = next; this.next = next;
} }
@ -1238,17 +1255,20 @@ import java.io.IOException;
public void handlePrepared(long positionUs, LoadControl loadControl) public void handlePrepared(long positionUs, LoadControl loadControl)
throws ExoPlaybackException { throws ExoPlaybackException {
prepared = true; prepared = true;
trackGroups = mediaPeriod.getTrackGroups();
selectTracks(); selectTracks();
startPositionUs = updatePeriodTrackSelection(positionUs, loadControl, false); startPositionUs = updatePeriodTrackSelection(positionUs, loadControl, false);
} }
public boolean selectTracks() throws ExoPlaybackException { public boolean selectTracks() throws ExoPlaybackException {
TrackSelections<T> newTrackSelections = trackSelector.selectTracks(rendererCapabilities, Pair<TrackSelectionArray, Object> selectorResult = trackSelector.selectTracks(
mediaPeriod.getTrackGroups()); rendererCapabilities, trackGroups);
TrackSelectionArray newTrackSelections = selectorResult.first;
if (newTrackSelections.equals(periodTrackSelections)) { if (newTrackSelections.equals(periodTrackSelections)) {
return false; return false;
} }
trackSelections = newTrackSelections; trackSelections = newTrackSelections;
trackSelectionsInfo = selectorResult.second;
return true; return true;
} }
@ -1283,10 +1303,14 @@ import java.io.IOException;
} }
// The track selection has changed. // The track selection has changed.
loadControl.onTracksSelected(renderers, mediaPeriod.getTrackGroups(), trackSelections); loadControl.onTracksSelected(renderers, trackGroups, trackSelections);
return positionUs; return positionUs;
} }
public TrackInfo getTrackInfo() {
return new TrackInfo(trackGroups, trackSelections, trackSelectionsInfo);
}
public void release() { public void release() {
try { try {
mediaSource.releasePeriod(mediaPeriod); mediaSource.releasePeriod(mediaPeriod);

View File

@ -17,7 +17,7 @@ package com.google.android.exoplayer2;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelections; import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Allocator;
/** /**
@ -38,7 +38,7 @@ public interface LoadControl {
* @param trackSelections The track selections that were made. * @param trackSelections The track selections that were made.
*/ */
void onTracksSelected(Renderer[] renderers, TrackGroupArray trackGroups, void onTracksSelected(Renderer[] renderers, TrackGroupArray trackGroups,
TrackSelections<?> trackSelections); TrackSelectionArray trackSelections);
/** /**
* Called by the player when stopped. * Called by the player when stopped.

View File

@ -39,9 +39,10 @@ import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.MetadataRenderer; import com.google.android.exoplayer2.metadata.MetadataRenderer;
import com.google.android.exoplayer2.metadata.id3.Id3Decoder; import com.google.android.exoplayer2.metadata.id3.Id3Decoder;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.TextRenderer; import com.google.android.exoplayer2.text.TextRenderer;
import com.google.android.exoplayer2.trackselection.TrackSelections; import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.video.MediaCodecVideoRenderer; import com.google.android.exoplayer2.video.MediaCodecVideoRenderer;
import com.google.android.exoplayer2.video.VideoRendererEventListener; import com.google.android.exoplayer2.video.VideoRendererEventListener;
@ -86,11 +87,6 @@ public final class SimpleExoPlayer implements ExoPlayer {
*/ */
void onRenderedFirstFrame(); void onRenderedFirstFrame();
/**
* Called when a video track is no longer selected.
*/
void onVideoTracksDisabled();
} }
private static final String TAG = "SimpleExoPlayer"; private static final String TAG = "SimpleExoPlayer";
@ -103,7 +99,6 @@ public final class SimpleExoPlayer implements ExoPlayer {
private final int videoRendererCount; private final int videoRendererCount;
private final int audioRendererCount; private final int audioRendererCount;
private boolean videoTracksEnabled;
private Format videoFormat; private Format videoFormat;
private Format audioFormat; private Format audioFormat;
@ -122,12 +117,11 @@ public final class SimpleExoPlayer implements ExoPlayer {
private float volume; private float volume;
private PlaybackParamsHolder playbackParamsHolder; private PlaybackParamsHolder playbackParamsHolder;
/* package */ SimpleExoPlayer(Context context, TrackSelector<?> trackSelector, /* package */ SimpleExoPlayer(Context context, TrackSelector trackSelector,
LoadControl loadControl, DrmSessionManager<FrameworkMediaCrypto> drmSessionManager, LoadControl loadControl, DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
boolean preferExtensionDecoders, long allowedVideoJoiningTimeMs) { boolean preferExtensionDecoders, long allowedVideoJoiningTimeMs) {
mainHandler = new Handler(); mainHandler = new Handler();
componentListener = new ComponentListener(); componentListener = new ComponentListener();
trackSelector.addListener(componentListener);
// Build the renderers. // Build the renderers.
ArrayList<Renderer> renderersList = new ArrayList<>(); ArrayList<Renderer> renderersList = new ArrayList<>();
@ -164,26 +158,6 @@ public final class SimpleExoPlayer implements ExoPlayer {
player = new ExoPlayerImpl(renderers, trackSelector, loadControl); player = new ExoPlayerImpl(renderers, trackSelector, loadControl);
} }
/**
* Returns the number of renderers.
*
* @return The number of renderers.
*/
public int getRendererCount() {
return renderers.length;
}
/**
* Returns the track type that the renderer at a given index handles.
*
* @see Renderer#getTrackType()
* @param index The index of the renderer.
* @return One of the {@code TRACK_TYPE_*} constants defined in {@link C}.
*/
public int getRendererType(int index) {
return renderers[index].getTrackType();
}
/** /**
* Clears any {@link Surface}, {@link SurfaceHolder}, {@link SurfaceView} or {@link TextureView} * Clears any {@link Surface}, {@link SurfaceHolder}, {@link SurfaceView} or {@link TextureView}
* currently set on the player. * currently set on the player.
@ -526,6 +500,26 @@ public final class SimpleExoPlayer implements ExoPlayer {
return player.getBufferedPercentage(); return player.getBufferedPercentage();
} }
@Override
public int getRendererCount() {
return player.getRendererCount();
}
@Override
public int getRendererType(int index) {
return player.getRendererType(index);
}
@Override
public TrackGroupArray getCurrentTrackGroups() {
return player.getCurrentTrackGroups();
}
@Override
public TrackSelectionArray getCurrentTrackSelections() {
return player.getCurrentTrackSelections();
}
@Override @Override
public Timeline getCurrentTimeline() { public Timeline getCurrentTimeline() {
return player.getCurrentTimeline(); return player.getCurrentTimeline();
@ -660,8 +654,7 @@ public final class SimpleExoPlayer implements ExoPlayer {
private final class ComponentListener implements VideoRendererEventListener, private final class ComponentListener implements VideoRendererEventListener,
AudioRendererEventListener, TextRenderer.Output, MetadataRenderer.Output, AudioRendererEventListener, TextRenderer.Output, MetadataRenderer.Output,
SurfaceHolder.Callback, TextureView.SurfaceTextureListener, SurfaceHolder.Callback, TextureView.SurfaceTextureListener {
TrackSelector.EventListener<Object> {
// VideoRendererEventListener implementation // VideoRendererEventListener implementation
@ -840,23 +833,6 @@ public final class SimpleExoPlayer implements ExoPlayer {
// Do nothing. // Do nothing.
} }
// TrackSelector.EventListener implementation
@Override
public void onTrackSelectionsChanged(TrackSelections<?> trackSelections) {
boolean videoTracksEnabled = false;
for (int i = 0; i < renderers.length; i++) {
if (renderers[i].getTrackType() == C.TRACK_TYPE_VIDEO && trackSelections.get(i) != null) {
videoTracksEnabled = true;
break;
}
}
if (videoListener != null && SimpleExoPlayer.this.videoTracksEnabled && !videoTracksEnabled) {
videoListener.onVideoTracksDisabled();
}
SimpleExoPlayer.this.videoTracksEnabled = videoTracksEnabled;
}
} }
@TargetApi(23) @TargetApi(23)

View File

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.extractor.ts; package com.google.android.exoplayer2.extractor.ts;
import android.support.annotation.IntDef; import android.support.annotation.IntDef;
import android.util.SparseArray;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.EsInfo; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.EsInfo;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
@ -49,6 +50,11 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
this.flags = flags; this.flags = flags;
} }
@Override
public SparseArray<TsPayloadReader> createInitialPayloadReaders() {
return new SparseArray<>();
}
@Override @Override
public TsPayloadReader createPayloadReader(int streamType, EsInfo esInfo) { public TsPayloadReader createPayloadReader(int streamType, EsInfo esInfo) {
switch (streamType) { switch (streamType) {

View File

@ -15,6 +15,10 @@
*/ */
package com.google.android.exoplayer2.extractor.ts; package com.google.android.exoplayer2.extractor.ts;
import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TimestampAdjuster;
import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
/** /**
@ -22,6 +26,17 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
*/ */
public interface SectionPayloadReader { public interface SectionPayloadReader {
/**
* Initializes the section payload reader.
*
* @param timestampAdjuster A timestamp adjuster for offsetting and scaling sample timestamps.
* @param extractorOutput The {@link ExtractorOutput} that receives the extracted data.
* @param idGenerator A {@link PesReader.TrackIdGenerator} that generates unique track ids for the
* {@link TrackOutput}s.
*/
void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput,
TrackIdGenerator idGenerator);
/** /**
* Called by a {@link SectionReader} when a full section is received. * Called by a {@link SectionReader} when a full section is received.
* *

View File

@ -43,8 +43,7 @@ public final class SectionReader implements TsPayloadReader {
@Override @Override
public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput, public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput,
TrackIdGenerator idGenerator) { TrackIdGenerator idGenerator) {
// TODO: Injectable section readers might want to generate metadata tracks. reader.init(timestampAdjuster, extractorOutput, idGenerator);
// Do nothing.
} }
@Override @Override

View File

@ -65,8 +65,6 @@ public final class TsExtractor implements Extractor {
public static final int TS_STREAM_TYPE_H265 = 0x24; public static final int TS_STREAM_TYPE_H265 = 0x24;
public static final int TS_STREAM_TYPE_ID3 = 0x15; public static final int TS_STREAM_TYPE_ID3 = 0x15;
private static final String TAG = "TsExtractor";
private static final int TS_PACKET_SIZE = 188; private static final int TS_PACKET_SIZE = 188;
private static final int TS_SYNC_BYTE = 0x47; // First byte of each TS packet. private static final int TS_SYNC_BYTE = 0x47; // First byte of each TS packet.
private static final int TS_PAT_PID = 0; private static final int TS_PAT_PID = 0;
@ -253,6 +251,12 @@ public final class TsExtractor implements Extractor {
private void resetPayloadReaders() { private void resetPayloadReaders() {
trackIds.clear(); trackIds.clear();
tsPayloadReaders.clear(); tsPayloadReaders.clear();
SparseArray<TsPayloadReader> initialPayloadReaders =
payloadReaderFactory.createInitialPayloadReaders();
int initialPayloadReadersSize = initialPayloadReaders.size();
for (int i = 0; i < initialPayloadReadersSize; i++) {
tsPayloadReaders.put(initialPayloadReaders.keyAt(i), initialPayloadReaders.valueAt(i));
}
tsPayloadReaders.put(TS_PAT_PID, new SectionReader(new PatReader())); tsPayloadReaders.put(TS_PAT_PID, new SectionReader(new PatReader()));
id3Reader = null; id3Reader = null;
} }
@ -268,6 +272,12 @@ public final class TsExtractor implements Extractor {
patScratch = new ParsableBitArray(new byte[4]); patScratch = new ParsableBitArray(new byte[4]);
} }
@Override
public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput,
TrackIdGenerator idGenerator) {
// Do nothing.
}
@Override @Override
public void consume(ParsableByteArray sectionData) { public void consume(ParsableByteArray sectionData) {
// table_id(8), section_syntax_indicator(1), '0'(1), reserved(2), section_length(12), // table_id(8), section_syntax_indicator(1), '0'(1), reserved(2), section_length(12),
@ -310,6 +320,12 @@ public final class TsExtractor implements Extractor {
this.pid = pid; this.pid = pid;
} }
@Override
public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput,
TrackIdGenerator idGenerator) {
// Do nothing.
}
@Override @Override
public void consume(ParsableByteArray sectionData) { public void consume(ParsableByteArray sectionData) {
// table_id(8), section_syntax_indicator(1), '0'(1), reserved(2), section_length(12), // table_id(8), section_syntax_indicator(1), '0'(1), reserved(2), section_length(12),

View File

@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.extractor.ts; package com.google.android.exoplayer2.extractor.ts;
import android.util.SparseArray;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TimestampAdjuster; import com.google.android.exoplayer2.extractor.TimestampAdjuster;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
@ -30,6 +31,15 @@ public interface TsPayloadReader {
*/ */
interface Factory { interface Factory {
/**
* Returns the initial mapping from PIDs to payload readers.
* <p>
* This method allows the injection of payload readers for reserved PIDs, excluding PID 0.
*
* @return A {@link SparseArray} that maps PIDs to payload readers.
*/
SparseArray<TsPayloadReader> createInitialPayloadReaders();
/** /**
* Returns a {@link TsPayloadReader} for a given stream type and elementary stream information. * Returns a {@link TsPayloadReader} for a given stream type and elementary stream information.
* May return null if the stream type is not supported. * May return null if the stream type is not supported.
@ -89,9 +99,10 @@ public interface TsPayloadReader {
/** /**
* Initializes the payload reader. * Initializes the payload reader.
* *
* @param timestampAdjuster * @param timestampAdjuster A timestamp adjuster for offsetting and scaling sample timestamps.
* @param extractorOutput * @param extractorOutput The {@link ExtractorOutput} that receives the extracted data.
* @param idGenerator * @param idGenerator A {@link PesReader.TrackIdGenerator} that generates unique track ids for the
* {@link TrackOutput}s.
*/ */
void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput, void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput,
TrackIdGenerator idGenerator); TrackIdGenerator idGenerator);

View File

@ -23,6 +23,11 @@ import java.util.Arrays;
*/ */
public final class TrackGroupArray { public final class TrackGroupArray {
/**
* The empty array.
*/
public static final TrackGroupArray EMPTY = new TrackGroupArray();
/** /**
* The number of groups in the array. Greater than or equal to zero. * The number of groups in the array. Greater than or equal to zero.
*/ */

View File

@ -17,7 +17,6 @@ package com.google.android.exoplayer2.trackselection;
import android.content.Context; import android.content.Context;
import android.graphics.Point; import android.graphics.Point;
import android.os.Handler;
import android.text.TextUtils; import android.text.TextUtils;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
@ -326,25 +325,18 @@ public class DefaultTrackSelector extends MappingTrackSelector {
/** /**
* Constructs an instance that does not support adaptive video. * Constructs an instance that does not support adaptive video.
*
* @param eventHandler A handler to use when delivering events to listeners. May be null if
* listeners will not be added.
*/ */
public DefaultTrackSelector(Handler eventHandler) { public DefaultTrackSelector() {
this(eventHandler, null); this(null);
} }
/** /**
* Constructs an instance that uses a factory to create adaptive video track selections. * Constructs an instance that uses a factory to create adaptive video track selections.
* *
* @param eventHandler A handler to use when delivering events to listeners. May be null if
* listeners will not be added.
* @param adaptiveVideoTrackSelectionFactory A factory for adaptive video {@link TrackSelection}s, * @param adaptiveVideoTrackSelectionFactory A factory for adaptive video {@link TrackSelection}s,
* or null if the selector should not support adaptive video. * or null if the selector should not support adaptive video.
*/ */
public DefaultTrackSelector(Handler eventHandler, public DefaultTrackSelector(TrackSelection.Factory adaptiveVideoTrackSelectionFactory) {
TrackSelection.Factory adaptiveVideoTrackSelectionFactory) {
super(eventHandler);
this.adaptiveVideoTrackSelectionFactory = adaptiveVideoTrackSelectionFactory; this.adaptiveVideoTrackSelectionFactory = adaptiveVideoTrackSelectionFactory;
params = new AtomicReference<>(new Parameters()); params = new AtomicReference<>(new Parameters());
} }

View File

@ -15,14 +15,13 @@
*/ */
package com.google.android.exoplayer2.trackselection; package com.google.android.exoplayer2.trackselection;
import android.os.Handler; import android.util.Pair;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.SparseBooleanArray; import android.util.SparseBooleanArray;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@ -32,7 +31,7 @@ import java.util.Map;
* Base class for {@link TrackSelector}s that first establish a mapping between {@link TrackGroup}s * Base class for {@link TrackSelector}s that first establish a mapping between {@link TrackGroup}s
* and renderers, and then from that mapping create a {@link TrackSelection} for each renderer. * and renderers, and then from that mapping create a {@link TrackSelection} for each renderer.
*/ */
public abstract class MappingTrackSelector extends TrackSelector<MappedTrackInfo> { public abstract class MappingTrackSelector extends TrackSelector {
/** /**
* A track selection override. * A track selection override.
@ -83,16 +82,21 @@ public abstract class MappingTrackSelector extends TrackSelector<MappedTrackInfo
private final SparseArray<Map<TrackGroupArray, SelectionOverride>> selectionOverrides; private final SparseArray<Map<TrackGroupArray, SelectionOverride>> selectionOverrides;
private final SparseBooleanArray rendererDisabledFlags; private final SparseBooleanArray rendererDisabledFlags;
/** private MappedTrackInfo currentMappedTrackInfo;
* @param eventHandler A handler to use when delivering events to listeners added via
* {@link #addListener(EventListener)}. public MappingTrackSelector() {
*/
public MappingTrackSelector(Handler eventHandler) {
super(eventHandler);
selectionOverrides = new SparseArray<>(); selectionOverrides = new SparseArray<>();
rendererDisabledFlags = new SparseBooleanArray(); rendererDisabledFlags = new SparseBooleanArray();
} }
/**
* Returns the mapping information associated with the current track selections, or null if no
* selection is currently active.
*/
public final MappedTrackInfo getCurrentMappedTrackInfo() {
return currentMappedTrackInfo;
}
/** /**
* Sets whether the renderer at the specified index is disabled. * Sets whether the renderer at the specified index is disabled.
* *
@ -224,7 +228,7 @@ public abstract class MappingTrackSelector extends TrackSelector<MappedTrackInfo
// TrackSelector implementation. // TrackSelector implementation.
@Override @Override
public final TrackSelections<MappedTrackInfo> selectTracks( public final Pair<TrackSelectionArray, Object> selectTracks(
RendererCapabilities[] rendererCapabilities, TrackGroupArray trackGroups) RendererCapabilities[] rendererCapabilities, TrackGroupArray trackGroups)
throws ExoPlaybackException { throws ExoPlaybackException {
// Structures into which data will be written during the selection. The extra item at the end // Structures into which data will be written during the selection. The extra item at the end
@ -294,7 +298,13 @@ public abstract class MappingTrackSelector extends TrackSelector<MappedTrackInfo
MappedTrackInfo mappedTrackInfo = new MappedTrackInfo(rendererTrackTypes, MappedTrackInfo mappedTrackInfo = new MappedTrackInfo(rendererTrackTypes,
rendererTrackGroupArrays, mixedMimeTypeAdaptationSupport, rendererFormatSupports, rendererTrackGroupArrays, mixedMimeTypeAdaptationSupport, rendererFormatSupports,
unassociatedTrackGroupArray); unassociatedTrackGroupArray);
return new TrackSelections<>(mappedTrackInfo, trackSelections); return Pair.<TrackSelectionArray, Object>create(new TrackSelectionArray(trackSelections),
mappedTrackInfo);
}
@Override
public final void onSelectionActivated(Object info) {
currentMappedTrackInfo = (MappedTrackInfo) info;
} }
/** /**
@ -409,12 +419,16 @@ public abstract class MappingTrackSelector extends TrackSelector<MappedTrackInfo
*/ */
public static final int RENDERER_SUPPORT_PLAYABLE_TRACKS = 2; public static final int RENDERER_SUPPORT_PLAYABLE_TRACKS = 2;
/**
* The number of renderers to which tracks are mapped.
*/
public final int length;
private final int[] rendererTrackTypes; private final int[] rendererTrackTypes;
private final TrackGroupArray[] trackGroups; private final TrackGroupArray[] trackGroups;
private final int[] mixedMimeTypeAdaptiveSupport; private final int[] mixedMimeTypeAdaptiveSupport;
private final int[][][] formatSupport; private final int[][][] formatSupport;
private final TrackGroupArray unassociatedTrackGroups; private final TrackGroupArray unassociatedTrackGroups;
private final int rendererCount;
/** /**
* @param rendererTrackTypes The track type supported by each renderer. * @param rendererTrackTypes The track type supported by each renderer.
@ -433,7 +447,7 @@ public abstract class MappingTrackSelector extends TrackSelector<MappedTrackInfo
this.formatSupport = formatSupport; this.formatSupport = formatSupport;
this.mixedMimeTypeAdaptiveSupport = mixedMimeTypeAdaptiveSupport; this.mixedMimeTypeAdaptiveSupport = mixedMimeTypeAdaptiveSupport;
this.unassociatedTrackGroups = unassociatedTrackGroups; this.unassociatedTrackGroups = unassociatedTrackGroups;
this.rendererCount = trackGroups.length; this.length = trackGroups.length;
} }
/** /**
@ -573,7 +587,7 @@ public abstract class MappingTrackSelector extends TrackSelector<MappedTrackInfo
*/ */
public boolean hasOnlyUnplayableTracks(int trackType) { public boolean hasOnlyUnplayableTracks(int trackType) {
int rendererSupport = RENDERER_SUPPORT_NO_TRACKS; int rendererSupport = RENDERER_SUPPORT_NO_TRACKS;
for (int i = 0; i < rendererCount; i++) { for (int i = 0; i < length; i++) {
if (rendererTrackTypes[i] == trackType) { if (rendererTrackTypes[i] == trackType) {
rendererSupport = Math.max(rendererSupport, getRendererSupport(i)); rendererSupport = Math.max(rendererSupport, getRendererSupport(i));
} }

View File

@ -20,12 +20,8 @@ import java.util.Arrays;
/** /**
* The result of a {@link TrackSelector} operation. * The result of a {@link TrackSelector} operation.
*/ */
public final class TrackSelections<T> { public final class TrackSelectionArray {
/**
* Opaque information associated with the result.
*/
public final T info;
/** /**
* The number of selections in the result. Greater than or equal to zero. * The number of selections in the result. Greater than or equal to zero.
*/ */
@ -37,11 +33,9 @@ public final class TrackSelections<T> {
private int hashCode; private int hashCode;
/** /**
* @param info Opaque information associated with the result.
* @param trackSelections The selections. Must not be null, but may contain null elements. * @param trackSelections The selections. Must not be null, but may contain null elements.
*/ */
public TrackSelections(T info, TrackSelection... trackSelections) { public TrackSelectionArray(TrackSelection... trackSelections) {
this.info = info;
this.trackSelections = trackSelections; this.trackSelections = trackSelections;
this.length = trackSelections.length; this.length = trackSelections.length;
} }
@ -81,7 +75,7 @@ public final class TrackSelections<T> {
if (obj == null || getClass() != obj.getClass()) { if (obj == null || getClass() != obj.getClass()) {
return false; return false;
} }
TrackSelections<?> other = (TrackSelections<?>) obj; TrackSelectionArray other = (TrackSelectionArray) obj;
return Arrays.equals(trackSelections, other.trackSelections); return Arrays.equals(trackSelections, other.trackSelections);
} }

View File

@ -15,15 +15,13 @@
*/ */
package com.google.android.exoplayer2.trackselection; package com.google.android.exoplayer2.trackselection;
import android.os.Handler; import android.util.Pair;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.util.Assertions;
import java.util.concurrent.CopyOnWriteArraySet;
/** Selects tracks to be consumed by available renderers. */ /** Selects tracks to be consumed by available renderers. */
public abstract class TrackSelector<T> { public abstract class TrackSelector {
/** /**
* Notified when previous selections by a {@link TrackSelector} are no longer valid. * Notified when previous selections by a {@link TrackSelector} are no longer valid.
@ -37,55 +35,7 @@ public abstract class TrackSelector<T> {
} }
/** Listener of {@link TrackSelector} events. */
public interface EventListener<T> {
/**
* Called when the track selections have changed.
*
* @param trackSelections The new track selections.
*/
void onTrackSelectionsChanged(TrackSelections<? extends T> trackSelections);
}
private final Handler eventHandler;
private final CopyOnWriteArraySet<MappingTrackSelector.EventListener<? super T>> listeners;
private InvalidationListener listener; private InvalidationListener listener;
private TrackSelections<T> activeSelections;
/**
* @param eventHandler A handler to use when delivering events to listeners added via {@link
* #addListener(EventListener)}.
*/
public TrackSelector(Handler eventHandler) {
this.eventHandler = Assertions.checkNotNull(eventHandler);
this.listeners = new CopyOnWriteArraySet<>();
}
/**
* Registers a listener to receive events from the selector. The listener's methods will be called
* using the {@link Handler} that was passed to the constructor.
*
* @param listener The listener to register.
*/
public final void addListener(EventListener<? super T> listener) {
listeners.add(listener);
}
/**
* Unregister a listener. The listener will no longer receive events from the selector.
*
* @param listener The listener to unregister.
*/
public final void removeListener(EventListener<? super T> listener) {
listeners.remove(listener);
}
/** Returns the current track selections. */
public final TrackSelections<T> getCurrentSelections() {
return activeSelections;
}
/** /**
* Initializes the selector. * Initializes the selector.
@ -97,28 +47,27 @@ public abstract class TrackSelector<T> {
} }
/** /**
* Generates {@link TrackSelections} for the renderers. * Generates {@link TrackSelectionArray} for the renderers.
* *
* @param rendererCapabilities The {@link RendererCapabilities} of the renderers for which {@link * @param rendererCapabilities The {@link RendererCapabilities} of the renderers for which
* TrackSelection}s are to be generated. * {@link TrackSelection}s are to be generated.
* @param trackGroups The available track groups. * @param trackGroups The available track groups.
* @return The track selections. * @return The track selections, and an implementation specific object that will be returned to
* the selector via {@link #onSelectionActivated(Object)} should the selections be activated.
* @throws ExoPlaybackException If an error occurs selecting tracks. * @throws ExoPlaybackException If an error occurs selecting tracks.
*/ */
public abstract TrackSelections<T> selectTracks( public abstract Pair<TrackSelectionArray, Object> selectTracks(
RendererCapabilities[] rendererCapabilities, TrackGroupArray trackGroups) RendererCapabilities[] rendererCapabilities, TrackGroupArray trackGroups)
throws ExoPlaybackException; throws ExoPlaybackException;
/** /**
* Called when {@link TrackSelections} previously generated by {@link * Called when {@link TrackSelectionArray} previously generated by
* #selectTracks(RendererCapabilities[], TrackGroupArray)} are activated. * {@link #selectTracks(RendererCapabilities[], TrackGroupArray)} are activated.
* *
* @param activeSelections The activated {@link TrackSelections}. * @param info The information associated with the selections, or null if the selected tracks are
* being cleared.
*/ */
public final void onSelectionActivated(TrackSelections<T> activeSelections) { public abstract void onSelectionActivated(Object info);
this.activeSelections = activeSelections;
notifyTrackSelectionsChanged(activeSelections);
}
/** /**
* Invalidates all previously generated track selections. * Invalidates all previously generated track selections.
@ -129,18 +78,4 @@ public abstract class TrackSelector<T> {
} }
} }
private void notifyTrackSelectionsChanged(final TrackSelections<T> activeSelections) {
if (eventHandler != null) {
eventHandler.post(
new Runnable() {
@Override
public void run() {
for (EventListener<? super T> listener : listeners) {
listener.onTrackSelectionsChanged(activeSelections);
}
}
});
}
}
} }

View File

@ -22,6 +22,8 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.decoder.DecoderCounters; import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
/** /**
* A helper class for periodically updating a {@link TextView} with debug information obtained from * A helper class for periodically updating a {@link TextView} with debug information obtained from
@ -98,6 +100,11 @@ public final class DebugTextViewHelper implements Runnable, ExoPlayer.EventListe
// Do nothing. // Do nothing.
} }
@Override
public void onTracksChanged(TrackGroupArray tracks, TrackSelectionArray selections) {
// Do nothing.
}
// Runnable implementation. // Runnable implementation.
@Override @Override

View File

@ -24,7 +24,6 @@ import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.SeekBar; import android.widget.SeekBar;
import android.widget.TextView; import android.widget.TextView;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
@ -32,12 +31,24 @@ import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.R; import com.google.android.exoplayer2.R;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.util.Formatter; import java.util.Formatter;
import java.util.Locale; import java.util.Locale;
/** /**
* A view to control video playback of an {@link ExoPlayer}. * A view to control video playback of an {@link ExoPlayer}.
*
* By setting the view attribute {@code controller_layout_id} a layout resource to use can
* be customized. All views are optional but if the buttons should have an appropriate logic
* assigned, the id of the views in the layout have to match the expected ids as follows:
*
* <ul>
* <li>Playback: {@code exo_play}, {@code exo_pause}, {@code exo_ffwd}, {@code exo_rew}.</li>
* <li>Progress: {@code exo_progress}, {@code exo_time_current}, {@code exo_time}.</li>
* <li>Playlist navigation: {@code exo_previous}, {@code exo_next}.</li>
* </ul>
*/ */
public class PlaybackControlView extends FrameLayout { public class PlaybackControlView extends FrameLayout {
@ -63,12 +74,13 @@ public class PlaybackControlView extends FrameLayout {
private final ComponentListener componentListener; private final ComponentListener componentListener;
private final View previousButton; private final View previousButton;
private final View nextButton; private final View nextButton;
private final ImageButton playButton; private final View playButton;
private final View pauseButton;
private final View fastForwardButton;
private final View rewindButton;
private final TextView time; private final TextView time;
private final TextView timeCurrent; private final TextView timeCurrent;
private final SeekBar progressBar; private final SeekBar progressBar;
private final View fastForwardButton;
private final View rewindButton;
private final StringBuilder formatBuilder; private final StringBuilder formatBuilder;
private final Formatter formatter; private final Formatter formatter;
private final Timeline.Window currentWindow; private final Timeline.Window currentWindow;
@ -108,6 +120,7 @@ public class PlaybackControlView extends FrameLayout {
public PlaybackControlView(Context context, AttributeSet attrs, int defStyleAttr) { public PlaybackControlView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr); super(context, attrs, defStyleAttr);
int layoutResourceId = R.layout.exo_playback_control_view;
rewindMs = DEFAULT_REWIND_MS; rewindMs = DEFAULT_REWIND_MS;
fastForwardMs = DEFAULT_FAST_FORWARD_MS; fastForwardMs = DEFAULT_FAST_FORWARD_MS;
showTimeoutMs = DEFAULT_SHOW_TIMEOUT_MS; showTimeoutMs = DEFAULT_SHOW_TIMEOUT_MS;
@ -119,32 +132,49 @@ public class PlaybackControlView extends FrameLayout {
fastForwardMs = a.getInt(R.styleable.PlaybackControlView_fastforward_increment, fastForwardMs = a.getInt(R.styleable.PlaybackControlView_fastforward_increment,
fastForwardMs); fastForwardMs);
showTimeoutMs = a.getInt(R.styleable.PlaybackControlView_show_timeout, showTimeoutMs); showTimeoutMs = a.getInt(R.styleable.PlaybackControlView_show_timeout, showTimeoutMs);
layoutResourceId = a.getResourceId(R.styleable.PlaybackControlView_controller_layout_id,
layoutResourceId);
} finally { } finally {
a.recycle(); a.recycle();
} }
} }
currentWindow = new Timeline.Window(); currentWindow = new Timeline.Window();
formatBuilder = new StringBuilder(); formatBuilder = new StringBuilder();
formatter = new Formatter(formatBuilder, Locale.getDefault()); formatter = new Formatter(formatBuilder, Locale.getDefault());
componentListener = new ComponentListener(); componentListener = new ComponentListener();
LayoutInflater.from(context).inflate(R.layout.exo_playback_control_view, this); LayoutInflater.from(context).inflate(layoutResourceId, this);
time = (TextView) findViewById(R.id.time); time = (TextView) findViewById(R.id.exo_time);
timeCurrent = (TextView) findViewById(R.id.time_current); timeCurrent = (TextView) findViewById(R.id.exo_time_current);
progressBar = (SeekBar) findViewById(R.id.mediacontroller_progress); progressBar = (SeekBar) findViewById(R.id.exo_progress);
progressBar.setOnSeekBarChangeListener(componentListener); if (progressBar != null) {
progressBar.setMax(PROGRESS_BAR_MAX); progressBar.setOnSeekBarChangeListener(componentListener);
playButton = (ImageButton) findViewById(R.id.play); progressBar.setMax(PROGRESS_BAR_MAX);
playButton.setOnClickListener(componentListener); }
previousButton = findViewById(R.id.prev); playButton = findViewById(R.id.exo_play);
previousButton.setOnClickListener(componentListener); if (playButton != null) {
nextButton = findViewById(R.id.next); playButton.setOnClickListener(componentListener);
nextButton.setOnClickListener(componentListener); }
rewindButton = findViewById(R.id.rew); pauseButton = findViewById(R.id.exo_pause);
rewindButton.setOnClickListener(componentListener); if (pauseButton != null) {
fastForwardButton = findViewById(R.id.ffwd); pauseButton.setOnClickListener(componentListener);
fastForwardButton.setOnClickListener(componentListener); }
previousButton = findViewById(R.id.exo_prev);
if (previousButton != null) {
previousButton.setOnClickListener(componentListener);
}
nextButton = findViewById(R.id.exo_next);
if (nextButton != null) {
nextButton.setOnClickListener(componentListener);
}
rewindButton = findViewById(R.id.exo_rew);
if (rewindButton != null) {
rewindButton.setOnClickListener(componentListener);
}
fastForwardButton = findViewById(R.id.exo_ffwd);
if (fastForwardButton != null) {
fastForwardButton.setOnClickListener(componentListener);
}
} }
/** /**
@ -285,11 +315,12 @@ public class PlaybackControlView extends FrameLayout {
return; return;
} }
boolean playing = player != null && player.getPlayWhenReady(); boolean playing = player != null && player.getPlayWhenReady();
String contentDescription = getResources().getString( if (playButton != null) {
playing ? R.string.exo_controls_pause_description : R.string.exo_controls_play_description); playButton.setVisibility(playing ? GONE : VISIBLE);
playButton.setContentDescription(contentDescription); }
playButton.setImageResource( if (pauseButton != null) {
playing ? R.drawable.exo_controls_pause : R.drawable.exo_controls_play); pauseButton.setVisibility(playing ? VISIBLE : GONE);
}
} }
private void updateNavigation() { private void updateNavigation() {
@ -313,7 +344,9 @@ public class PlaybackControlView extends FrameLayout {
setButtonEnabled(enableNext, nextButton); setButtonEnabled(enableNext, nextButton);
setButtonEnabled(fastForwardMs > 0 && isSeekable, fastForwardButton); setButtonEnabled(fastForwardMs > 0 && isSeekable, fastForwardButton);
setButtonEnabled(rewindMs > 0 && isSeekable, rewindButton); setButtonEnabled(rewindMs > 0 && isSeekable, rewindButton);
progressBar.setEnabled(isSeekable); if (progressBar != null) {
progressBar.setEnabled(isSeekable);
}
} }
private void updateProgress() { private void updateProgress() {
@ -322,16 +355,21 @@ public class PlaybackControlView extends FrameLayout {
} }
long duration = player == null ? 0 : player.getDuration(); long duration = player == null ? 0 : player.getDuration();
long position = player == null ? 0 : player.getCurrentPosition(); long position = player == null ? 0 : player.getCurrentPosition();
time.setText(stringForTime(duration)); if (time != null) {
if (!dragging) { time.setText(stringForTime(duration));
}
if (timeCurrent != null && !dragging) {
timeCurrent.setText(stringForTime(position)); timeCurrent.setText(stringForTime(position));
} }
if (!dragging) {
progressBar.setProgress(progressBarValue(position)); if (progressBar != null) {
if (!dragging) {
progressBar.setProgress(progressBarValue(position));
}
long bufferedPosition = player == null ? 0 : player.getBufferedPosition();
progressBar.setSecondaryProgress(progressBarValue(bufferedPosition));
// Remove scheduled updates.
} }
long bufferedPosition = player == null ? 0 : player.getBufferedPosition();
progressBar.setSecondaryProgress(progressBarValue(bufferedPosition));
// Remove scheduled updates.
removeCallbacks(updateProgressAction); removeCallbacks(updateProgressAction);
// Schedule an update if necessary. // Schedule an update if necessary.
int playbackState = player == null ? ExoPlayer.STATE_IDLE : player.getPlaybackState(); int playbackState = player == null ? ExoPlayer.STATE_IDLE : player.getPlaybackState();
@ -350,6 +388,9 @@ public class PlaybackControlView extends FrameLayout {
} }
private void setButtonEnabled(boolean enabled, View view) { private void setButtonEnabled(boolean enabled, View view) {
if (view == null) {
return;
}
view.setEnabled(enabled); view.setEnabled(enabled);
if (Util.SDK_INT >= 11) { if (Util.SDK_INT >= 11) {
setViewAlphaV11(view, enabled ? 1f : 0.3f); setViewAlphaV11(view, enabled ? 1f : 0.3f);
@ -500,7 +541,7 @@ public class PlaybackControlView extends FrameLayout {
@Override @Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) { if (fromUser && timeCurrent != null) {
timeCurrent.setText(stringForTime(positionValue(progress))); timeCurrent.setText(stringForTime(positionValue(progress)));
} }
} }
@ -508,7 +549,9 @@ public class PlaybackControlView extends FrameLayout {
@Override @Override
public void onStopTrackingTouch(SeekBar seekBar) { public void onStopTrackingTouch(SeekBar seekBar) {
dragging = false; dragging = false;
player.seekTo(positionValue(seekBar.getProgress())); if (player != null) {
player.seekTo(positionValue(seekBar.getProgress()));
}
hideAfterTimeout(); hideAfterTimeout();
} }
@ -535,6 +578,11 @@ public class PlaybackControlView extends FrameLayout {
// Do nothing. // Do nothing.
} }
@Override
public void onTracksChanged(TrackGroupArray tracks, TrackSelectionArray selections) {
// Do nothing.
}
@Override @Override
public void onPlayerError(ExoPlaybackException error) { public void onPlayerError(ExoPlaybackException error) {
// Do nothing. // Do nothing.
@ -542,17 +590,20 @@ public class PlaybackControlView extends FrameLayout {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
Timeline currentTimeline = player.getCurrentTimeline(); if (player != null) {
if (nextButton == view) { if (nextButton == view) {
next(); next();
} else if (previousButton == view) { } else if (previousButton == view) {
previous(); previous();
} else if (fastForwardButton == view) { } else if (fastForwardButton == view) {
fastForward(); fastForward();
} else if (rewindButton == view && currentTimeline != null) { } else if (rewindButton == view) {
rewind(); rewind();
} else if (playButton == view) { } else if (playButton == view) {
player.setPlayWhenReady(!player.getPlayWhenReady()); player.setPlayWhenReady(true);
} else if (pauseButton == view) {
player.setPlayWhenReady(false);
}
} }
hideAfterTimeout(); hideAfterTimeout();
} }

View File

@ -27,13 +27,16 @@ import android.view.TextureView;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.R; import com.google.android.exoplayer2.R;
import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.TextRenderer; import com.google.android.exoplayer2.text.TextRenderer;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import java.util.List; import java.util.List;
/** /**
@ -90,23 +93,30 @@ public final class SimpleExoPlayerView extends FrameLayout {
LayoutInflater.from(context).inflate(R.layout.exo_simple_player_view, this); LayoutInflater.from(context).inflate(R.layout.exo_simple_player_view, this);
componentListener = new ComponentListener(); componentListener = new ComponentListener();
layout = (AspectRatioFrameLayout) findViewById(R.id.video_frame); layout = (AspectRatioFrameLayout) findViewById(R.id.exo_video_frame);
layout.setResizeMode(resizeMode); layout.setResizeMode(resizeMode);
shutterView = findViewById(R.id.shutter); shutterView = findViewById(R.id.exo_shutter);
subtitleLayout = (SubtitleView) findViewById(R.id.subtitles); subtitleLayout = (SubtitleView) findViewById(R.id.exo_subtitles);
subtitleLayout.setUserDefaultStyle(); subtitleLayout.setUserDefaultStyle();
subtitleLayout.setUserDefaultTextSize(); subtitleLayout.setUserDefaultTextSize();
controller = (PlaybackControlView) findViewById(R.id.control); View controllerPlaceholder = findViewById(R.id.exo_controller_placeholder);
controller.hide();
controller = new PlaybackControlView(context, attrs);
controller.setRewindIncrementMs(rewindMs); controller.setRewindIncrementMs(rewindMs);
controller.setFastForwardIncrementMs(fastForwardMs); controller.setFastForwardIncrementMs(fastForwardMs);
controller.setLayoutParams(controllerPlaceholder.getLayoutParams());
controller.hide();
this.controllerShowTimeoutMs = controllerShowTimeoutMs; this.controllerShowTimeoutMs = controllerShowTimeoutMs;
ViewGroup parent = ((ViewGroup) controllerPlaceholder.getParent());
int controllerIndex = parent.indexOfChild(controllerPlaceholder);
parent.removeView(controllerPlaceholder);
parent.addView(controller, controllerIndex);
View view = useTextureView ? new TextureView(context) : new SurfaceView(context); View view = useTextureView ? new TextureView(context) : new SurfaceView(context);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams( ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
ViewGroup.LayoutParams.MATCH_PARENT);
view.setLayoutParams(params); view.setLayoutParams(params);
surfaceView = view; surfaceView = view;
layout.addView(surfaceView, 0); layout.addView(surfaceView, 0);
@ -318,7 +328,13 @@ public final class SimpleExoPlayerView extends FrameLayout {
} }
@Override @Override
public void onVideoTracksDisabled() { public void onTracksChanged(TrackGroupArray tracks, TrackSelectionArray selections) {
for (int i = 0; i < selections.length; i++) {
if (player.getRendererType(i) == C.TRACK_TYPE_VIDEO && selections.get(i) != null) {
return;
}
}
// No enabled video renderers. Close the shutter.
shutterView.setVisibility(VISIBLE); shutterView.setVisibility(VISIBLE);
} }

View File

@ -122,10 +122,10 @@ public final class SubtitleView extends View implements TextRenderer.Output {
/** /**
* Sets the text size to one derived from {@link CaptioningManager#getFontScale()}, or to a * Sets the text size to one derived from {@link CaptioningManager#getFontScale()}, or to a
* default size on API level 19 and earlier. * default size before API level 19.
*/ */
public void setUserDefaultTextSize() { public void setUserDefaultTextSize() {
float fontScale = Util.SDK_INT >= 19 ? getUserCaptionFontScaleV19() : 1f; float fontScale = Util.SDK_INT >= 19 && !isInEditMode() ? getUserCaptionFontScaleV19() : 1f;
setFractionalTextSize(DEFAULT_TEXT_SIZE_FRACTION * fontScale); setFractionalTextSize(DEFAULT_TEXT_SIZE_FRACTION * fontScale);
} }
@ -180,10 +180,11 @@ public final class SubtitleView extends View implements TextRenderer.Output {
/** /**
* Sets the caption style to be equivalent to the one returned by * Sets the caption style to be equivalent to the one returned by
* {@link CaptioningManager#getUserStyle()}, or to a default style on API level 19 and earlier. * {@link CaptioningManager#getUserStyle()}, or to a default style before API level 19.
*/ */
public void setUserDefaultStyle() { public void setUserDefaultStyle() {
setStyle(Util.SDK_INT >= 19 ? getUserCaptionStyleV19() : CaptionStyleCompat.DEFAULT); setStyle(Util.SDK_INT >= 19 && !isInEditMode()
? getUserCaptionStyleV19() : CaptionStyleCompat.DEFAULT);
} }
/** /**

View File

@ -14,7 +14,6 @@
limitations under the License. limitations under the License.
--> -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom" android:layout_gravity="bottom"
@ -29,24 +28,22 @@
android:paddingTop="4dp" android:paddingTop="4dp"
android:orientation="horizontal"> android:orientation="horizontal">
<ImageButton android:id="@+id/prev" <ImageButton android:id="@+id/exo_prev"
android:contentDescription="@string/exo_controls_previous_description"
style="@style/ExoMediaButton.Previous"/> style="@style/ExoMediaButton.Previous"/>
<ImageButton android:id="@+id/rew" <ImageButton android:id="@+id/exo_rew"
android:contentDescription="@string/exo_controls_rewind_description"
style="@style/ExoMediaButton.Rewind"/> style="@style/ExoMediaButton.Rewind"/>
<ImageButton android:id="@+id/play" <ImageButton android:id="@+id/exo_play"
tools:ignore="ContentDescription" style="@style/ExoMediaButton.Play"/>
style="@style/ExoMediaButton"/>
<ImageButton android:id="@+id/ffwd" <ImageButton android:id="@+id/exo_pause"
android:contentDescription="@string/exo_controls_fastforward_description" style="@style/ExoMediaButton.Pause"/>
<ImageButton android:id="@+id/exo_ffwd"
style="@style/ExoMediaButton.FastForward"/> style="@style/ExoMediaButton.FastForward"/>
<ImageButton android:id="@+id/next" <ImageButton android:id="@+id/exo_next"
android:contentDescription="@string/exo_controls_previous_description"
style="@style/ExoMediaButton.Next"/> style="@style/ExoMediaButton.Next"/>
</LinearLayout> </LinearLayout>
@ -56,7 +53,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"> android:orientation="horizontal">
<TextView android:id="@+id/time_current" <TextView android:id="@+id/exo_time_current"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
@ -67,13 +64,13 @@
android:paddingEnd="4dp" android:paddingEnd="4dp"
android:textColor="#FFBEBEBE"/> android:textColor="#FFBEBEBE"/>
<SeekBar android:id="@+id/mediacontroller_progress" <SeekBar android:id="@+id/exo_progress"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_weight="1" android:layout_weight="1"
android:layout_height="32dp" android:layout_height="32dp"
style="?android:attr/progressBarStyleHorizontal"/> style="?android:attr/progressBarStyleHorizontal"/>
<TextView android:id="@+id/time" <TextView android:id="@+id/exo_time"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"

View File

@ -17,23 +17,23 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_width="match_parent"> android:layout_width="match_parent">
<com.google.android.exoplayer2.ui.AspectRatioFrameLayout android:id="@+id/video_frame" <com.google.android.exoplayer2.ui.AspectRatioFrameLayout android:id="@+id/exo_video_frame"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="center"> android:layout_gravity="center">
<View android:id="@+id/shutter" <View android:id="@+id/exo_shutter"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@android:color/black"/> android:background="@android:color/black"/>
<com.google.android.exoplayer2.ui.SubtitleView android:id="@+id/subtitles" <com.google.android.exoplayer2.ui.SubtitleView android:id="@+id/exo_subtitles"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"/> android:layout_height="match_parent"/>
</com.google.android.exoplayer2.ui.AspectRatioFrameLayout> </com.google.android.exoplayer2.ui.AspectRatioFrameLayout>
<com.google.android.exoplayer2.ui.PlaybackControlView android:id="@+id/control" <View android:id="@+id/exo_controller_placeholder"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"/> android:layout_height="match_parent"/>

View File

@ -23,6 +23,7 @@
<attr name="show_timeout" format="integer"/> <attr name="show_timeout" format="integer"/>
<attr name="rewind_increment" format="integer"/> <attr name="rewind_increment" format="integer"/>
<attr name="fastforward_increment" format="integer"/> <attr name="fastforward_increment" format="integer"/>
<attr name="controller_layout_id" format="reference"/>
<declare-styleable name="SimpleExoPlayerView"> <declare-styleable name="SimpleExoPlayerView">
<attr name="use_controller" format="boolean"/> <attr name="use_controller" format="boolean"/>
@ -31,6 +32,7 @@
<attr name="rewind_increment"/> <attr name="rewind_increment"/>
<attr name="fastforward_increment"/> <attr name="fastforward_increment"/>
<attr name="resize_mode"/> <attr name="resize_mode"/>
<attr name="controller_layout_id"/>
</declare-styleable> </declare-styleable>
<declare-styleable name="AspectRatioFrameLayout"> <declare-styleable name="AspectRatioFrameLayout">
@ -41,6 +43,7 @@
<attr name="show_timeout"/> <attr name="show_timeout"/>
<attr name="rewind_increment"/> <attr name="rewind_increment"/>
<attr name="fastforward_increment"/> <attr name="fastforward_increment"/>
<attr name="controller_layout_id"/>
</declare-styleable> </declare-styleable>
</resources> </resources>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 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.
-->
<resources>
<item name="exo_play" type="id"/>
<item name="exo_pause" type="id"/>
<item name="exo_rew" type="id"/>
<item name="exo_ffwd" type="id"/>
<item name="exo_prev" type="id"/>
<item name="exo_next" type="id"/>
<item name="exo_time" type="id"/>
<item name="exo_time_current" type="id"/>
<item name="exo_progress" type="id"/>
</resources>

View File

@ -41,4 +41,14 @@
<item name="android:contentDescription">@string/exo_controls_rewind_description</item> <item name="android:contentDescription">@string/exo_controls_rewind_description</item>
</style> </style>
<style name="ExoMediaButton.Play">
<item name="android:src">@drawable/exo_controls_play</item>
<item name="android:contentDescription">@string/exo_controls_play_description</item>
</style>
<style name="ExoMediaButton.Pause">
<item name="android:src">@drawable/exo_controls_pause</item>
<item name="android:contentDescription">@string/exo_controls_pause_description</item>
</style>
</resources> </resources>

View File

@ -19,8 +19,6 @@ import android.annotation.TargetApi;
import android.media.MediaDrm; import android.media.MediaDrm;
import android.media.UnsupportedSchemeException; import android.media.UnsupportedSchemeException;
import android.net.Uri; import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.test.ActivityInstrumentationTestCase2; import android.test.ActivityInstrumentationTestCase2;
import android.util.Log; import android.util.Log;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
@ -805,7 +803,6 @@ public final class DashTest extends ActivityInstrumentationTestCase2<HostActivit
private DashTestTrackSelector(String audioFormatId, String[] videoFormatIds, private DashTestTrackSelector(String audioFormatId, String[] videoFormatIds,
boolean canIncludeAdditionalVideoFormats) { boolean canIncludeAdditionalVideoFormats) {
super(new Handler(Looper.getMainLooper()));
this.audioFormatId = audioFormatId; this.audioFormatId = audioFormatId;
this.videoFormatIds = videoFormatIds; this.videoFormatIds = videoFormatIds;
this.canIncludeAdditionalVideoFormats = canIncludeAdditionalVideoFormats; this.canIncludeAdditionalVideoFormats = canIncludeAdditionalVideoFormats;

View File

@ -33,9 +33,11 @@ import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto; import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
import com.google.android.exoplayer2.playbacktests.util.HostActivity.HostedTest; import com.google.android.exoplayer2.playbacktests.util.HostActivity.HostedTest;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.AdaptiveVideoTrackSelection; import com.google.android.exoplayer2.trackselection.AdaptiveVideoTrackSelection;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.MappingTrackSelector; import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.upstream.BandwidthMeter; import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
@ -186,6 +188,11 @@ public abstract class ExoHostedTest implements HostedTest, ExoPlayer.EventListen
// Do nothing. // Do nothing.
} }
@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
// Do nothing.
}
@Override @Override
public final void onPlayerStateChanged(boolean playWhenReady, int playbackState) { public final void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
Log.d(tag, "state [" + playWhenReady + ", " + playbackState + "]"); Log.d(tag, "state [" + playWhenReady + ", " + playbackState + "]");
@ -305,7 +312,7 @@ public abstract class ExoHostedTest implements HostedTest, ExoPlayer.EventListen
@SuppressWarnings("unused") @SuppressWarnings("unused")
protected MappingTrackSelector buildTrackSelector(HostActivity host, protected MappingTrackSelector buildTrackSelector(HostActivity host,
BandwidthMeter bandwidthMeter) { BandwidthMeter bandwidthMeter) {
return new DefaultTrackSelector(null, new AdaptiveVideoTrackSelection.Factory(bandwidthMeter)); return new DefaultTrackSelector(new AdaptiveVideoTrackSelection.Factory(bandwidthMeter));
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")