Commit e770e5c2 by Oliver Woodman

Multi-track - The (nearly) final step.

- Migrate demo app to use new APIs.
- Add multi-track support for ExtractorSampleSource case.
- Add multi-track support for SmoothStreaming use case.

The final step is to add support back for the DASH use case and
delete MultiTrackChunkSource. This is blocked on multi-period support
landing, in order to prevent a horrendous merge conflict. We also
need to update HLS to expose sensible track information.

Issue: #514
parent 57250036
...@@ -17,6 +17,7 @@ package com.google.android.exoplayer.demo; ...@@ -17,6 +17,7 @@ package com.google.android.exoplayer.demo;
import com.google.android.exoplayer.AspectRatioFrameLayout; import com.google.android.exoplayer.AspectRatioFrameLayout;
import com.google.android.exoplayer.ExoPlayer; import com.google.android.exoplayer.ExoPlayer;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.audio.AudioCapabilities; import com.google.android.exoplayer.audio.AudioCapabilities;
import com.google.android.exoplayer.audio.AudioCapabilitiesReceiver; import com.google.android.exoplayer.audio.AudioCapabilitiesReceiver;
import com.google.android.exoplayer.demo.player.DashRendererBuilder; import com.google.android.exoplayer.demo.player.DashRendererBuilder;
...@@ -33,6 +34,7 @@ import com.google.android.exoplayer.text.CaptionStyleCompat; ...@@ -33,6 +34,7 @@ import com.google.android.exoplayer.text.CaptionStyleCompat;
import com.google.android.exoplayer.text.Cue; import com.google.android.exoplayer.text.Cue;
import com.google.android.exoplayer.text.SubtitleLayout; import com.google.android.exoplayer.text.SubtitleLayout;
import com.google.android.exoplayer.util.DebugTextViewHelper; import com.google.android.exoplayer.util.DebugTextViewHelper;
import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.Util; import com.google.android.exoplayer.util.Util;
import com.google.android.exoplayer.util.VerboseLogUtil; import com.google.android.exoplayer.util.VerboseLogUtil;
...@@ -435,23 +437,34 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, ...@@ -435,23 +437,34 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
}); });
Menu menu = popup.getMenu(); Menu menu = popup.getMenu();
// ID_OFFSET ensures we avoid clashing with Menu.NONE (which equals 0) // ID_OFFSET ensures we avoid clashing with Menu.NONE (which equals 0)
menu.add(MENU_GROUP_TRACKS, DemoPlayer.DISABLED_TRACK + ID_OFFSET, Menu.NONE, R.string.off); menu.add(MENU_GROUP_TRACKS, DemoPlayer.TRACK_DISABLED + ID_OFFSET, Menu.NONE, R.string.off);
if (trackCount == 1 && TextUtils.isEmpty(player.getTrackName(trackType, 0))) {
menu.add(MENU_GROUP_TRACKS, DemoPlayer.PRIMARY_TRACK + ID_OFFSET, Menu.NONE, R.string.on);
} else {
for (int i = 0; i < trackCount; i++) { for (int i = 0; i < trackCount; i++) {
menu.add(MENU_GROUP_TRACKS, i + ID_OFFSET, Menu.NONE, player.getTrackName(trackType, i)); menu.add(MENU_GROUP_TRACKS, i + ID_OFFSET, Menu.NONE,
} buildTrackName(player.getTrackFormat(trackType, i)));
} }
menu.setGroupCheckable(MENU_GROUP_TRACKS, true, true); menu.setGroupCheckable(MENU_GROUP_TRACKS, true, true);
menu.findItem(player.getSelectedTrackIndex(trackType) + ID_OFFSET).setChecked(true); menu.findItem(player.getSelectedTrack(trackType) + ID_OFFSET).setChecked(true);
}
private static String buildTrackName(MediaFormat format) {
if (format.adaptive) {
return "auto";
} else if (MimeTypes.isVideo(format.mimeType)) {
return format.width + "x" + format.height;
} else if (MimeTypes.isAudio(format.mimeType)) {
return format.channelCount + "ch, " + format.sampleRate + "Hz";
} else if (MimeTypes.isText(format.mimeType) && !TextUtils.isEmpty(format.language)) {
return format.language;
} else {
return "unknown";
}
} }
private boolean onTrackItemClick(MenuItem item, int type) { private boolean onTrackItemClick(MenuItem item, int type) {
if (player == null || item.getGroupId() != MENU_GROUP_TRACKS) { if (player == null || item.getGroupId() != MENU_GROUP_TRACKS) {
return false; return false;
} }
player.selectTrack(type, item.getItemId() - ID_OFFSET); player.setSelectedTrack(type, item.getItemId() - ID_OFFSET);
return true; return true;
} }
......
...@@ -352,7 +352,7 @@ public class DashRendererBuilder implements RendererBuilder { ...@@ -352,7 +352,7 @@ public class DashRendererBuilder implements RendererBuilder {
renderers[DemoPlayer.TYPE_VIDEO] = videoRenderer; renderers[DemoPlayer.TYPE_VIDEO] = videoRenderer;
renderers[DemoPlayer.TYPE_AUDIO] = audioRenderer; renderers[DemoPlayer.TYPE_AUDIO] = audioRenderer;
renderers[DemoPlayer.TYPE_TEXT] = textRenderer; renderers[DemoPlayer.TYPE_TEXT] = textRenderer;
player.onRenderers(trackNames, multiTrackChunkSources, renderers, bandwidthMeter); player.onRenderers(renderers, bandwidthMeter);
} }
private static int getWidevineSecurityLevel(StreamingDrmSessionManager sessionManager) { private static int getWidevineSecurityLevel(StreamingDrmSessionManager sessionManager) {
......
...@@ -23,12 +23,12 @@ import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; ...@@ -23,12 +23,12 @@ import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
import com.google.android.exoplayer.MediaCodecTrackRenderer; import com.google.android.exoplayer.MediaCodecTrackRenderer;
import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializationException; import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializationException;
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.TimeRange; import com.google.android.exoplayer.TimeRange;
import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.audio.AudioTrack; import com.google.android.exoplayer.audio.AudioTrack;
import com.google.android.exoplayer.chunk.ChunkSampleSource; import com.google.android.exoplayer.chunk.ChunkSampleSource;
import com.google.android.exoplayer.chunk.Format; import com.google.android.exoplayer.chunk.Format;
import com.google.android.exoplayer.chunk.MultiTrackChunkSource;
import com.google.android.exoplayer.dash.DashChunkSource; import com.google.android.exoplayer.dash.DashChunkSource;
import com.google.android.exoplayer.drm.StreamingDrmSessionManager; import com.google.android.exoplayer.drm.StreamingDrmSessionManager;
import com.google.android.exoplayer.hls.HlsSampleSource; import com.google.android.exoplayer.hls.HlsSampleSource;
...@@ -46,7 +46,6 @@ import android.os.Looper; ...@@ -46,7 +46,6 @@ import android.os.Looper;
import android.view.Surface; import android.view.Surface;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
...@@ -148,9 +147,8 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi ...@@ -148,9 +147,8 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
public static final int STATE_BUFFERING = ExoPlayer.STATE_BUFFERING; public static final int STATE_BUFFERING = ExoPlayer.STATE_BUFFERING;
public static final int STATE_READY = ExoPlayer.STATE_READY; public static final int STATE_READY = ExoPlayer.STATE_READY;
public static final int STATE_ENDED = ExoPlayer.STATE_ENDED; public static final int STATE_ENDED = ExoPlayer.STATE_ENDED;
public static final int TRACK_DISABLED = ExoPlayer.TRACK_DISABLED;
public static final int DISABLED_TRACK = -1; public static final int TRACK_DEFAULT = ExoPlayer.TRACK_DEFAULT;
public static final int PRIMARY_TRACK = 0;
public static final int RENDERER_COUNT = 4; public static final int RENDERER_COUNT = 4;
public static final int TYPE_VIDEO = 0; public static final int TYPE_VIDEO = 0;
...@@ -179,9 +177,6 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi ...@@ -179,9 +177,6 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
private int videoTrackToRestore; private int videoTrackToRestore;
private BandwidthMeter bandwidthMeter; private BandwidthMeter bandwidthMeter;
private MultiTrackChunkSource[] multiTrackSources;
private String[][] trackNames;
private int[] selectedTracks;
private boolean backgrounded; private boolean backgrounded;
private CaptionListener captionListener; private CaptionListener captionListener;
...@@ -198,9 +193,8 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi ...@@ -198,9 +193,8 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
listeners = new CopyOnWriteArrayList<>(); listeners = new CopyOnWriteArrayList<>();
lastReportedPlaybackState = STATE_IDLE; lastReportedPlaybackState = STATE_IDLE;
rendererBuildingState = RENDERER_BUILDING_STATE_IDLE; rendererBuildingState = RENDERER_BUILDING_STATE_IDLE;
selectedTracks = new int[RENDERER_COUNT];
// Disable text initially. // Disable text initially.
selectedTracks[TYPE_TEXT] = DISABLED_TRACK; player.setSelectedTrack(TYPE_TEXT, TRACK_DISABLED);
} }
public PlayerControl getPlayerControl() { public PlayerControl getPlayerControl() {
...@@ -245,28 +239,20 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi ...@@ -245,28 +239,20 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
pushSurface(true); pushSurface(true);
} }
@SuppressWarnings("deprecation")
public int getTrackCount(int type) { public int getTrackCount(int type) {
return !player.getRendererHasMedia(type) ? 0 : trackNames[type].length; return player.getTrackCount(type);
} }
public String getTrackName(int type, int index) { public MediaFormat getTrackFormat(int type, int index) {
return trackNames[type][index]; return player.getTrackFormat(type, index);
} }
public int getSelectedTrackIndex(int type) { public int getSelectedTrack(int type) {
return selectedTracks[type]; return player.getSelectedTrack(type);
} }
public void selectTrack(int type, int index) { public void setSelectedTrack(int type, int index) {
if (selectedTracks[type] == index) { player.setSelectedTrack(type, index);
return;
}
selectedTracks[type] = index;
pushTrackSelection(type, true);
if (type == TYPE_TEXT && index == DISABLED_TRACK && captionListener != null) {
captionListener.onCues(Collections.<Cue>emptyList());
}
} }
public boolean getBackgrounded() { public boolean getBackgrounded() {
...@@ -279,11 +265,11 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi ...@@ -279,11 +265,11 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
} }
this.backgrounded = backgrounded; this.backgrounded = backgrounded;
if (backgrounded) { if (backgrounded) {
videoTrackToRestore = getSelectedTrackIndex(TYPE_VIDEO); videoTrackToRestore = getSelectedTrack(TYPE_VIDEO);
selectTrack(TYPE_VIDEO, DISABLED_TRACK); setSelectedTrack(TYPE_VIDEO, TRACK_DISABLED);
blockingClearSurface(); blockingClearSurface();
} else { } else {
selectTrack(TYPE_VIDEO, videoTrackToRestore); setSelectedTrack(TYPE_VIDEO, videoTrackToRestore);
} }
} }
...@@ -294,7 +280,6 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi ...@@ -294,7 +280,6 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
rendererBuilder.cancel(); rendererBuilder.cancel();
videoFormat = null; videoFormat = null;
videoRenderer = null; videoRenderer = null;
multiTrackSources = null;
rendererBuildingState = RENDERER_BUILDING_STATE_BUILDING; rendererBuildingState = RENDERER_BUILDING_STATE_BUILDING;
maybeReportPlayerState(); maybeReportPlayerState();
rendererBuilder.buildRenderers(this); rendererBuilder.buildRenderers(this);
...@@ -303,51 +288,25 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi ...@@ -303,51 +288,25 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
/** /**
* Invoked with the results from a {@link RendererBuilder}. * Invoked with the results from a {@link RendererBuilder}.
* *
* @param trackNames The names of the available tracks, indexed by {@link DemoPlayer} TYPE_*
* constants. May be null if the track names are unknown. An individual element may be null
* if the track names are unknown for the corresponding type.
* @param multiTrackSources Sources capable of switching between multiple available tracks,
* indexed by {@link DemoPlayer} TYPE_* constants. May be null if there are no types with
* multiple tracks. An individual element may be null if it does not have multiple tracks.
* @param renderers Renderers indexed by {@link DemoPlayer} TYPE_* constants. An individual * @param renderers Renderers indexed by {@link DemoPlayer} TYPE_* constants. An individual
* element may be null if there do not exist tracks of the corresponding type. * element may be null if there do not exist tracks of the corresponding type.
* @param bandwidthMeter Provides an estimate of the currently available bandwidth. May be null. * @param bandwidthMeter Provides an estimate of the currently available bandwidth. May be null.
*/ */
/* package */ void onRenderers(String[][] trackNames, /* package */ void onRenderers(TrackRenderer[] renderers, BandwidthMeter bandwidthMeter) {
MultiTrackChunkSource[] multiTrackSources, TrackRenderer[] renderers, for (int i = 0; i < RENDERER_COUNT; i++) {
BandwidthMeter bandwidthMeter) { if (renderers[i] == null) {
// Normalize the results.
if (trackNames == null) {
trackNames = new String[RENDERER_COUNT][];
}
if (multiTrackSources == null) {
multiTrackSources = new MultiTrackChunkSource[RENDERER_COUNT];
}
for (int rendererIndex = 0; rendererIndex < RENDERER_COUNT; rendererIndex++) {
if (renderers[rendererIndex] == null) {
// Convert a null renderer to a dummy renderer. // Convert a null renderer to a dummy renderer.
renderers[rendererIndex] = new DummyTrackRenderer(); renderers[i] = new DummyTrackRenderer();
}
if (trackNames[rendererIndex] == null) {
// Convert a null trackNames to an array of suitable length.
int trackCount = multiTrackSources[rendererIndex] != null
? multiTrackSources[rendererIndex].getMultiTrackCount() : 1;
trackNames[rendererIndex] = new String[trackCount];
} }
} }
// Complete preparation. // Complete preparation.
this.trackNames = trackNames;
this.videoRenderer = renderers[TYPE_VIDEO]; this.videoRenderer = renderers[TYPE_VIDEO];
this.codecCounters = videoRenderer instanceof MediaCodecTrackRenderer this.codecCounters = videoRenderer instanceof MediaCodecTrackRenderer
? ((MediaCodecTrackRenderer) videoRenderer).codecCounters ? ((MediaCodecTrackRenderer) videoRenderer).codecCounters
: renderers[TYPE_AUDIO] instanceof MediaCodecTrackRenderer : renderers[TYPE_AUDIO] instanceof MediaCodecTrackRenderer
? ((MediaCodecTrackRenderer) renderers[TYPE_AUDIO]).codecCounters : null; ? ((MediaCodecTrackRenderer) renderers[TYPE_AUDIO]).codecCounters : null;
this.multiTrackSources = multiTrackSources;
this.bandwidthMeter = bandwidthMeter; this.bandwidthMeter = bandwidthMeter;
pushSurface(false); pushSurface(false);
pushTrackSelection(TYPE_VIDEO, true);
pushTrackSelection(TYPE_AUDIO, true);
pushTrackSelection(TYPE_TEXT, true);
player.prepare(renderers); player.prepare(renderers);
rendererBuildingState = RENDERER_BUILDING_STATE_BUILT; rendererBuildingState = RENDERER_BUILDING_STATE_BUILT;
} }
...@@ -537,14 +496,14 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi ...@@ -537,14 +496,14 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
@Override @Override
public void onCues(List<Cue> cues) { public void onCues(List<Cue> cues) {
if (captionListener != null && selectedTracks[TYPE_TEXT] != DISABLED_TRACK) { if (captionListener != null && getSelectedTrack(TYPE_TEXT) != TRACK_DISABLED) {
captionListener.onCues(cues); captionListener.onCues(cues);
} }
} }
@Override @Override
public void onMetadata(Map<String, Object> metadata) { public void onMetadata(Map<String, Object> metadata) {
if (id3MetadataListener != null && selectedTracks[TYPE_METADATA] != DISABLED_TRACK) { if (id3MetadataListener != null && getSelectedTrack(TYPE_METADATA) != TRACK_DISABLED) {
id3MetadataListener.onId3Metadata(metadata); id3MetadataListener.onId3Metadata(metadata);
} }
} }
...@@ -620,26 +579,4 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi ...@@ -620,26 +579,4 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
} }
} }
@SuppressWarnings("deprecation")
private void pushTrackSelection(int type, boolean allowRendererEnable) {
if (multiTrackSources == null) {
return;
}
int trackIndex = selectedTracks[type];
if (trackIndex == DISABLED_TRACK) {
player.setRendererEnabled(type, false);
} else if (multiTrackSources[type] == null) {
player.setRendererEnabled(type, allowRendererEnable);
} else {
boolean playWhenReady = player.getPlayWhenReady();
player.setPlayWhenReady(false);
player.setRendererEnabled(type, false);
player.sendMessage(multiTrackSources[type], MultiTrackChunkSource.MSG_SELECT_TRACK,
trackIndex);
player.setRendererEnabled(type, allowRendererEnable);
player.setPlayWhenReady(playWhenReady);
}
}
} }
...@@ -74,7 +74,7 @@ public class ExtractorRendererBuilder implements RendererBuilder { ...@@ -74,7 +74,7 @@ public class ExtractorRendererBuilder implements RendererBuilder {
renderers[DemoPlayer.TYPE_VIDEO] = videoRenderer; renderers[DemoPlayer.TYPE_VIDEO] = videoRenderer;
renderers[DemoPlayer.TYPE_AUDIO] = audioRenderer; renderers[DemoPlayer.TYPE_AUDIO] = audioRenderer;
renderers[DemoPlayer.TYPE_TEXT] = textRenderer; renderers[DemoPlayer.TYPE_TEXT] = textRenderer;
player.onRenderers(null, null, renderers, bandwidthMeter); player.onRenderers(renderers, bandwidthMeter);
} }
@Override @Override
......
...@@ -162,7 +162,7 @@ public class HlsRendererBuilder implements RendererBuilder { ...@@ -162,7 +162,7 @@ public class HlsRendererBuilder implements RendererBuilder {
renderers[DemoPlayer.TYPE_AUDIO] = audioRenderer; renderers[DemoPlayer.TYPE_AUDIO] = audioRenderer;
renderers[DemoPlayer.TYPE_METADATA] = id3Renderer; renderers[DemoPlayer.TYPE_METADATA] = id3Renderer;
renderers[DemoPlayer.TYPE_TEXT] = closedCaptionRenderer; renderers[DemoPlayer.TYPE_TEXT] = closedCaptionRenderer;
player.onRenderers(null, null, renderers, bandwidthMeter); player.onRenderers(renderers, bandwidthMeter);
} }
} }
......
...@@ -28,6 +28,7 @@ import android.text.TextUtils; ...@@ -28,6 +28,7 @@ import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
/** /**
...@@ -42,7 +43,7 @@ public final class MediaCodecUtil { ...@@ -42,7 +43,7 @@ public final class MediaCodecUtil {
* Such failures are not expected in normal operation and are normally temporary (e.g. if the * Such failures are not expected in normal operation and are normally temporary (e.g. if the
* mediaserver process has crashed and is yet to restart). * mediaserver process has crashed and is yet to restart).
*/ */
public static class DecoderQueryException extends Exception { public static class DecoderQueryException extends IOException {
private DecoderQueryException(Throwable cause) { private DecoderQueryException(Throwable cause) {
super("Failed to query underlying media codecs", cause); super("Failed to query underlying media codecs", cause);
......
...@@ -187,15 +187,6 @@ public final class MediaFormat { ...@@ -187,15 +187,6 @@ public final class MediaFormat {
NO_VALUE, NO_VALUE, null, OFFSET_SAMPLE_RELATIVE, null, false, NO_VALUE, NO_VALUE); NO_VALUE, NO_VALUE, null, OFFSET_SAMPLE_RELATIVE, null, false, NO_VALUE, NO_VALUE);
} }
public static MediaFormat createAdaptiveFormat(String mimeType) {
return createAdaptiveFormat(mimeType, C.UNKNOWN_TIME_US);
}
public static MediaFormat createAdaptiveFormat(String mimeType, long durationUs) {
return new MediaFormat(mimeType, NO_VALUE, durationUs, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, NO_VALUE, null, OFFSET_SAMPLE_RELATIVE, null, true, NO_VALUE, NO_VALUE);
}
/* package */ MediaFormat(String mimeType, int maxInputSize, long durationUs, int width, /* package */ MediaFormat(String mimeType, int maxInputSize, long durationUs, int width,
int height, int rotationDegrees, float pixelWidthHeightRatio, int channelCount, int height, int rotationDegrees, float pixelWidthHeightRatio, int channelCount,
int sampleRate, String language, long subsampleOffsetUs, List<byte[]> initializationData, int sampleRate, String language, long subsampleOffsetUs, List<byte[]> initializationData,
...@@ -236,6 +227,12 @@ public final class MediaFormat { ...@@ -236,6 +227,12 @@ public final class MediaFormat {
initializationData, adaptive, maxWidth, maxHeight); initializationData, adaptive, maxWidth, maxHeight);
} }
public MediaFormat copyWithAdaptive(boolean adaptive) {
return new MediaFormat(mimeType, maxInputSize, durationUs, width, height, rotationDegrees,
pixelWidthHeightRatio, channelCount, sampleRate, language, subsampleOffsetUs,
initializationData, adaptive, maxWidth, maxHeight);
}
/** /**
* @return A {@link MediaFormat} representation of this format. * @return A {@link MediaFormat} representation of this format.
*/ */
......
...@@ -134,7 +134,9 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load ...@@ -134,7 +134,9 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load
} else if (!chunkSource.prepare()) { } else if (!chunkSource.prepare()) {
return false; return false;
} }
if (chunkSource.getTrackCount() > 0) {
loader = new Loader("Loader:" + chunkSource.getFormat(0).mimeType); loader = new Loader("Loader:" + chunkSource.getFormat(0).mimeType);
}
state = STATE_PREPARED; state = STATE_PREPARED;
return true; return true;
} }
......
...@@ -67,11 +67,21 @@ public interface ChunkSource { ...@@ -67,11 +67,21 @@ public interface ChunkSource {
MediaFormat getFormat(int track); MediaFormat getFormat(int track);
/** /**
* Enable the source for the specified track.
* <p>
* This method should only be called after the source has been prepared, and when the source is
* disabled.
*
* @param track The track index.
*/
void enable(int track);
/**
* Adaptive video {@link ChunkSource} implementations must return a copy of the provided * Adaptive video {@link ChunkSource} implementations must return a copy of the provided
* {@link MediaFormat} with the maximum video dimensions set. Other implementations can return * {@link MediaFormat} with the maximum video dimensions set. Other implementations can return
* the provided {@link MediaFormat} directly. * the provided {@link MediaFormat} directly.
* <p> * <p>
* This method should only be called after the source has been prepared. * This method should only be called when the source is enabled.
* *
* @param format The format to be copied or returned. * @param format The format to be copied or returned.
* @return A copy of the provided {@link MediaFormat} with the maximum video dimensions set, or * @return A copy of the provided {@link MediaFormat} with the maximum video dimensions set, or
...@@ -80,16 +90,6 @@ public interface ChunkSource { ...@@ -80,16 +90,6 @@ public interface ChunkSource {
MediaFormat getWithMaxVideoDimensions(MediaFormat format); MediaFormat getWithMaxVideoDimensions(MediaFormat format);
/** /**
* Enable the source for the specified track.
* <p>
* This method should only be called after the source has been prepared, and when the source is
* disabled.
*
* @param track The track index.
*/
void enable(int track);
/**
* Indicates to the source that it should still be checking for updates to the stream. * Indicates to the source that it should still be checking for updates to the stream.
* <p> * <p>
* This method should only be called when the source is enabled. * This method should only be called when the source is enabled.
......
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer.smoothstreaming;
import java.io.IOException;
/**
* Specifies a track selection from a {@link SmoothStreamingManifest}.
*/
public interface SmoothStreamingTrackSelector {
/**
* Defines a selector output.
*/
interface Output {
/**
* Outputs an adaptive track, covering the specified tracks in the specified element.
*
* @param manifest The manifest being processed.
* @param element The index of the element within which the adaptive tracks are located.
* @param tracks The indices of the tracks within the element.
*/
void adaptiveTrack(SmoothStreamingManifest manifest, int element, int[] tracks);
/**
* Outputs a fixed track corresponding to the specified track in the specified element.
*
* @param manifest The manifest being processed.
* @param element The index of the element within which the adaptive tracks are located.
* @param track The index of the track within the element.
*/
void fixedTrack(SmoothStreamingManifest manifest, int element, int track);
}
/**
* Outputs a track selection for a given manifest.
*
* @param manifest The manifest to process.
* @param output The output to receive tracks.
* @throws IOException If an error occurs processing the manifest.
*/
void selectTracks(SmoothStreamingManifest manifest, Output output) throws IOException;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment