mirror of
https://github.com/androidx/media.git
synced 2025-05-17 12:39:52 +08:00
Merge branch 'dev-v2' into dev-v2-id3
This commit is contained in:
commit
0c8f3c7270
@ -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(
|
||||||
|
@ -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;
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
BIN
library/src/androidTest/assets/ts/sample_with_sdt.ts
Normal file
BIN
library/src/androidTest/assets/ts/sample_with_sdt.ts
Normal file
Binary file not shown.
@ -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++;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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}.
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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.
|
||||||
|
@ -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)
|
||||||
|
@ -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) {
|
||||||
|
@ -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.
|
||||||
*
|
*
|
||||||
|
@ -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
|
||||||
|
@ -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),
|
||||||
|
@ -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);
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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"
|
||||||
|
@ -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"/>
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
26
library/src/main/res/values/ids.xml
Normal file
26
library/src/main/res/values/ids.xml
Normal 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>
|
@ -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>
|
||||||
|
@ -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;
|
||||||
|
@ -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")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user