Refactored ExoPlayer to use String-based format ids.

This commit is contained in:
Oliver Woodman 2014-07-04 01:04:10 +01:00
parent 563b434de2
commit 4fd4c89518
10 changed files with 73 additions and 52 deletions

View File

@ -91,7 +91,7 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener
}
@Override
public void onLoadStarted(int sourceId, int formatId, int trigger, boolean isInitialization,
public void onLoadStarted(int sourceId, String formatId, int trigger, boolean isInitialization,
int mediaStartTimeMs, int mediaEndTimeMs, long totalBytes) {
loadStartTimeMs[sourceId] = SystemClock.elapsedRealtime();
if (VerboseLogUtil.isTagEnabled(TAG)) {
@ -110,13 +110,13 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener
}
@Override
public void onVideoFormatEnabled(int formatId, int trigger, int mediaTimeMs) {
public void onVideoFormatEnabled(String formatId, int trigger, int mediaTimeMs) {
Log.d(TAG, "videoFormat [" + getSessionTimeString() + ", " + formatId + ", " +
Integer.toString(trigger) + "]");
}
@Override
public void onAudioFormatEnabled(int formatId, int trigger, int mediaTimeMs) {
public void onAudioFormatEnabled(String formatId, int trigger, int mediaTimeMs) {
Log.d(TAG, "audioFormat [" + getSessionTimeString() + ", " + formatId + ", " +
Integer.toString(trigger) + "]");
}

View File

@ -118,11 +118,11 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
* A listener for debugging information.
*/
public interface InfoListener {
void onVideoFormatEnabled(int formatId, int trigger, int mediaTimeMs);
void onAudioFormatEnabled(int formatId, int trigger, int mediaTimeMs);
void onVideoFormatEnabled(String formatId, int trigger, int mediaTimeMs);
void onAudioFormatEnabled(String formatId, int trigger, int mediaTimeMs);
void onDroppedFrames(int count, long elapsed);
void onBandwidthSample(int elapsedMs, long bytes, long bandwidthEstimate);
void onLoadStarted(int sourceId, int formatId, int trigger, boolean isInitialization,
void onLoadStarted(int sourceId, String formatId, int trigger, boolean isInitialization,
int mediaStartTimeMs, int mediaEndTimeMs, long totalBytes);
void onLoadCompleted(int sourceId);
}
@ -398,7 +398,8 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
}
@Override
public void onDownstreamFormatChanged(int sourceId, int formatId, int trigger, int mediaTimeMs) {
public void onDownstreamFormatChanged(int sourceId, String formatId, int trigger,
int mediaTimeMs) {
if (infoListener == null) {
return;
}
@ -469,7 +470,7 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
}
@Override
public void onLoadStarted(int sourceId, int formatId, int trigger, boolean isInitialization,
public void onLoadStarted(int sourceId, String formatId, int trigger, boolean isInitialization,
int mediaStartTimeMs, int mediaEndTimeMs, long totalBytes) {
if (infoListener != null) {
infoListener.onLoadStarted(sourceId, formatId, trigger, isInitialization, mediaStartTimeMs,

View File

@ -59,7 +59,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Listener {
* load is for initialization data.
* @param totalBytes The length of the data being loaded in bytes.
*/
void onLoadStarted(int sourceId, int formatId, int trigger, boolean isInitialization,
void onLoadStarted(int sourceId, String formatId, int trigger, boolean isInitialization,
int mediaStartTimeMs, int mediaEndTimeMs, long totalBytes);
/**
@ -126,7 +126,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Listener {
* {@link ChunkSource}.
* @param mediaTimeMs The media time at which the change occurred.
*/
void onDownstreamFormatChanged(int sourceId, int formatId, int trigger, int mediaTimeMs);
void onDownstreamFormatChanged(int sourceId, String formatId, int trigger, int mediaTimeMs);
}
@ -295,7 +295,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Listener {
}
return NOTHING_READ;
}
} else if (downstreamFormat == null || downstreamFormat.id != mediaChunk.format.id) {
} else if (downstreamFormat == null || !downstreamFormat.id.equals(mediaChunk.format.id)) {
notifyDownstreamFormatChanged(mediaChunk.format.id, mediaChunk.trigger,
mediaChunk.startTimeUs);
MediaFormat format = mediaChunk.getMediaFormat();
@ -653,7 +653,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Listener {
return (int) (timeUs / 1000);
}
private void notifyLoadStarted(final int formatId, final int trigger,
private void notifyLoadStarted(final String formatId, final int trigger,
final boolean isInitialization, final long mediaStartTimeUs, final long mediaEndTimeUs,
final long totalBytes) {
if (eventHandler != null && eventListener != null) {
@ -724,7 +724,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Listener {
}
}
private void notifyDownstreamFormatChanged(final int formatId, final int trigger,
private void notifyDownstreamFormatChanged(final String formatId, final int trigger,
final long mediaTimeUs) {
if (eventHandler != null && eventListener != null) {
eventHandler.post(new Runnable() {

View File

@ -20,7 +20,7 @@ import java.util.Comparator;
/**
* A format definition for streams.
*/
public final class Format {
public class Format {
/**
* Sorts {@link Format} objects in order of decreasing bandwidth.
@ -37,7 +37,7 @@ public final class Format {
/**
* An identifier for the format.
*/
public final int id;
public final String id;
/**
* The mime type of the format.
@ -70,6 +70,8 @@ public final class Format {
public final int bandwidth;
/**
* @deprecated Format identifiers are now strings.
*
* @param id The format identifier.
* @param mimeType The format mime type.
* @param width The width of the video in pixels, or -1 for non-video formats.
@ -78,8 +80,23 @@ public final class Format {
* @param audioSamplingRate The audio sampling rate in Hz, or -1 for non-audio formats.
* @param bandwidth The average bandwidth of the format in bytes per second.
*/
@Deprecated
public Format(int id, String mimeType, int width, int height, int numChannels,
int audioSamplingRate, int bandwidth) {
this(String.valueOf(id), mimeType, width, height, numChannels, audioSamplingRate, bandwidth);
}
/**
* @param id The format identifier.
* @param mimeType The format mime type.
* @param width The width of the video in pixels, or -1 for non-video formats.
* @param height The height of the video in pixels, or -1 for non-video formats.
* @param numChannels The number of audio channels, or -1 for non-audio formats.
* @param audioSamplingRate The audio sampling rate in Hz, or -1 for non-audio formats.
* @param bandwidth The average bandwidth of the format in bytes per second.
*/
public Format(String id, String mimeType, int width, int height, int numChannels,
int audioSamplingRate, int bandwidth) {
this.id = id;
this.mimeType = mimeType;
this.width = width;

View File

@ -146,7 +146,7 @@ public interface FormatEvaluator {
public void evaluate(List<? extends MediaChunk> queue, long playbackPositionUs,
Format[] formats, Evaluation evaluation) {
Format newFormat = formats[random.nextInt(formats.length)];
if (evaluation.format != null && evaluation.format.id != newFormat.id) {
if (evaluation.format != null && !evaluation.format.id.equals(newFormat.id)) {
evaluation.trigger = TRIGGER_ADAPTIVE;
}
evaluation.format = newFormat;

View File

@ -35,10 +35,10 @@ import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.upstream.NonBlockingInputStream;
import android.util.Log;
import android.util.SparseArray;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
/**
@ -64,8 +64,8 @@ public class DashMp4ChunkSource implements ChunkSource {
private final int numSegmentsPerChunk;
private final Format[] formats;
private final SparseArray<Representation> representations;
private final SparseArray<FragmentedMp4Extractor> extractors;
private final HashMap<String, Representation> representations;
private final HashMap<String, FragmentedMp4Extractor> extractors;
private boolean lastChunkWasInitialization;
@ -92,8 +92,8 @@ public class DashMp4ChunkSource implements ChunkSource {
this.evaluator = evaluator;
this.numSegmentsPerChunk = numSegmentsPerChunk;
this.formats = new Format[representations.length];
this.extractors = new SparseArray<FragmentedMp4Extractor>();
this.representations = new SparseArray<Representation>();
this.extractors = new HashMap<String, FragmentedMp4Extractor>();
this.representations = new HashMap<String, Representation>();
this.trackInfo = new TrackInfo(representations[0].format.mimeType,
representations[0].periodDuration * 1000);
this.evaluation = new Evaluation();
@ -103,7 +103,7 @@ public class DashMp4ChunkSource implements ChunkSource {
formats[i] = representations[i].format;
maxWidth = Math.max(formats[i].width, maxWidth);
maxHeight = Math.max(formats[i].height, maxHeight);
extractors.append(formats[i].id, new FragmentedMp4Extractor());
extractors.put(formats[i].id, new FragmentedMp4Extractor());
this.representations.put(formats[i].id, representations[i]);
}
this.maxWidth = maxWidth;
@ -152,7 +152,7 @@ public class DashMp4ChunkSource implements ChunkSource {
out.chunk = null;
return;
} else if (out.queueSize == queue.size() && out.chunk != null
&& out.chunk.format.id == selectedFormat.id) {
&& out.chunk.format.id.equals(selectedFormat.id)) {
// We already have a chunk, and the evaluation hasn't changed either the format or the size
// of the queue. Leave unchanged.
return;

View File

@ -35,10 +35,10 @@ import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.upstream.NonBlockingInputStream;
import android.util.Log;
import android.util.SparseArray;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
/**
@ -57,8 +57,8 @@ public class DashWebmChunkSource implements ChunkSource {
private final int numSegmentsPerChunk;
private final Format[] formats;
private final SparseArray<Representation> representations;
private final SparseArray<WebmExtractor> extractors;
private final HashMap<String, Representation> representations;
private final HashMap<String, WebmExtractor> extractors;
private boolean lastChunkWasInitialization;
@ -73,8 +73,8 @@ public class DashWebmChunkSource implements ChunkSource {
this.evaluator = evaluator;
this.numSegmentsPerChunk = numSegmentsPerChunk;
this.formats = new Format[representations.length];
this.extractors = new SparseArray<WebmExtractor>();
this.representations = new SparseArray<Representation>();
this.extractors = new HashMap<String, WebmExtractor>();
this.representations = new HashMap<String, Representation>();
this.trackInfo = new TrackInfo(
representations[0].format.mimeType, representations[0].periodDuration * 1000);
this.evaluation = new Evaluation();
@ -84,7 +84,7 @@ public class DashWebmChunkSource implements ChunkSource {
formats[i] = representations[i].format;
maxWidth = Math.max(formats[i].width, maxWidth);
maxHeight = Math.max(formats[i].height, maxHeight);
extractors.append(formats[i].id, new WebmExtractor());
extractors.put(formats[i].id, new WebmExtractor());
this.representations.put(formats[i].id, representations[i]);
}
this.maxWidth = maxWidth;
@ -133,7 +133,7 @@ public class DashWebmChunkSource implements ChunkSource {
out.chunk = null;
return;
} else if (out.queueSize == queue.size() && out.chunk != null
&& out.chunk.format.id == selectedFormat.id) {
&& out.chunk.format.id.equals(selectedFormat.id)) {
// We already have a chunk, and the evaluation hasn't changed either the format or the size
// of the queue. Leave unchanged.
return;

View File

@ -21,7 +21,6 @@ import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.util.MimeTypes;
import android.net.Uri;
import android.util.Log;
import org.xml.sax.helpers.DefaultHandler;
import org.xmlpull.v1.XmlPullParser;
@ -45,8 +44,6 @@ import java.util.regex.Pattern;
*/
public class MediaPresentationDescriptionParser extends DefaultHandler {
private static final String TAG = "MediaPresentationDescriptionParser";
// Note: Does not support the date part of ISO 8601
private static final Pattern DURATION =
Pattern.compile("^PT(([0-9]*)H)?(([0-9]*)M)?(([0-9.]*)S)?$");
@ -214,14 +211,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler {
private Representation parseRepresentation(XmlPullParser xpp, String contentId, long periodStart,
long periodDuration, String parentMimeType, List<Segment.Timeline> segmentTimelineList)
throws XmlPullParserException, IOException {
int id;
try {
id = parseInt(xpp, "id");
} catch (NumberFormatException nfe) {
Log.d(TAG, "Unable to parse id; " + nfe.getMessage());
// TODO: need a way to generate a unique and stable id; use hashCode for now
id = xpp.getAttributeValue(null, "id").hashCode();
}
String id = xpp.getAttributeValue(null, "id");
int bandwidth = parseInt(xpp, "bandwidth") / 8;
int audioSamplingRate = parseInt(xpp, "audioSamplingRate");
int width = parseInt(xpp, "width");

View File

@ -81,7 +81,7 @@ public class Representation {
/**
* Generates a cache key for the {@link Representation}, in the format
* {@link #contentId}.{@link #format.id}.{@link #revisionId}.
* {@code contentId + "." + format.id + "." + revisionId}.
*
* @return A cache key.
*/

View File

@ -63,7 +63,7 @@ public class SmoothStreamingChunkSource implements ChunkSource {
private final int maxHeight;
private final SparseArray<FragmentedMp4Extractor> extractors;
private final Format[] formats;
private final SmoothStreamingFormat[] formats;
/**
* @param baseUrl The base URL for the streams.
@ -94,16 +94,16 @@ public class SmoothStreamingChunkSource implements ChunkSource {
}
int trackCount = trackIndices != null ? trackIndices.length : streamElement.tracks.length;
formats = new Format[trackCount];
formats = new SmoothStreamingFormat[trackCount];
extractors = new SparseArray<FragmentedMp4Extractor>();
int maxWidth = 0;
int maxHeight = 0;
for (int i = 0; i < trackCount; i++) {
int trackIndex = trackIndices != null ? trackIndices[i] : i;
TrackElement trackElement = streamElement.tracks[trackIndex];
formats[i] = new Format(trackIndex, trackElement.mimeType, trackElement.maxWidth,
trackElement.maxHeight, trackElement.numChannels, trackElement.sampleRate,
trackElement.bitrate / 8);
formats[i] = new SmoothStreamingFormat(String.valueOf(trackIndex), trackElement.mimeType,
trackElement.maxWidth, trackElement.maxHeight, trackElement.numChannels,
trackElement.sampleRate, trackElement.bitrate / 8, trackIndex);
maxWidth = Math.max(maxWidth, trackElement.maxWidth);
maxHeight = Math.max(maxHeight, trackElement.maxHeight);
@ -155,14 +155,14 @@ public class SmoothStreamingChunkSource implements ChunkSource {
long playbackPositionUs, ChunkOperationHolder out) {
evaluation.queueSize = queue.size();
formatEvaluator.evaluate(queue, playbackPositionUs, formats, evaluation);
Format selectedFormat = evaluation.format;
SmoothStreamingFormat selectedFormat = (SmoothStreamingFormat) evaluation.format;
out.queueSize = evaluation.queueSize;
if (selectedFormat == null) {
out.chunk = null;
return;
} else if (out.queueSize == queue.size() && out.chunk != null
&& out.chunk.format.id == evaluation.format.id) {
&& out.chunk.format.id.equals(evaluation.format.id)) {
// We already have a chunk, and the evaluation hasn't changed either the format or the size
// of the queue. Do nothing.
return;
@ -181,11 +181,12 @@ public class SmoothStreamingChunkSource implements ChunkSource {
}
boolean isLastChunk = nextChunkIndex == streamElement.chunkCount - 1;
String requestUrl = streamElement.buildRequestUrl(selectedFormat.id, nextChunkIndex);
String requestUrl = streamElement.buildRequestUrl(selectedFormat.trackIndex,
nextChunkIndex);
Uri uri = Uri.parse(baseUrl + '/' + requestUrl);
Chunk mediaChunk = newMediaChunk(selectedFormat, uri, null,
extractors.get(selectedFormat.id), dataSource, nextChunkIndex, isLastChunk,
streamElement.getStartTimeUs(nextChunkIndex),
extractors.get(Integer.parseInt(selectedFormat.id)), dataSource, nextChunkIndex,
isLastChunk, streamElement.getStartTimeUs(nextChunkIndex),
isLastChunk ? -1 : streamElement.getStartTimeUs(nextChunkIndex + 1), 0);
out.chunk = mediaChunk;
}
@ -254,4 +255,16 @@ public class SmoothStreamingChunkSource implements ChunkSource {
data[secondPosition] = temp;
}
private static final class SmoothStreamingFormat extends Format {
public final int trackIndex;
public SmoothStreamingFormat(String id, String mimeType, int width, int height,
int numChannels, int audioSamplingRate, int bandwidth, int trackIndex) {
super(id, mimeType, width, height, numChannels, audioSamplingRate, bandwidth);
this.trackIndex = trackIndex;
}
}
}