Propagate MediaFormat instead of TrackInfo.

Issue #514.
This commit is contained in:
Oliver Woodman 2015-08-11 18:23:22 +01:00
parent f7ffeb75cf
commit 8db1331021
28 changed files with 178 additions and 235 deletions

View File

@ -22,7 +22,6 @@ import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.MediaFormatHolder;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.SampleSourceTrackRenderer;
import com.google.android.exoplayer.TrackInfo;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.audio.AudioTrack;
import com.google.android.exoplayer.ext.opus.OpusDecoderWrapper.InputBuffer;
@ -125,9 +124,10 @@ public final class LibopusAudioTrackRenderer extends SampleSourceTrackRenderer
}
@Override
protected boolean handlesTrack(TrackInfo trackInfo) {
return MimeTypes.AUDIO_OPUS.equalsIgnoreCase(trackInfo.mimeType)
|| MimeTypes.AUDIO_WEBM.equalsIgnoreCase(trackInfo.mimeType);
protected boolean handlesTrack(MediaFormat mediaFormat) {
// TODO: Stop claiming to handle the WebM mime type (b/22996976).
return MimeTypes.AUDIO_OPUS.equalsIgnoreCase(mediaFormat.mimeType)
|| MimeTypes.AUDIO_WEBM.equalsIgnoreCase(mediaFormat.mimeType);
}
@Override

View File

@ -21,7 +21,6 @@ import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.MediaFormatHolder;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.SampleSourceTrackRenderer;
import com.google.android.exoplayer.TrackInfo;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.ext.vp9.VpxDecoderWrapper.InputBuffer;
import com.google.android.exoplayer.ext.vp9.VpxDecoderWrapper.OutputBuffer;
@ -149,9 +148,10 @@ public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
}
@Override
protected boolean handlesTrack(TrackInfo trackInfo) {
return MimeTypes.VIDEO_VP9.equalsIgnoreCase(trackInfo.mimeType)
|| MimeTypes.VIDEO_WEBM.equalsIgnoreCase(trackInfo.mimeType);
protected boolean handlesTrack(MediaFormat mediaFormat) {
// TODO: Stop claiming to handle the WebM mime type (b/22996976).
return MimeTypes.VIDEO_VP9.equalsIgnoreCase(mediaFormat.mimeType)
|| MimeTypes.VIDEO_WEBM.equalsIgnoreCase(mediaFormat.mimeType);
}
@Override

View File

@ -69,8 +69,8 @@ public final class MediaFormatTest extends TestCase {
assertOptionalV16(out, android.media.MediaFormat.KEY_HEIGHT, in.height);
assertOptionalV16(out, android.media.MediaFormat.KEY_CHANNEL_COUNT, in.channelCount);
assertOptionalV16(out, android.media.MediaFormat.KEY_SAMPLE_RATE, in.sampleRate);
assertOptionalV16(out, android.media.MediaFormat.KEY_MAX_WIDTH, in.getMaxVideoWidth());
assertOptionalV16(out, android.media.MediaFormat.KEY_MAX_HEIGHT, in.getMaxVideoHeight());
assertOptionalV16(out, android.media.MediaFormat.KEY_MAX_WIDTH, in.maxWidth);
assertOptionalV16(out, android.media.MediaFormat.KEY_MAX_HEIGHT, in.maxHeight);
for (int i = 0; i < in.initializationData.size(); i++) {
byte[] originalData = in.initializationData.get(i);
ByteBuffer frameworkBuffer = out.getByteBuffer("csd-" + i);

View File

@ -91,11 +91,11 @@ public class DashChunkSourceTest extends InstrumentationTestCase {
public void testMaxVideoDimensions() {
DashChunkSource chunkSource = new DashChunkSource(generateVodMpd(), AdaptationSet.TYPE_VIDEO,
null, null, null);
MediaFormat out = MediaFormat.createVideoFormat("video/h264", 1, 1, 1, 1, null);
chunkSource.getMaxVideoDimensions(out);
MediaFormat format = MediaFormat.createVideoFormat("video/h264", 1, 1, 1, 1, null);
format = chunkSource.getWithMaxVideoDimensions(format);
assertEquals(WIDE_WIDTH, out.getMaxVideoWidth());
assertEquals(TALL_HEIGHT, out.getMaxVideoHeight());
assertEquals(WIDE_WIDTH, format.maxWidth);
assertEquals(TALL_HEIGHT, format.maxHeight);
}
public void testGetSeekRangeOnVod() {
@ -121,11 +121,11 @@ public class DashChunkSourceTest extends InstrumentationTestCase {
Representation.newInstance(0, 0, null, 0, WIDE_VIDEO, segmentBase2);
DashChunkSource chunkSource = new DashChunkSource(null, null, representation1, representation2);
MediaFormat out = MediaFormat.createVideoFormat("video/h264", 1, 1, 1, 1, null);
chunkSource.getMaxVideoDimensions(out);
MediaFormat format = MediaFormat.createVideoFormat("video/h264", 1, 1, 1, 1, null);
format = chunkSource.getWithMaxVideoDimensions(format);
assertEquals(WIDE_WIDTH, out.getMaxVideoWidth());
assertEquals(TALL_HEIGHT, out.getMaxVideoHeight());
assertEquals(WIDE_WIDTH, format.maxWidth);
assertEquals(TALL_HEIGHT, format.maxHeight);
}
public void testLiveEdgeNoLatency() {

View File

@ -35,7 +35,7 @@ public final class DummyTrackRenderer extends TrackRenderer {
}
@Override
protected TrackInfo getTrackInfo(int track) {
protected MediaFormat getFormat(int track) {
throw new IllegalStateException();
}

View File

@ -34,7 +34,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
private final Handler eventHandler;
private final ExoPlayerImplInternal internalPlayer;
private final CopyOnWriteArraySet<Listener> listeners;
private final TrackInfo[][] trackInfos;
private final MediaFormat[][] trackFormats;
private final int[] selectedTrackIndices;
private boolean playWhenReady;
@ -58,7 +58,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
this.playWhenReady = false;
this.playbackState = STATE_IDLE;
this.listeners = new CopyOnWriteArraySet<>();
this.trackInfos = new TrackInfo[rendererCount][];
this.trackFormats = new MediaFormat[rendererCount][];
this.selectedTrackIndices = new int[rendererCount];
eventHandler = new Handler() {
@Override
@ -92,7 +92,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
@Override
public void prepare(TrackRenderer... renderers) {
Arrays.fill(trackInfos, null);
Arrays.fill(trackFormats, null);
internalPlayer.prepare(renderers);
}
@ -116,12 +116,12 @@ import java.util.concurrent.CopyOnWriteArraySet;
// TODO: Expose in ExoPlayer.
public int getRendererTrackCount(int rendererIndex) {
return trackInfos[rendererIndex] != null ? trackInfos[rendererIndex].length : 0;
return trackFormats[rendererIndex] != null ? trackFormats[rendererIndex].length : 0;
}
// TODO: Expose in ExoPlayer.
public TrackInfo getRendererTrackInfo(int rendererIndex, int trackIndex) {
return trackInfos[rendererIndex][trackIndex];
public MediaFormat getRendererTrackInfo(int rendererIndex, int trackIndex) {
return trackFormats[rendererIndex][trackIndex];
}
// TODO: Expose in ExoPlayer.
@ -212,8 +212,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
/* package */ void handleEvent(Message msg) {
switch (msg.what) {
case ExoPlayerImplInternal.MSG_PREPARED: {
TrackInfo[][] trackInfos = (TrackInfo[][]) msg.obj;
System.arraycopy(trackInfos, 0, this.trackInfos, 0, trackInfos.length);
System.arraycopy(msg.obj, 0, trackFormats, 0, trackFormats.length);
playbackState = msg.arg1;
for (Listener listener : listeners) {
listener.onPlayerStateChanged(playWhenReady, playbackState);

View File

@ -67,7 +67,7 @@ import java.util.List;
private final Handler eventHandler;
private final StandaloneMediaClock standaloneMediaClock;
private final List<TrackRenderer> enabledRenderers;
private final TrackInfo[][] trackInfos;
private final MediaFormat[][] trackFormats;
private final int[] selectedTrackIndices;
private final long minBufferUs;
private final long minRebufferUs;
@ -101,7 +101,7 @@ import java.util.List;
standaloneMediaClock = new StandaloneMediaClock();
enabledRenderers = new ArrayList<>(selectedTrackIndices.length);
trackInfos = new TrackInfo[selectedTrackIndices.length][];
trackFormats = new MediaFormat[selectedTrackIndices.length][];
// Note: The documentation for Process.THREAD_PRIORITY_AUDIO that states "Applications can
// not normally change to this priority" is incorrect.
internalPlaybackThread = new PriorityHandlerThread(getClass().getSimpleName() + ":Handler",
@ -253,7 +253,7 @@ import java.util.List;
private void prepareInternal(TrackRenderer[] renderers) throws ExoPlaybackException {
resetInternal();
this.renderers = renderers;
Arrays.fill(trackInfos, null);
Arrays.fill(trackFormats, null);
for (int i = 0; i < renderers.length; i++) {
MediaClock mediaClock = renderers[i].getMediaClock();
if (mediaClock != null) {
@ -292,11 +292,11 @@ import java.util.List;
for (int rendererIndex = 0; rendererIndex < renderers.length; rendererIndex++) {
TrackRenderer renderer = renderers[rendererIndex];
int rendererTrackCount = renderer.getTrackCount();
TrackInfo[] rendererTrackInfos = new TrackInfo[rendererTrackCount];
MediaFormat[] rendererTrackFormats = new MediaFormat[rendererTrackCount];
for (int trackIndex = 0; trackIndex < rendererTrackCount; trackIndex++) {
rendererTrackInfos[trackIndex] = renderer.getTrackInfo(trackIndex);
rendererTrackFormats[trackIndex] = renderer.getFormat(trackIndex);
}
trackInfos[rendererIndex] = rendererTrackInfos;
trackFormats[rendererIndex] = rendererTrackFormats;
if (rendererTrackCount > 0) {
if (durationUs == TrackRenderer.UNKNOWN_TIME_US) {
// We've already encountered a track for which the duration is unknown, so the media
@ -312,7 +312,7 @@ import java.util.List;
}
}
int trackIndex = selectedTrackIndices[rendererIndex];
if (0 <= trackIndex && trackIndex < rendererTrackInfos.length) {
if (0 <= trackIndex && trackIndex < rendererTrackFormats.length) {
renderer.enable(trackIndex, positionUs, false);
enabledRenderers.add(renderer);
allRenderersEnded = allRenderersEnded && renderer.isEnded();
@ -332,7 +332,7 @@ import java.util.List;
// Fire an event indicating that the player has been prepared, passing the initial state and
// renderer track information.
eventHandler.obtainMessage(MSG_PREPARED, state, 0, trackInfos).sendToTarget();
eventHandler.obtainMessage(MSG_PREPARED, state, 0, trackFormats).sendToTarget();
// Start the renderers if required, and schedule the first piece of work.
if (playWhenReady && state == ExoPlayer.STATE_READY) {
@ -610,7 +610,7 @@ import java.util.List;
boolean isEnabled = rendererState == TrackRenderer.STATE_ENABLED
|| rendererState == TrackRenderer.STATE_STARTED;
boolean shouldEnable = 0 <= trackIndex && trackIndex < trackInfos[rendererIndex].length;
boolean shouldEnable = 0 <= trackIndex && trackIndex < trackFormats[rendererIndex].length;
if (isEnabled) {
// The renderer is currently enabled. We need to disable it, so that we can either re-enable

View File

@ -73,7 +73,7 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe
private IOException preparationError;
private MediaExtractor extractor;
private TrackInfo[] trackInfos;
private MediaFormat[] trackFormats;
private boolean prepared;
private int remainingReleaseCount;
private int[] trackStates;
@ -144,13 +144,9 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe
trackStates = new int[extractor.getTrackCount()];
pendingDiscontinuities = new boolean[trackStates.length];
trackInfos = new TrackInfo[trackStates.length];
trackFormats = new MediaFormat[trackStates.length];
for (int i = 0; i < trackStates.length; i++) {
android.media.MediaFormat format = extractor.getTrackFormat(i);
long durationUs = format.containsKey(android.media.MediaFormat.KEY_DURATION)
? format.getLong(android.media.MediaFormat.KEY_DURATION) : C.UNKNOWN_TIME_US;
String mime = format.getString(android.media.MediaFormat.KEY_MIME);
trackInfos[i] = new TrackInfo(mime, durationUs);
trackFormats[i] = createMediaFormat(extractor.getTrackFormat(i));
}
prepared = true;
}
@ -164,9 +160,9 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe
}
@Override
public TrackInfo getTrackInfo(int track) {
public MediaFormat getFormat(int track) {
Assertions.checkState(prepared);
return trackInfos[track];
return trackFormats[track];
}
@Override
@ -200,7 +196,7 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe
return NOTHING_READ;
}
if (trackStates[track] != TRACK_STATE_FORMAT_SENT) {
formatHolder.format = createMediaFormat(extractor.getTrackFormat(track));
formatHolder.format = trackFormats[track];
formatHolder.drmInitData = Util.SDK_INT >= 18 ? getDrmInitDataV18() : null;
trackStates[track] = TRACK_STATE_FORMAT_SENT;
return FORMAT_READ;
@ -319,7 +315,8 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe
long durationUs = format.containsKey(android.media.MediaFormat.KEY_DURATION)
? format.getLong(android.media.MediaFormat.KEY_DURATION) : C.UNKNOWN_TIME_US;
return new MediaFormat(mimeType, maxInputSize, durationUs, width, height, MediaFormat.NO_VALUE,
channelCount, sampleRate, language, initializationData);
channelCount, sampleRate, language, initializationData, MediaFormat.NO_VALUE,
MediaFormat.NO_VALUE);
}
@TargetApi(16)

View File

@ -193,8 +193,9 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
}
@Override
protected boolean handlesTrack(TrackInfo trackInfo) {
return MimeTypes.isAudio(trackInfo.mimeType);
protected boolean handlesTrack(MediaFormat mediaFormat) {
// TODO: Check the mime type against the available decoders (b/22996976).
return MimeTypes.isAudio(mediaFormat.mimeType);
}
@Override

View File

@ -256,8 +256,9 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
}
@Override
protected boolean handlesTrack(TrackInfo trackInfo) {
return MimeTypes.isVideo(trackInfo.mimeType);
protected boolean handlesTrack(MediaFormat mediaFormat) {
// TODO: Check the mime type against the available decoders (b/22996976).
return MimeTypes.isVideo(mediaFormat.mimeType);
}
@Override

View File

@ -35,6 +35,11 @@ public final class MediaFormat {
public final String mimeType;
public final int maxInputSize;
/**
* The duration in microseconds, or {@link C#UNKNOWN_TIME_US} if the duration is unknown, or
* {@link C#MATCH_LONGEST_US} if the duration should match the duration of the longest track whose
* duration is known.
*/
public final long durationUs;
public final int width;
@ -48,8 +53,8 @@ public final class MediaFormat {
public final List<byte[]> initializationData;
private int maxWidth;
private int maxHeight;
public final int maxWidth;
public final int maxHeight;
// Lazy-initialized hashcode.
private int hashCode;
@ -71,7 +76,7 @@ public final class MediaFormat {
public static MediaFormat createVideoFormat(String mimeType, int maxInputSize, long durationUs,
int width, int height, float pixelWidthHeightRatio, List<byte[]> initializationData) {
return new MediaFormat(mimeType, maxInputSize, durationUs, width, height, pixelWidthHeightRatio,
NO_VALUE, NO_VALUE, null, initializationData);
NO_VALUE, NO_VALUE, null, initializationData, NO_VALUE, NO_VALUE);
}
public static MediaFormat createAudioFormat(String mimeType, int maxInputSize, int channelCount,
@ -83,7 +88,7 @@ public final class MediaFormat {
public static MediaFormat createAudioFormat(String mimeType, int maxInputSize, long durationUs,
int channelCount, int sampleRate, List<byte[]> initializationData) {
return new MediaFormat(mimeType, maxInputSize, durationUs, NO_VALUE, NO_VALUE, NO_VALUE,
channelCount, sampleRate, null, initializationData);
channelCount, sampleRate, null, initializationData, NO_VALUE, NO_VALUE);
}
public static MediaFormat createTextFormat(String mimeType, String language) {
@ -92,7 +97,7 @@ public final class MediaFormat {
public static MediaFormat createTextFormat(String mimeType, String language, long durationUs) {
return new MediaFormat(mimeType, NO_VALUE, durationUs, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, language, null);
NO_VALUE, NO_VALUE, language, null, NO_VALUE, NO_VALUE);
}
public static MediaFormat createFormatForMimeType(String mimeType) {
@ -101,12 +106,12 @@ public final class MediaFormat {
public static MediaFormat createFormatForMimeType(String mimeType, long durationUs) {
return new MediaFormat(mimeType, NO_VALUE, durationUs, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, null, null);
NO_VALUE, NO_VALUE, null, null, NO_VALUE, NO_VALUE);
}
/* package */ MediaFormat(String mimeType, int maxInputSize, long durationUs, int width,
int height, float pixelWidthHeightRatio, int channelCount, int sampleRate, String language,
List<byte[]> initializationData) {
List<byte[]> initializationData, int maxWidth, int maxHeight) {
this.mimeType = mimeType;
this.maxInputSize = maxInputSize;
this.durationUs = durationUs;
@ -118,26 +123,13 @@ public final class MediaFormat {
this.language = language;
this.initializationData = initializationData == null ? Collections.<byte[]>emptyList()
: initializationData;
maxWidth = NO_VALUE;
maxHeight = NO_VALUE;
}
@SuppressLint("InlinedApi")
public void setMaxVideoDimensions(int maxWidth, int maxHeight) {
this.maxWidth = maxWidth;
this.maxHeight = maxHeight;
if (frameworkMediaFormat != null) {
maybeSetIntegerV16(frameworkMediaFormat, android.media.MediaFormat.KEY_MAX_WIDTH, maxWidth);
maybeSetIntegerV16(frameworkMediaFormat, android.media.MediaFormat.KEY_MAX_HEIGHT, maxHeight);
}
}
public int getMaxVideoWidth() {
return maxWidth;
}
public int getMaxVideoHeight() {
return maxHeight;
public MediaFormat copyWithMaxVideoDimension(int maxWidth, int maxHeight) {
return new MediaFormat(mimeType, maxInputSize, durationUs, width, height, pixelWidthHeightRatio,
channelCount, sampleRate, language, initializationData, maxWidth, maxHeight);
}
/**

View File

@ -20,9 +20,9 @@ import java.io.IOException;
/**
* A source of media samples.
* <p>
* A {@link SampleSource} may expose one or multiple tracks. The number of tracks and information
* about each can be queried using {@link SampleSourceReader#getTrackCount()} and
* {@link SampleSourceReader#getTrackInfo(int)} respectively.
* A {@link SampleSource} may expose one or multiple tracks. The number of tracks and each track's
* media format can be queried using {@link SampleSourceReader#getTrackCount()} and
* {@link SampleSourceReader#getFormat(int)} respectively.
*/
public interface SampleSource {
@ -87,13 +87,14 @@ public interface SampleSource {
public int getTrackCount();
/**
* Returns information about the specified track.
* Returns the format of the specified track.
* <p>
* This method should not be called until after the source has been successfully prepared.
*
* @return Information about the specified track.
* @param track The track index.
* @return The format of the specified track.
*/
public TrackInfo getTrackInfo(int track);
public MediaFormat getFormat(int track);
/**
* Enable the specified track. This allows the track's format and samples to be read from

View File

@ -30,7 +30,6 @@ public abstract class SampleSourceTrackRenderer extends TrackRenderer {
private int enabledSourceTrackIndex;
private int[] handledSourceTrackIndices;
private TrackInfo[] trackInfos;
/**
* @param source The upstream source from which the renderer obtains samples.
@ -48,27 +47,24 @@ public abstract class SampleSourceTrackRenderer extends TrackRenderer {
int handledTrackCount = 0;
int sourceTrackCount = source.getTrackCount();
int[] trackIndices = new int[sourceTrackCount];
TrackInfo[] trackInfos = new TrackInfo[sourceTrackCount];
for (int trackIndex = 0; trackIndex < sourceTrackCount; trackIndex++) {
TrackInfo trackInfo = source.getTrackInfo(trackIndex);
if (handlesTrack(trackInfo)) {
MediaFormat format = source.getFormat(trackIndex);
if (handlesTrack(format)) {
trackIndices[handledTrackCount] = trackIndex;
trackInfos[handledTrackCount] = trackInfo;
handledTrackCount++;
}
}
this.handledSourceTrackIndices = Arrays.copyOf(trackIndices, handledTrackCount);
this.trackInfos = Arrays.copyOf(trackInfos, handledTrackCount);
return true;
}
/**
* Returns whether this renderer is capable of handling the provided track.
*
* @param trackInfo The track.
* @param mediaFormat The track.
* @return True if the renderer can handle the track, false otherwise.
*/
protected abstract boolean handlesTrack(TrackInfo trackInfo);
protected abstract boolean handlesTrack(MediaFormat mediaFormat);
@Override
protected void onEnabled(int track, long positionUs, boolean joining)
@ -89,7 +85,7 @@ public abstract class SampleSourceTrackRenderer extends TrackRenderer {
@Override
protected long getDurationUs() {
return source.getTrackInfo(enabledSourceTrackIndex).durationUs;
return source.getFormat(enabledSourceTrackIndex).durationUs;
}
@Override
@ -123,12 +119,12 @@ public abstract class SampleSourceTrackRenderer extends TrackRenderer {
@Override
protected final int getTrackCount() {
return trackInfos.length;
return handledSourceTrackIndices.length;
}
@Override
protected final TrackInfo getTrackInfo(int track) {
return trackInfos[track];
protected final MediaFormat getFormat(int track) {
return source.getFormat(handledSourceTrackIndices[track]);
}
}

View File

@ -20,6 +20,7 @@ import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.upstream.Loader;
import com.google.android.exoplayer.upstream.Loader.Loadable;
import com.google.android.exoplayer.util.Assertions;
import android.net.Uri;
import android.os.SystemClock;
@ -43,15 +44,18 @@ public final class SingleSampleSource implements SampleSource, SampleSourceReade
*/
private static final int INITIAL_SAMPLE_SIZE = 1;
private static final int STATE_SEND_FORMAT = 0;
private static final int STATE_SEND_SAMPLE = 1;
private static final int STATE_END_OF_STREAM = 2;
private final Uri uri;
private final DataSource dataSource;
private final MediaFormat format;
private final TrackInfo trackInfo;
private final int minLoadableRetryCount;
private int state;
private byte[] sampleData;
private int sampleSize;
private boolean pendingSample;
private boolean loadingFinished;
private Loader loader;
@ -69,7 +73,6 @@ public final class SingleSampleSource implements SampleSource, SampleSourceReade
this.dataSource = dataSource;
this.format = format;
this.minLoadableRetryCount = minLoadableRetryCount;
trackInfo = new TrackInfo(format.mimeType, format.durationUs);
sampleData = new byte[INITIAL_SAMPLE_SIZE];
}
@ -92,13 +95,13 @@ public final class SingleSampleSource implements SampleSource, SampleSourceReade
}
@Override
public TrackInfo getTrackInfo(int track) {
return trackInfo;
public MediaFormat getFormat(int track) {
return format;
}
@Override
public void enable(int track, long positionUs) {
pendingSample = true;
state = STATE_SEND_FORMAT;
clearCurrentLoadableException();
maybeStartLoading();
}
@ -121,9 +124,16 @@ public final class SingleSampleSource implements SampleSource, SampleSourceReade
SampleHolder sampleHolder, boolean onlyReadDiscontinuity) {
if (onlyReadDiscontinuity) {
return NOTHING_READ;
} else if (!pendingSample) {
} else if (state == STATE_END_OF_STREAM) {
return END_OF_STREAM;
} else if (!loadingFinished) {
} else if (state == STATE_SEND_FORMAT) {
formatHolder.format = format;
state = STATE_SEND_SAMPLE;
return FORMAT_READ;
}
Assertions.checkState(state == STATE_SEND_SAMPLE);
if (!loadingFinished) {
return NOTHING_READ;
} else {
sampleHolder.timeUs = 0;
@ -133,13 +143,16 @@ public final class SingleSampleSource implements SampleSource, SampleSourceReade
sampleHolder.replaceBuffer(sampleHolder.size);
}
sampleHolder.data.put(sampleData, 0, sampleSize);
state = STATE_END_OF_STREAM;
return SAMPLE_READ;
}
}
@Override
public void seekToUs(long positionUs) {
pendingSample = true;
if (state == STATE_END_OF_STREAM) {
state = STATE_SEND_SAMPLE;
}
}
@Override
@ -149,7 +162,7 @@ public final class SingleSampleSource implements SampleSource, SampleSourceReade
@Override
public void disable(int track) {
pendingSample = false;
state = STATE_END_OF_STREAM;
}
@Override
@ -163,7 +176,7 @@ public final class SingleSampleSource implements SampleSource, SampleSourceReade
// Private methods.
private void maybeStartLoading() {
if (loadingFinished || !pendingSample || loader.isLoading()) {
if (loadingFinished || state != STATE_END_OF_STREAM || loader.isLoading()) {
return;
}
if (currentLoadableException != null) {

View File

@ -1,46 +0,0 @@
/*
* 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;
/**
* Holds high level information about a media track.
*/
public final class TrackInfo {
/**
* The mime type.
*/
public final String mimeType;
/**
* The duration in microseconds, or {@link C#UNKNOWN_TIME_US} if the duration is unknown, or
* {@link C#MATCH_LONGEST_US} if the duration should match the duration of the longest track whose
* duration is known.
*/
public final long durationUs;
/**
* @param mimeType The mime type.
* @param durationUs The duration in microseconds, or {@link C#UNKNOWN_TIME_US} if the duration
* is unknown, or {@link C#MATCH_LONGEST_US} if the duration should match the duration of the
* longest track whose duration is known.
*/
public TrackInfo(String mimeType, long durationUs) {
this.mimeType = mimeType;
this.durationUs = durationUs;
}
}

View File

@ -138,17 +138,17 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
}
/**
* Returns information about the specified track.
* Returns the format of the specified track.
* <p>
* This method may be called when the renderer is in the following states:
* {@link #STATE_PREPARED}, {@link #STATE_ENABLED}, {@link #STATE_STARTED}
*
* @param track The track index.
* @return Information about the specified track.
* @return The format of the specified track.
*/
// TODO: This method should be abstract. This implementation is provided as an interim step only.
protected TrackInfo getTrackInfo(int track) {
return new TrackInfo("application/octet-stream", getDurationUs());
protected MediaFormat getFormat(int track) {
return MediaFormat.createFormatForMimeType("application/octet-stream", getDurationUs());
}
/**

View File

@ -22,7 +22,6 @@ import com.google.android.exoplayer.MediaFormatHolder;
import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.SampleSource.SampleSourceReader;
import com.google.android.exoplayer.TrackInfo;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.extractor.DefaultTrackOutput;
import com.google.android.exoplayer.upstream.Loader;
@ -132,7 +131,7 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load
if (state == STATE_PREPARED) {
return true;
}
loader = new Loader("Loader:" + chunkSource.getTrackInfo().mimeType);
loader = new Loader("Loader:" + chunkSource.getFormat().mimeType);
state = STATE_PREPARED;
return true;
}
@ -144,10 +143,10 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load
}
@Override
public TrackInfo getTrackInfo(int track) {
public MediaFormat getFormat(int track) {
Assertions.checkState(state == STATE_PREPARED || state == STATE_ENABLED);
Assertions.checkState(track == 0);
return chunkSource.getTrackInfo();
return chunkSource.getFormat();
}
@Override
@ -232,7 +231,7 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load
if (haveSamples || currentChunk.isMediaFormatFinal) {
MediaFormat mediaFormat = currentChunk.getMediaFormat();
if (!mediaFormat.equals(downstreamMediaFormat, true)) {
chunkSource.getMaxVideoDimensions(mediaFormat);
mediaFormat = chunkSource.getWithMaxVideoDimensions(mediaFormat);
formatHolder.format = mediaFormat;
formatHolder.drmInitData = currentChunk.getDrmInitData();
downstreamMediaFormat = mediaFormat;

View File

@ -16,7 +16,6 @@
package com.google.android.exoplayer.chunk;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.TrackInfo;
import java.io.IOException;
import java.util.List;
@ -32,23 +31,24 @@ import java.util.List;
public interface ChunkSource {
/**
* Gets information about the track for which this instance provides {@link Chunk}s.
* Gets the format.
* <p>
* May be called when the source is disabled or enabled.
*
* @return Information about the track.
* @return The format.
*/
TrackInfo getTrackInfo();
MediaFormat getFormat();
/**
* Adaptive video {@link ChunkSource} implementations must set the maximum video dimensions on
* the supplied {@link MediaFormat}. Other implementations do nothing.
* <p>
* Only called when the source is enabled.
* Adaptive video {@link ChunkSource} implementations must return a copy of the provided
* {@link MediaFormat} with the maximum video dimensions set. Other implementations can return
* the provided {@link MediaFormat} directly.
*
* @param out The {@link MediaFormat} on which the maximum video dimensions should be set.
* @param format The format to be copied or returned.
* @return A copy of the provided {@link MediaFormat} with the maximum video dimensions set, or
* the provided format.
*/
void getMaxVideoDimensions(MediaFormat out);
MediaFormat getWithMaxVideoDimensions(MediaFormat format);
/**
* Called when the source is enabled.

View File

@ -18,7 +18,6 @@ package com.google.android.exoplayer.chunk;
import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.ExoPlayer.ExoPlayerComponent;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.TrackInfo;
import com.google.android.exoplayer.util.Assertions;
import java.io.IOException;
@ -61,8 +60,8 @@ public final class MultiTrackChunkSource implements ChunkSource, ExoPlayerCompon
}
@Override
public TrackInfo getTrackInfo() {
return selectedSource.getTrackInfo();
public MediaFormat getFormat() {
return selectedSource.getFormat();
}
@Override
@ -94,8 +93,8 @@ public final class MultiTrackChunkSource implements ChunkSource, ExoPlayerCompon
}
@Override
public void getMaxVideoDimensions(MediaFormat out) {
selectedSource.getMaxVideoDimensions(out);
public MediaFormat getWithMaxVideoDimensions(MediaFormat format) {
return selectedSource.getWithMaxVideoDimensions(format);
}
@Override

View File

@ -17,7 +17,6 @@ package com.google.android.exoplayer.chunk;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.TrackInfo;
import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSpec;
@ -36,7 +35,6 @@ public final class SingleSampleChunkSource implements ChunkSource {
private final Format format;
private final long durationUs;
private final MediaFormat mediaFormat;
private final TrackInfo trackInfo;
/**
* @param dataSource A {@link DataSource} suitable for loading the sample data.
@ -54,17 +52,16 @@ public final class SingleSampleChunkSource implements ChunkSource {
this.format = format;
this.durationUs = durationUs;
this.mediaFormat = mediaFormat;
trackInfo = new TrackInfo(format.mimeType, durationUs);
}
@Override
public TrackInfo getTrackInfo() {
return trackInfo;
public MediaFormat getFormat() {
return mediaFormat;
}
@Override
public void getMaxVideoDimensions(MediaFormat out) {
// Do nothing.
public MediaFormat getWithMaxVideoDimensions(MediaFormat format) {
return format;
}
@Override

View File

@ -18,7 +18,6 @@ package com.google.android.exoplayer.dash;
import com.google.android.exoplayer.BehindLiveWindowException;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.TimeRange;
import com.google.android.exoplayer.TrackInfo;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.chunk.Chunk;
import com.google.android.exoplayer.chunk.ChunkExtractorWrapper;
@ -98,7 +97,7 @@ public class DashChunkSource implements ChunkSource {
private final Handler eventHandler;
private final EventListener eventListener;
private final TrackInfo trackInfo;
private final MediaFormat mediaFormat;
private final DataSource dataSource;
private final FormatEvaluator formatEvaluator;
private final Evaluation evaluation;
@ -263,7 +262,9 @@ public class DashChunkSource implements ChunkSource {
adaptationSetIndex, representationIndices);
long periodDurationUs = (representations[0].periodDurationMs == TrackRenderer.UNKNOWN_TIME_US)
? TrackRenderer.UNKNOWN_TIME_US : representations[0].periodDurationMs * 1000;
this.trackInfo = new TrackInfo(representations[0].format.mimeType, periodDurationUs);
// TODO: Remove this and pass proper formats instead (b/22996976).
this.mediaFormat = MediaFormat.createFormatForMimeType(representations[0].format.mimeType,
periodDurationUs);
this.formats = new Format[representations.length];
this.representationHolders = new HashMap<>();
@ -284,15 +285,14 @@ public class DashChunkSource implements ChunkSource {
}
@Override
public final void getMaxVideoDimensions(MediaFormat out) {
if (trackInfo.mimeType.startsWith("video")) {
out.setMaxVideoDimensions(maxWidth, maxHeight);
}
public final MediaFormat getWithMaxVideoDimensions(MediaFormat format) {
return MimeTypes.isVideo(mediaFormat.mimeType)
? format.copyWithMaxVideoDimension(maxWidth, maxHeight) : format;
}
@Override
public final TrackInfo getTrackInfo() {
return trackInfo;
public final MediaFormat getFormat() {
return mediaFormat;
}
// VisibleForTesting

View File

@ -22,7 +22,6 @@ import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.SampleSource.SampleSourceReader;
import com.google.android.exoplayer.TrackInfo;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.drm.DrmInitData;
import com.google.android.exoplayer.upstream.Allocator;
@ -163,7 +162,7 @@ public final class ExtractorSampleSource implements SampleSource, SampleSourceRe
private boolean prepared;
private int enabledTrackCount;
private TrackInfo[] trackInfos;
private MediaFormat[] mediaFormats;
private long maxTrackDurationUs;
private boolean[] pendingMediaFormat;
private boolean[] pendingDiscontinuities;
@ -292,11 +291,11 @@ public final class ExtractorSampleSource implements SampleSource, SampleSourceRe
trackEnabledStates = new boolean[trackCount];
pendingDiscontinuities = new boolean[trackCount];
pendingMediaFormat = new boolean[trackCount];
trackInfos = new TrackInfo[trackCount];
mediaFormats = new MediaFormat[trackCount];
maxTrackDurationUs = C.UNKNOWN_TIME_US;
for (int i = 0; i < trackCount; i++) {
MediaFormat format = sampleQueues.valueAt(i).getFormat();
trackInfos[i] = new TrackInfo(format.mimeType, format.durationUs);
mediaFormats[i] = format;
if (format.durationUs != C.UNKNOWN_TIME_US && format.durationUs > maxTrackDurationUs) {
maxTrackDurationUs = format.durationUs;
}
@ -314,9 +313,9 @@ public final class ExtractorSampleSource implements SampleSource, SampleSourceRe
}
@Override
public TrackInfo getTrackInfo(int track) {
public MediaFormat getFormat(int track) {
Assertions.checkState(prepared);
return trackInfos[track];
return mediaFormats[track];
}
@Override

View File

@ -224,19 +224,17 @@ public class HlsChunkSource {
}
/**
* Adaptive implementations must set the maximum video dimensions on the supplied
* {@link MediaFormat}. Other implementations do nothing.
* <p>
* Only called when the source is enabled.
* Adaptive implementations must return a copy of the provided {@link MediaFormat} with the
* maximum video dimensions set. Other implementations can return the provided {@link MediaFormat}
* directly.
*
* @param out The {@link MediaFormat} on which the maximum video dimensions should be set.
* @param format The format to be copied or returned.
* @return A copy of the provided {@link MediaFormat} with the maximum video dimensions set, or
* the provided format.
*/
public void getMaxVideoDimensions(MediaFormat out) {
if (maxWidth == -1 || maxHeight == -1) {
// Not adaptive.
return;
}
out.setMaxVideoDimensions(maxWidth, maxHeight);
public MediaFormat getMaxVideoDimensions(MediaFormat format) {
return (maxWidth == -1 || maxHeight == -1) ? format
: format.copyWithMaxVideoDimension(maxWidth, maxHeight);
}
/**

View File

@ -22,7 +22,6 @@ import com.google.android.exoplayer.MediaFormatHolder;
import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.SampleSource.SampleSourceReader;
import com.google.android.exoplayer.TrackInfo;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.chunk.BaseChunkSampleSourceEventListener;
import com.google.android.exoplayer.chunk.Chunk;
@ -71,7 +70,7 @@ public final class HlsSampleSource implements SampleSource, SampleSourceReader,
private int enabledTrackCount;
private boolean[] trackEnabledStates;
private boolean[] pendingDiscontinuities;
private TrackInfo[] trackInfos;
private MediaFormat[] mediaFormats;
private MediaFormat[] downstreamMediaFormats;
private Format downstreamFormat;
@ -135,10 +134,9 @@ public final class HlsSampleSource implements SampleSource, SampleSourceReader,
trackEnabledStates = new boolean[trackCount];
pendingDiscontinuities = new boolean[trackCount];
downstreamMediaFormats = new MediaFormat[trackCount];
trackInfos = new TrackInfo[trackCount];
mediaFormats = new MediaFormat[trackCount];
for (int i = 0; i < trackCount; i++) {
MediaFormat format = extractor.getMediaFormat(i);
trackInfos[i] = new TrackInfo(format.mimeType, chunkSource.getDurationUs());
mediaFormats[i] = extractor.getMediaFormat(i);
}
prepared = true;
return true;
@ -170,9 +168,9 @@ public final class HlsSampleSource implements SampleSource, SampleSourceReader,
}
@Override
public TrackInfo getTrackInfo(int track) {
public MediaFormat getFormat(int track) {
Assertions.checkState(prepared);
return trackInfos[track];
return mediaFormats[track];
}
@Override

View File

@ -16,11 +16,11 @@
package com.google.android.exoplayer.metadata;
import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.MediaFormatHolder;
import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.SampleSourceTrackRenderer;
import com.google.android.exoplayer.TrackInfo;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.util.Assertions;
@ -88,8 +88,8 @@ public final class MetadataTrackRenderer<T> extends SampleSourceTrackRenderer im
}
@Override
protected boolean handlesTrack(TrackInfo trackInfo) {
return metadataParser.canParse(trackInfo.mimeType);
protected boolean handlesTrack(MediaFormat mediaFormat) {
return metadataParser.canParse(mediaFormat.mimeType);
}
@Override

View File

@ -17,7 +17,6 @@ package com.google.android.exoplayer.smoothstreaming;
import com.google.android.exoplayer.BehindLiveWindowException;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.TrackInfo;
import com.google.android.exoplayer.chunk.Chunk;
import com.google.android.exoplayer.chunk.ChunkExtractorWrapper;
import com.google.android.exoplayer.chunk.ChunkOperationHolder;
@ -59,7 +58,7 @@ public class SmoothStreamingChunkSource implements ChunkSource {
private static final int MINIMUM_MANIFEST_REFRESH_PERIOD_MS = 5000;
private static final int INITIALIZATION_VECTOR_SIZE = 8;
private final TrackInfo trackInfo;
private final MediaFormat mediaFormat;
private final DataSource dataSource;
private final FormatEvaluator formatEvaluator;
private final Evaluation evaluation;
@ -135,7 +134,9 @@ public class SmoothStreamingChunkSource implements ChunkSource {
this.liveEdgeLatencyUs = liveEdgeLatencyMs * 1000;
StreamElement streamElement = getElement(initialManifest);
trackInfo = new TrackInfo(streamElement.tracks[0].format.mimeType, initialManifest.durationUs);
// TODO: Remove this and pass proper formats instead (b/22996976).
mediaFormat = MediaFormat.createFormatForMimeType(streamElement.tracks[0].format.mimeType,
initialManifest.durationUs);
evaluation = new Evaluation();
TrackEncryptionBox[] trackEncryptionBoxes = null;
@ -180,15 +181,14 @@ public class SmoothStreamingChunkSource implements ChunkSource {
}
@Override
public final void getMaxVideoDimensions(MediaFormat out) {
if (trackInfo.mimeType.startsWith("video")) {
out.setMaxVideoDimensions(maxWidth, maxHeight);
}
public final MediaFormat getWithMaxVideoDimensions(MediaFormat format) {
return MimeTypes.isVideo(mediaFormat.mimeType)
? format.copyWithMaxVideoDimension(maxWidth, maxHeight) : format;
}
@Override
public final TrackInfo getTrackInfo() {
return trackInfo;
public final MediaFormat getFormat() {
return mediaFormat;
}
@Override
@ -384,7 +384,6 @@ public class SmoothStreamingChunkSource implements ChunkSource {
if (streamElement.type == StreamElement.TYPE_VIDEO) {
MediaFormat format = MediaFormat.createVideoFormat(mimeType, MediaFormat.NO_VALUE,
trackFormat.width, trackFormat.height, Arrays.asList(trackElement.csd));
format.setMaxVideoDimensions(streamElement.maxWidth, streamElement.maxHeight);
return format;
} else if (streamElement.type == StreamElement.TYPE_AUDIO) {
List<byte[]> csd;

View File

@ -16,11 +16,11 @@
package com.google.android.exoplayer.text;
import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.MediaFormatHolder;
import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.SampleSourceTrackRenderer;
import com.google.android.exoplayer.TrackInfo;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.util.Assertions;
@ -150,15 +150,15 @@ public final class TextTrackRenderer extends SampleSourceTrackRenderer implement
}
@Override
protected boolean handlesTrack(TrackInfo trackInfo) {
return getParserIndex(trackInfo) != -1;
protected boolean handlesTrack(MediaFormat mediaFormat) {
return getParserIndex(mediaFormat) != -1;
}
@Override
protected void onEnabled(int track, long positionUs, boolean joining)
throws ExoPlaybackException {
super.onEnabled(track, positionUs, joining);
parserIndex = getParserIndex(getTrackInfo(track));
parserIndex = getParserIndex(getFormat(track));
parserThread = new HandlerThread("textParser");
parserThread.start();
parserHelper = new SubtitleParserHelper(parserThread.getLooper(), subtitleParsers[parserIndex]);
@ -296,9 +296,9 @@ public final class TextTrackRenderer extends SampleSourceTrackRenderer implement
textRenderer.onCues(cues);
}
private int getParserIndex(TrackInfo trackInfo) {
private int getParserIndex(MediaFormat mediaFormat) {
for (int i = 0; i < subtitleParsers.length; i++) {
if (subtitleParsers[i].canParse(trackInfo.mimeType)) {
if (subtitleParsers[i].canParse(mediaFormat.mimeType)) {
return i;
}
}

View File

@ -17,11 +17,11 @@ package com.google.android.exoplayer.text.eia608;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.MediaFormatHolder;
import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.SampleSourceTrackRenderer;
import com.google.android.exoplayer.TrackInfo;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.text.Cue;
import com.google.android.exoplayer.text.TextRenderer;
@ -89,8 +89,8 @@ public final class Eia608TrackRenderer extends SampleSourceTrackRenderer impleme
}
@Override
protected boolean handlesTrack(TrackInfo trackInfo) {
return eia608Parser.canParse(trackInfo.mimeType);
protected boolean handlesTrack(MediaFormat mediaFormat) {
return eia608Parser.canParse(mediaFormat.mimeType);
}
@Override