mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Refactored ExoPlayer to use String-based format ids.
This commit is contained in:
parent
563b434de2
commit
4fd4c89518
@ -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) + "]");
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user