Common base class for SampleSource based TrackRenderers.

This will allow multi-track support when consuming from a SampleSource to
be added in a single class, rather than in 6. Prep for Issue #514.
This commit is contained in:
Oliver Woodman 2015-08-11 18:04:00 +01:00
parent 150b3cdb19
commit 13f4a3e3bd
13 changed files with 291 additions and 383 deletions

View File

@ -21,7 +21,8 @@ import com.google.android.exoplayer.MediaClock;
import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.MediaFormatHolder; import com.google.android.exoplayer.MediaFormatHolder;
import com.google.android.exoplayer.SampleSource; import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.SampleSource.SampleSourceReader; import com.google.android.exoplayer.SampleSourceTrackRenderer;
import com.google.android.exoplayer.TrackInfo;
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.ext.opus.OpusDecoderWrapper.InputBuffer; import com.google.android.exoplayer.ext.opus.OpusDecoderWrapper.InputBuffer;
@ -30,7 +31,6 @@ import com.google.android.exoplayer.util.MimeTypes;
import android.os.Handler; import android.os.Handler;
import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.List; import java.util.List;
@ -39,7 +39,8 @@ import java.util.List;
* *
* @author vigneshv@google.com (Vignesh Venkatasubramanian) * @author vigneshv@google.com (Vignesh Venkatasubramanian)
*/ */
public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClock { public final class LibopusAudioTrackRenderer extends SampleSourceTrackRenderer
implements MediaClock {
/** /**
* Interface definition for a callback to be notified of {@link LibopusAudioTrackRenderer} events. * Interface definition for a callback to be notified of {@link LibopusAudioTrackRenderer} events.
@ -76,7 +77,6 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
*/ */
public static final int MSG_SET_VOLUME = 1; public static final int MSG_SET_VOLUME = 1;
private final SampleSourceReader source;
private final Handler eventHandler; private final Handler eventHandler;
private final EventListener eventListener; private final EventListener eventListener;
private final MediaFormatHolder formatHolder; private final MediaFormatHolder formatHolder;
@ -86,7 +86,6 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
private InputBuffer inputBuffer; private InputBuffer inputBuffer;
private OutputBuffer outputBuffer; private OutputBuffer outputBuffer;
private int trackIndex;
private long currentPositionUs; private long currentPositionUs;
private boolean allowPositionDiscontinuity; private boolean allowPositionDiscontinuity;
private boolean inputStreamEnded; private boolean inputStreamEnded;
@ -112,7 +111,7 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
*/ */
public LibopusAudioTrackRenderer(SampleSource source, Handler eventHandler, public LibopusAudioTrackRenderer(SampleSource source, Handler eventHandler,
EventListener eventListener) { EventListener eventListener) {
this.source = source.register(); super(source);
this.eventHandler = eventHandler; this.eventHandler = eventHandler;
this.eventListener = eventListener; this.eventListener = eventListener;
this.audioSessionId = AudioTrack.SESSION_ID_NOT_SET; this.audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
@ -126,20 +125,15 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
} }
@Override @Override
protected int doPrepare(long positionUs) { protected boolean handlesTrack(TrackInfo trackInfo) {
boolean sourcePrepared = source.prepare(positionUs); return MimeTypes.AUDIO_OPUS.equalsIgnoreCase(trackInfo.mimeType)
if (!sourcePrepared) { || MimeTypes.AUDIO_WEBM.equalsIgnoreCase(trackInfo.mimeType);
return TrackRenderer.STATE_UNPREPARED; }
}
int trackCount = source.getTrackCount(); @Override
for (int i = 0; i < trackCount; i++) { protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
if (source.getTrackInfo(i).mimeType.equalsIgnoreCase(MimeTypes.AUDIO_OPUS) super.onEnabled(positionUs, joining);
|| source.getTrackInfo(i).mimeType.equalsIgnoreCase(MimeTypes.AUDIO_WEBM)) { seekToInternal(positionUs);
trackIndex = i;
return TrackRenderer.STATE_PREPARED;
}
}
return TrackRenderer.STATE_IGNORE;
} }
@Override @Override
@ -147,8 +141,8 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
if (outputStreamEnded) { if (outputStreamEnded) {
return; return;
} }
sourceIsReady = source.continueBuffering(trackIndex, positionUs); sourceIsReady = continueBufferingSource(positionUs);
checkForDiscontinuity(); checkForDiscontinuity(positionUs);
// Try and read a format if we don't have one already. // Try and read a format if we don't have one already.
if (format == null && !readFormat(positionUs)) { if (format == null && !readFormat(positionUs)) {
@ -189,7 +183,7 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
// Rendering loop. // Rendering loop.
try { try {
renderBuffer(); renderBuffer();
while (feedInputBuffer()) {} while (feedInputBuffer(positionUs)) {}
} catch (AudioTrack.InitializationException e) { } catch (AudioTrack.InitializationException e) {
notifyAudioTrackInitializationError(e); notifyAudioTrackInitializationError(e);
throw new ExoPlaybackException(e); throw new ExoPlaybackException(e);
@ -249,7 +243,7 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
} }
} }
private boolean feedInputBuffer() throws OpusDecoderException { private boolean feedInputBuffer(long positionUs) throws OpusDecoderException {
if (inputStreamEnded) { if (inputStreamEnded) {
return false; return false;
} }
@ -261,8 +255,7 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
} }
} }
int result = source.readData(trackIndex, currentPositionUs, formatHolder, int result = readSource(positionUs, formatHolder, inputBuffer.sampleHolder, false);
inputBuffer.sampleHolder, false);
if (result == SampleSource.NOTHING_READ) { if (result == SampleSource.NOTHING_READ) {
return false; return false;
} }
@ -291,11 +284,11 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
return true; return true;
} }
private void checkForDiscontinuity() { private void checkForDiscontinuity(long positionUs) {
if (decoder == null) { if (decoder == null) {
return; return;
} }
int result = source.readData(trackIndex, currentPositionUs, formatHolder, null, true); int result = readSource(positionUs, formatHolder, null, true);
if (result == SampleSource.DISCONTINUITY_READ) { if (result == SampleSource.DISCONTINUITY_READ) {
flushDecoder(); flushDecoder();
} }
@ -319,11 +312,6 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
return audioTrack.hasPendingData() || (format != null && sourceIsReady); return audioTrack.hasPendingData() || (format != null && sourceIsReady);
} }
@Override
protected long getDurationUs() {
return source.getTrackInfo(trackIndex).durationUs;
}
@Override @Override
public long getPositionUs() { public long getPositionUs() {
long newCurrentPositionUs = audioTrack.getCurrentPositionUs(isEnded()); long newCurrentPositionUs = audioTrack.getCurrentPositionUs(isEnded());
@ -335,14 +323,9 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
return currentPositionUs; return currentPositionUs;
} }
@Override
protected long getBufferedPositionUs() {
return source.getBufferedPositionUs();
}
@Override @Override
protected void seekTo(long positionUs) throws ExoPlaybackException { protected void seekTo(long positionUs) throws ExoPlaybackException {
source.seekToUs(positionUs); super.seekTo(positionUs);
seekToInternal(positionUs); seekToInternal(positionUs);
} }
@ -350,18 +333,11 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
audioTrack.reset(); audioTrack.reset();
currentPositionUs = positionUs; currentPositionUs = positionUs;
allowPositionDiscontinuity = true; allowPositionDiscontinuity = true;
source.seekToUs(positionUs);
inputStreamEnded = false; inputStreamEnded = false;
outputStreamEnded = false; outputStreamEnded = false;
sourceIsReady = false; sourceIsReady = false;
} }
@Override
protected void onEnabled(long positionUs, boolean joining) {
source.enable(trackIndex, positionUs);
seekToInternal(positionUs);
}
@Override @Override
protected void onStarted() { protected void onStarted() {
audioTrack.play(); audioTrack.play();
@ -373,38 +349,24 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
} }
@Override @Override
protected void onReleased() { protected void onDisabled() throws ExoPlaybackException {
source.release(); inputBuffer = null;
} outputBuffer = null;
format = null;
@Override
protected void onDisabled() {
if (decoder != null) {
decoder.release();
decoder = null;
}
audioSessionId = AudioTrack.SESSION_ID_NOT_SET; audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
try { try {
if (decoder != null) {
decoder.release();
decoder = null;
}
audioTrack.release(); audioTrack.release();
} finally { } finally {
inputBuffer = null; super.onDisabled();
outputBuffer = null;
format = null;
source.disable(trackIndex);
}
}
@Override
protected void maybeThrowError() throws ExoPlaybackException {
try {
source.maybeThrowError();
} catch (IOException e) {
throw new ExoPlaybackException(e);
} }
} }
private boolean readFormat(long positionUs) { private boolean readFormat(long positionUs) {
int result = source.readData(trackIndex, positionUs, formatHolder, null, false); int result = readSource(positionUs, formatHolder, null, false);
if (result == SampleSource.FORMAT_READ) { if (result == SampleSource.FORMAT_READ) {
format = formatHolder.format; format = formatHolder.format;
audioTrack.reconfigure(format.getFrameworkMediaFormatV16()); audioTrack.reconfigure(format.getFrameworkMediaFormatV16());

View File

@ -20,7 +20,8 @@ import com.google.android.exoplayer.ExoPlayer;
import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.MediaFormatHolder; import com.google.android.exoplayer.MediaFormatHolder;
import com.google.android.exoplayer.SampleSource; import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.SampleSource.SampleSourceReader; import com.google.android.exoplayer.SampleSourceTrackRenderer;
import com.google.android.exoplayer.TrackInfo;
import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.ext.vp9.VpxDecoderWrapper.InputBuffer; import com.google.android.exoplayer.ext.vp9.VpxDecoderWrapper.InputBuffer;
import com.google.android.exoplayer.ext.vp9.VpxDecoderWrapper.OutputBuffer; import com.google.android.exoplayer.ext.vp9.VpxDecoderWrapper.OutputBuffer;
@ -32,12 +33,10 @@ import android.os.Handler;
import android.os.SystemClock; import android.os.SystemClock;
import android.view.Surface; import android.view.Surface;
import java.io.IOException;
/** /**
* Decodes and renders video using the native VP9 decoder. * Decodes and renders video using the native VP9 decoder.
*/ */
public class LibvpxVideoTrackRenderer extends TrackRenderer { public final class LibvpxVideoTrackRenderer extends SampleSourceTrackRenderer {
/** /**
* Interface definition for a callback to be notified of {@link LibvpxVideoTrackRenderer} events. * Interface definition for a callback to be notified of {@link LibvpxVideoTrackRenderer} events.
@ -91,7 +90,6 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer {
public static final int MSG_SET_SURFACE = 1; public static final int MSG_SET_SURFACE = 1;
public static final int MSG_SET_VPX_SURFACE_VIEW = 2; public static final int MSG_SET_VPX_SURFACE_VIEW = 2;
private final SampleSourceReader source;
private final boolean scaleToFit; private final boolean scaleToFit;
private final Handler eventHandler; private final Handler eventHandler;
private final EventListener eventListener; private final EventListener eventListener;
@ -110,7 +108,6 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer {
private VpxVideoSurfaceView vpxVideoSurfaceView; private VpxVideoSurfaceView vpxVideoSurfaceView;
private boolean outputRgb; private boolean outputRgb;
private int trackIndex;
private boolean inputStreamEnded; private boolean inputStreamEnded;
private boolean outputStreamEnded; private boolean outputStreamEnded;
private boolean sourceIsReady; private boolean sourceIsReady;
@ -141,7 +138,7 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer {
*/ */
public LibvpxVideoTrackRenderer(SampleSource source, boolean scaleToFit, public LibvpxVideoTrackRenderer(SampleSource source, boolean scaleToFit,
Handler eventHandler, EventListener eventListener, int maxDroppedFrameCountToNotify) { Handler eventHandler, EventListener eventListener, int maxDroppedFrameCountToNotify) {
this.source = source.register(); super(source);
this.scaleToFit = scaleToFit; this.scaleToFit = scaleToFit;
this.eventHandler = eventHandler; this.eventHandler = eventHandler;
this.eventListener = eventListener; this.eventListener = eventListener;
@ -152,20 +149,9 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer {
} }
@Override @Override
protected int doPrepare(long positionUs) throws ExoPlaybackException { protected boolean handlesTrack(TrackInfo trackInfo) {
boolean sourcePrepared = source.prepare(positionUs); return MimeTypes.VIDEO_VP9.equalsIgnoreCase(trackInfo.mimeType)
if (!sourcePrepared) { || MimeTypes.VIDEO_WEBM.equalsIgnoreCase(trackInfo.mimeType);
return TrackRenderer.STATE_UNPREPARED;
}
int trackCount = source.getTrackCount();
for (int i = 0; i < trackCount; i++) {
if (source.getTrackInfo(i).mimeType.equalsIgnoreCase(MimeTypes.VIDEO_VP9)
|| source.getTrackInfo(i).mimeType.equalsIgnoreCase(MimeTypes.VIDEO_WEBM)) {
trackIndex = i;
return TrackRenderer.STATE_PREPARED;
}
}
return TrackRenderer.STATE_IGNORE;
} }
@Override @Override
@ -173,7 +159,7 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer {
if (outputStreamEnded) { if (outputStreamEnded) {
return; return;
} }
sourceIsReady = source.continueBuffering(trackIndex, positionUs); sourceIsReady = continueBufferingSource(positionUs);
checkForDiscontinuity(positionUs); checkForDiscontinuity(positionUs);
// Try and read a format if we don't have one already. // Try and read a format if we don't have one already.
@ -302,7 +288,7 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer {
} }
} }
int result = source.readData(trackIndex, positionUs, formatHolder, inputBuffer.sampleHolder, int result = readSource(positionUs, formatHolder, inputBuffer.sampleHolder,
false); false);
if (result == SampleSource.NOTHING_READ) { if (result == SampleSource.NOTHING_READ) {
return false; return false;
@ -334,7 +320,7 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer {
if (decoder == null) { if (decoder == null) {
return; return;
} }
int result = source.readData(trackIndex, positionUs, formatHolder, null, true); int result = readSource(positionUs, formatHolder, null, true);
if (result == SampleSource.DISCONTINUITY_READ) { if (result == SampleSource.DISCONTINUITY_READ) {
flushDecoder(); flushDecoder();
} }
@ -356,25 +342,15 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer {
return format != null && sourceIsReady; return format != null && sourceIsReady;
} }
@Override
protected long getDurationUs() {
return source.getTrackInfo(trackIndex).durationUs;
}
@Override
protected long getBufferedPositionUs() {
return source.getBufferedPositionUs();
}
@Override @Override
protected void seekTo(long positionUs) throws ExoPlaybackException { protected void seekTo(long positionUs) throws ExoPlaybackException {
source.seekToUs(positionUs); super.seekTo(positionUs);
seekToInternal(); seekToInternal();
} }
@Override @Override
protected void onEnabled(long positionUs, boolean joining) { protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
source.enable(trackIndex, positionUs); super.onEnabled(positionUs, joining);
seekToInternal(); seekToInternal();
} }
@ -397,33 +373,22 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer {
} }
@Override @Override
protected void onReleased() { protected void onDisabled() throws ExoPlaybackException {
source.release();
}
@Override
protected void onDisabled() {
if (decoder != null) {
decoder.release();
decoder = null;
}
inputBuffer = null; inputBuffer = null;
outputBuffer = null; outputBuffer = null;
format = null; format = null;
source.disable(trackIndex);
}
@Override
protected void maybeThrowError() throws ExoPlaybackException {
try { try {
source.maybeThrowError(); if (decoder != null) {
} catch (IOException e) { decoder.release();
throw new ExoPlaybackException(e); decoder = null;
}
} finally {
super.onDisabled();
} }
} }
private boolean readFormat(long positionUs) { private boolean readFormat(long positionUs) {
int result = source.readData(trackIndex, positionUs, formatHolder, null, false); int result = readSource(positionUs, formatHolder, null, false);
if (result == SampleSource.FORMAT_READ) { if (result == SampleSource.FORMAT_READ) {
format = formatHolder.format; format = formatHolder.format;
return true; return true;

View File

@ -157,12 +157,12 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
} }
@Override @Override
protected boolean handlesMimeType(String mimeType) { protected boolean handlesTrack(TrackInfo trackInfo) {
return MimeTypes.isAudio(mimeType) && super.handlesMimeType(mimeType); return MimeTypes.isAudio(trackInfo.mimeType);
} }
@Override @Override
protected void onEnabled(long positionUs, boolean joining) { protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
super.onEnabled(positionUs, joining); super.onEnabled(positionUs, joining);
seekToInternal(positionUs); seekToInternal(positionUs);
} }
@ -231,7 +231,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
} }
@Override @Override
protected void onDisabled() { protected void onDisabled() throws ExoPlaybackException {
audioSessionId = AudioTrack.SESSION_ID_NOT_SET; audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
try { try {
audioTrack.release(); audioTrack.release();

View File

@ -16,7 +16,6 @@
package com.google.android.exoplayer; package com.google.android.exoplayer;
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException; import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
import com.google.android.exoplayer.SampleSource.SampleSourceReader;
import com.google.android.exoplayer.drm.DrmInitData; import com.google.android.exoplayer.drm.DrmInitData;
import com.google.android.exoplayer.drm.DrmSessionManager; import com.google.android.exoplayer.drm.DrmSessionManager;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
@ -31,7 +30,6 @@ import android.media.MediaCrypto;
import android.os.Handler; import android.os.Handler;
import android.os.SystemClock; import android.os.SystemClock;
import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -40,7 +38,7 @@ import java.util.List;
* An abstract {@link TrackRenderer} that uses {@link MediaCodec} to decode samples for rendering. * An abstract {@link TrackRenderer} that uses {@link MediaCodec} to decode samples for rendering.
*/ */
@TargetApi(16) @TargetApi(16)
public abstract class MediaCodecTrackRenderer extends TrackRenderer { public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer {
/** /**
* Interface definition for a callback to be notified of {@link MediaCodecTrackRenderer} events. * Interface definition for a callback to be notified of {@link MediaCodecTrackRenderer} events.
@ -183,7 +181,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
private final DrmSessionManager drmSessionManager; private final DrmSessionManager drmSessionManager;
private final boolean playClearSamplesWithoutKeys; private final boolean playClearSamplesWithoutKeys;
private final SampleSourceReader source;
private final SampleHolder sampleHolder; private final SampleHolder sampleHolder;
private final MediaFormatHolder formatHolder; private final MediaFormatHolder formatHolder;
private final List<Long> decodeOnlyPresentationTimestamps; private final List<Long> decodeOnlyPresentationTimestamps;
@ -207,7 +204,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
private int codecReinitializationState; private int codecReinitializationState;
private boolean codecHasQueuedBuffers; private boolean codecHasQueuedBuffers;
private int trackIndex;
private int sourceState; private int sourceState;
private boolean inputStreamEnded; private boolean inputStreamEnded;
private boolean outputStreamEnded; private boolean outputStreamEnded;
@ -229,8 +225,8 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
*/ */
public MediaCodecTrackRenderer(SampleSource source, DrmSessionManager drmSessionManager, public MediaCodecTrackRenderer(SampleSource source, DrmSessionManager drmSessionManager,
boolean playClearSamplesWithoutKeys, Handler eventHandler, EventListener eventListener) { boolean playClearSamplesWithoutKeys, Handler eventHandler, EventListener eventListener) {
super(source);
Assertions.checkState(Util.SDK_INT >= 16); Assertions.checkState(Util.SDK_INT >= 16);
this.source = source.register();
this.drmSessionManager = drmSessionManager; this.drmSessionManager = drmSessionManager;
this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys; this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys;
this.eventHandler = eventHandler; this.eventHandler = eventHandler;
@ -245,39 +241,8 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
} }
@Override @Override
protected int doPrepare(long positionUs) { protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
boolean sourcePrepared = source.prepare(positionUs); super.onEnabled(positionUs, joining);
if (!sourcePrepared) {
return TrackRenderer.STATE_UNPREPARED;
}
int trackCount = source.getTrackCount();
for (int i = 0; i < trackCount; i++) {
// TODO: Right now this is getting the mime types of the container format
// (e.g. audio/mp4 and video/mp4 for fragmented mp4). It needs to be getting the mime types
// of the actual samples (e.g. audio/mp4a-latm and video/avc).
if (handlesMimeType(source.getTrackInfo(i).mimeType)) {
trackIndex = i;
return TrackRenderer.STATE_PREPARED;
}
}
return TrackRenderer.STATE_IGNORE;
}
/**
* Determines whether a mime type is handled by the renderer.
*
* @param mimeType The mime type to test.
* @return True if the renderer can handle the mime type. False otherwise.
*/
protected boolean handlesMimeType(String mimeType) {
return true;
// TODO: Uncomment once the TODO above is fixed.
// DecoderInfoUtil.getDecoder(mimeType) != null;
}
@Override
protected void onEnabled(long positionUs, boolean joining) {
source.enable(trackIndex, positionUs);
seekToInternal(); seekToInternal();
} }
@ -400,7 +365,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
} }
@Override @Override
protected void onDisabled() { protected void onDisabled() throws ExoPlaybackException {
format = null; format = null;
drmInitData = null; drmInitData = null;
try { try {
@ -412,7 +377,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
openedDrmSession = false; openedDrmSession = false;
} }
} finally { } finally {
source.disable(trackIndex); super.onDisabled();
} }
} }
} }
@ -445,24 +410,9 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
} }
} }
@Override
protected void onReleased() {
source.release();
}
@Override
protected long getDurationUs() {
return source.getTrackInfo(trackIndex).durationUs;
}
@Override
protected long getBufferedPositionUs() {
return source.getBufferedPositionUs();
}
@Override @Override
protected void seekTo(long positionUs) throws ExoPlaybackException { protected void seekTo(long positionUs) throws ExoPlaybackException {
source.seekToUs(positionUs); super.seekTo(positionUs);
seekToInternal(); seekToInternal();
} }
@ -484,7 +434,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
@Override @Override
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
sourceState = source.continueBuffering(trackIndex, positionUs) sourceState = continueBufferingSource(positionUs)
? (sourceState == SOURCE_STATE_NOT_READY ? SOURCE_STATE_READY : sourceState) ? (sourceState == SOURCE_STATE_NOT_READY ? SOURCE_STATE_READY : sourceState)
: SOURCE_STATE_NOT_READY; : SOURCE_STATE_NOT_READY;
checkForDiscontinuity(positionUs); checkForDiscontinuity(positionUs);
@ -506,7 +456,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
} }
private void readFormat(long positionUs) throws ExoPlaybackException { private void readFormat(long positionUs) throws ExoPlaybackException {
int result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false); int result = readSource(positionUs, formatHolder, sampleHolder, false);
if (result == SampleSource.FORMAT_READ) { if (result == SampleSource.FORMAT_READ) {
onInputFormatChanged(formatHolder); onInputFormatChanged(formatHolder);
} }
@ -516,7 +466,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
if (codec == null) { if (codec == null) {
return; return;
} }
int result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, true); int result = readSource(positionUs, formatHolder, sampleHolder, true);
if (result == SampleSource.DISCONTINUITY_READ) { if (result == SampleSource.DISCONTINUITY_READ) {
flushCodec(); flushCodec();
} }
@ -597,7 +547,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
} }
codecReconfigurationState = RECONFIGURATION_STATE_QUEUE_PENDING; codecReconfigurationState = RECONFIGURATION_STATE_QUEUE_PENDING;
} }
result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false); result = readSource(positionUs, formatHolder, sampleHolder, false);
if (firstFeed && sourceState == SOURCE_STATE_READY && result == SampleSource.NOTHING_READ) { if (firstFeed && sourceState == SOURCE_STATE_READY && result == SampleSource.NOTHING_READ) {
sourceState = SOURCE_STATE_READY_READ_MAY_FAIL; sourceState = SOURCE_STATE_READY_READ_MAY_FAIL;
} }
@ -774,15 +724,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
return false; return false;
} }
@Override
protected void maybeThrowError() throws ExoPlaybackException {
try {
source.maybeThrowError();
} catch (IOException e) {
throw new ExoPlaybackException(e);
}
}
@Override @Override
protected boolean isEnded() { protected boolean isEnded() {
return outputStreamEnded; return outputStreamEnded;
@ -950,11 +891,8 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
* incorrectly on the host device. False otherwise. * incorrectly on the host device. False otherwise.
*/ */
private static boolean codecNeedsEndOfStreamWorkaround(String name) { private static boolean codecNeedsEndOfStreamWorkaround(String name) {
return Util.SDK_INT <= 17 return Util.SDK_INT <= 17 && "ht7s3".equals(Util.DEVICE) // Tesco HUDL
&& "OMX.rk.video_decoder.avc".equals(name) && "OMX.rk.video_decoder.avc".equals(name);
&& ("ht7s3".equals(Util.DEVICE) // Tesco HUDL
|| "rk30sdk".equals(Util.DEVICE) // Rockchip rk30
|| "rk31sdk".equals(Util.DEVICE)); // Rockchip rk31
} }
} }

View File

@ -256,12 +256,12 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
} }
@Override @Override
protected boolean handlesMimeType(String mimeType) { protected boolean handlesTrack(TrackInfo trackInfo) {
return MimeTypes.isVideo(mimeType) && super.handlesMimeType(mimeType); return MimeTypes.isVideo(trackInfo.mimeType);
} }
@Override @Override
protected void onEnabled(long positionUs, boolean joining) { protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
super.onEnabled(positionUs, joining); super.onEnabled(positionUs, joining);
renderedFirstFrame = false; renderedFirstFrame = false;
if (joining && allowedJoiningTimeUs > 0) { if (joining && allowedJoiningTimeUs > 0) {
@ -314,7 +314,7 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
} }
@Override @Override
public void onDisabled() { protected void onDisabled() throws ExoPlaybackException {
currentWidth = -1; currentWidth = -1;
currentHeight = -1; currentHeight = -1;
currentPixelWidthHeightRatio = -1; currentPixelWidthHeightRatio = -1;

View File

@ -0,0 +1,123 @@
/*
* 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;
import com.google.android.exoplayer.SampleSource.SampleSourceReader;
import java.io.IOException;
/**
* Base class for {@link TrackRenderer} implementations that render samples obtained from a
* {@link SampleSource}.
*/
public abstract class SampleSourceTrackRenderer extends TrackRenderer {
private final SampleSourceReader source;
private int trackIndex;
/**
* @param source The upstream source from which the renderer obtains samples.
*/
public SampleSourceTrackRenderer(SampleSource source) {
this.source = source.register();
}
@Override
protected int doPrepare(long positionUs) throws ExoPlaybackException {
boolean sourcePrepared = source.prepare(positionUs);
if (!sourcePrepared) {
return TrackRenderer.STATE_UNPREPARED;
}
int trackCount = source.getTrackCount();
for (int i = 0; i < trackCount; i++) {
TrackInfo trackInfo = source.getTrackInfo(i);
if (handlesTrack(trackInfo)) {
trackIndex = i;
onTrackSelected(trackInfo);
return TrackRenderer.STATE_PREPARED;
}
}
return TrackRenderer.STATE_IGNORE;
}
/**
* Returns whether this renderer is capable of handling the provided track.
*
* @param trackInfo The track.
* @return True if the renderer can handle the track, false otherwise.
*/
protected abstract boolean handlesTrack(TrackInfo trackInfo);
/**
* Invoked when a track is selected.
*
* @param trackInfo The selected track.
*/
protected void onTrackSelected(TrackInfo trackInfo) {
// Do nothing.
}
@Override
protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
source.enable(trackIndex, positionUs);
}
@Override
protected void seekTo(long positionUs) throws ExoPlaybackException {
source.seekToUs(positionUs);
}
@Override
protected long getBufferedPositionUs() {
return source.getBufferedPositionUs();
}
@Override
protected long getDurationUs() {
return source.getTrackInfo(trackIndex).durationUs;
}
@Override
protected void maybeThrowError() throws ExoPlaybackException {
try {
source.maybeThrowError();
} catch (IOException e) {
throw new ExoPlaybackException(e);
}
}
@Override
protected void onDisabled() throws ExoPlaybackException {
source.disable(trackIndex);
}
@Override
protected void onReleased() throws ExoPlaybackException {
source.release();
}
protected final boolean continueBufferingSource(long positionUs) {
return source.continueBuffering(trackIndex, positionUs);
}
protected final int readSource(long positionUs, MediaFormatHolder formatHolder,
SampleHolder sampleHolder, boolean onlyReadDiscontinuity) {
return source.readData(trackIndex, positionUs, formatHolder, sampleHolder,
onlyReadDiscontinuity);
}
}

View File

@ -110,11 +110,11 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
* @throws ExoPlaybackException If an error occurs. * @throws ExoPlaybackException If an error occurs.
*/ */
/* package */ final int prepare(long positionUs) throws ExoPlaybackException { /* package */ final int prepare(long positionUs) throws ExoPlaybackException {
Assertions.checkState(state == TrackRenderer.STATE_UNPREPARED); Assertions.checkState(state == STATE_UNPREPARED);
state = doPrepare(positionUs); state = doPrepare(positionUs);
Assertions.checkState(state == TrackRenderer.STATE_UNPREPARED || Assertions.checkState(state == STATE_UNPREPARED ||
state == TrackRenderer.STATE_PREPARED || state == STATE_PREPARED ||
state == TrackRenderer.STATE_IGNORE); state == STATE_IGNORE);
return state; return state;
} }
@ -143,8 +143,8 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
* @throws ExoPlaybackException If an error occurs. * @throws ExoPlaybackException If an error occurs.
*/ */
/* package */ final void enable(long positionUs, boolean joining) throws ExoPlaybackException { /* package */ final void enable(long positionUs, boolean joining) throws ExoPlaybackException {
Assertions.checkState(state == TrackRenderer.STATE_PREPARED); Assertions.checkState(state == STATE_PREPARED);
state = TrackRenderer.STATE_ENABLED; state = STATE_ENABLED;
onEnabled(positionUs, joining); onEnabled(positionUs, joining);
} }
@ -170,8 +170,8 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
* @throws ExoPlaybackException If an error occurs. * @throws ExoPlaybackException If an error occurs.
*/ */
/* package */ final void start() throws ExoPlaybackException { /* package */ final void start() throws ExoPlaybackException {
Assertions.checkState(state == TrackRenderer.STATE_ENABLED); Assertions.checkState(state == STATE_ENABLED);
state = TrackRenderer.STATE_STARTED; state = STATE_STARTED;
onStarted(); onStarted();
} }
@ -192,8 +192,8 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
* @throws ExoPlaybackException If an error occurs. * @throws ExoPlaybackException If an error occurs.
*/ */
/* package */ final void stop() throws ExoPlaybackException { /* package */ final void stop() throws ExoPlaybackException {
Assertions.checkState(state == TrackRenderer.STATE_STARTED); Assertions.checkState(state == STATE_STARTED);
state = TrackRenderer.STATE_ENABLED; state = STATE_ENABLED;
onStopped(); onStopped();
} }
@ -214,8 +214,8 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
* @throws ExoPlaybackException If an error occurs. * @throws ExoPlaybackException If an error occurs.
*/ */
/* package */ final void disable() throws ExoPlaybackException { /* package */ final void disable() throws ExoPlaybackException {
Assertions.checkState(state == TrackRenderer.STATE_ENABLED); Assertions.checkState(state == STATE_ENABLED);
state = TrackRenderer.STATE_PREPARED; state = STATE_PREPARED;
onDisabled(); onDisabled();
} }
@ -236,10 +236,10 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
* @throws ExoPlaybackException If an error occurs. * @throws ExoPlaybackException If an error occurs.
*/ */
/* package */ final void release() throws ExoPlaybackException { /* package */ final void release() throws ExoPlaybackException {
Assertions.checkState(state != TrackRenderer.STATE_ENABLED Assertions.checkState(state != STATE_ENABLED
&& state != TrackRenderer.STATE_STARTED && state != STATE_STARTED
&& state != TrackRenderer.STATE_RELEASED); && state != STATE_RELEASED);
state = TrackRenderer.STATE_RELEASED; state = STATE_RELEASED;
onReleased(); onReleased();
} }

View File

@ -58,7 +58,7 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load
private static final int STATE_PREPARED = 2; private static final int STATE_PREPARED = 2;
private static final int STATE_ENABLED = 3; private static final int STATE_ENABLED = 3;
private static final int NO_RESET_PENDING = -1; private static final long NO_RESET_PENDING = Long.MIN_VALUE;
private final int eventSourceId; private final int eventSourceId;
private final LoadControl loadControl; private final LoadControl loadControl;

View File

@ -93,7 +93,7 @@ public final class ExtractorSampleSource implements SampleSource, SampleSourceRe
public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT_LIVE = 6; public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT_LIVE = 6;
private static final int MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA = -1; private static final int MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA = -1;
private static final int NO_RESET_PENDING = -1; private static final long NO_RESET_PENDING = Long.MIN_VALUE;
/** /**
* Default extractor classes in priority order. They are referred to indirectly so that it is * Default extractor classes in priority order. They are referred to indirectly so that it is
@ -326,10 +326,14 @@ public final class ExtractorSampleSource implements SampleSource, SampleSourceRe
enabledTrackCount++; enabledTrackCount++;
trackEnabledStates[track] = true; trackEnabledStates[track] = true;
pendingMediaFormat[track] = true; pendingMediaFormat[track] = true;
if (enabledTrackCount == 1) {
seekToUs(positionUs);
}
pendingDiscontinuities[track] = false; pendingDiscontinuities[track] = false;
if (enabledTrackCount == 1) {
// Treat all enables in non-seekable media as being from t=0.
positionUs = !seekMap.isSeekable() ? 0 : positionUs;
downstreamPositionUs = positionUs;
lastSeekPositionUs = positionUs;
restartFrom(positionUs);
}
} }
@Override @Override
@ -431,10 +435,8 @@ public final class ExtractorSampleSource implements SampleSource, SampleSourceRe
public void seekToUs(long positionUs) { public void seekToUs(long positionUs) {
Assertions.checkState(prepared); Assertions.checkState(prepared);
Assertions.checkState(enabledTrackCount > 0); Assertions.checkState(enabledTrackCount > 0);
if (!seekMap.isSeekable()) { // Treat all seeks into non-seekable media as being to t=0.
// Treat all seeks into non-seekable media as seeks to the start. positionUs = !seekMap.isSeekable() ? 0 : positionUs;
positionUs = 0;
}
long currentPositionUs = isPendingReset() ? pendingResetPositionUs : downstreamPositionUs; long currentPositionUs = isPendingReset() ? pendingResetPositionUs : downstreamPositionUs;
downstreamPositionUs = positionUs; downstreamPositionUs = positionUs;

View File

@ -52,7 +52,7 @@ public final class HlsSampleSource implements SampleSource, SampleSourceReader,
*/ */
public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT = 3; public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT = 3;
private static final int NO_RESET_PENDING = -1; private static final long NO_RESET_PENDING = Long.MIN_VALUE;
private final HlsChunkSource chunkSource; private final HlsChunkSource chunkSource;
private final LinkedList<HlsExtractorWrapper> extractors; private final LinkedList<HlsExtractorWrapper> extractors;
@ -182,15 +182,17 @@ public final class HlsSampleSource implements SampleSource, SampleSourceReader,
enabledTrackCount++; enabledTrackCount++;
trackEnabledStates[track] = true; trackEnabledStates[track] = true;
downstreamMediaFormats[track] = null; downstreamMediaFormats[track] = null;
pendingDiscontinuities[track] = false;
downstreamFormat = null; downstreamFormat = null;
if (!loadControlRegistered) { if (!loadControlRegistered) {
loadControl.register(this, bufferSizeContribution); loadControl.register(this, bufferSizeContribution);
loadControlRegistered = true; loadControlRegistered = true;
} }
if (enabledTrackCount == 1) { if (enabledTrackCount == 1) {
seekToUs(positionUs); downstreamPositionUs = positionUs;
lastSeekPositionUs = positionUs;
restartFrom(positionUs);
} }
pendingDiscontinuities[track] = false;
} }
@Override @Override

View File

@ -19,7 +19,8 @@ import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.MediaFormatHolder; import com.google.android.exoplayer.MediaFormatHolder;
import com.google.android.exoplayer.SampleHolder; import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource; import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.SampleSource.SampleSourceReader; import com.google.android.exoplayer.SampleSourceTrackRenderer;
import com.google.android.exoplayer.TrackInfo;
import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
@ -35,7 +36,7 @@ import java.io.IOException;
* *
* @param <T> The type of the metadata. * @param <T> The type of the metadata.
*/ */
public final class MetadataTrackRenderer<T> extends TrackRenderer implements Callback { public final class MetadataTrackRenderer<T> extends SampleSourceTrackRenderer implements Callback {
/** /**
* An interface for components that process metadata. * An interface for components that process metadata.
@ -55,16 +56,13 @@ public final class MetadataTrackRenderer<T> extends TrackRenderer implements Cal
private static final int MSG_INVOKE_RENDERER = 0; private static final int MSG_INVOKE_RENDERER = 0;
private final SampleSourceReader source;
private final MetadataParser<T> metadataParser; private final MetadataParser<T> metadataParser;
private final MetadataRenderer<T> metadataRenderer; private final MetadataRenderer<T> metadataRenderer;
private final Handler metadataHandler; private final Handler metadataHandler;
private final MediaFormatHolder formatHolder; private final MediaFormatHolder formatHolder;
private final SampleHolder sampleHolder; private final SampleHolder sampleHolder;
private int trackIndex;
private boolean inputStreamEnded; private boolean inputStreamEnded;
private long pendingMetadataTimestamp; private long pendingMetadataTimestamp;
private T pendingMetadata; private T pendingMetadata;
@ -80,7 +78,7 @@ public final class MetadataTrackRenderer<T> extends TrackRenderer implements Cal
*/ */
public MetadataTrackRenderer(SampleSource source, MetadataParser<T> metadataParser, public MetadataTrackRenderer(SampleSource source, MetadataParser<T> metadataParser,
MetadataRenderer<T> metadataRenderer, Looper metadataRendererLooper) { MetadataRenderer<T> metadataRenderer, Looper metadataRendererLooper) {
this.source = source.register(); super(source);
this.metadataParser = Assertions.checkNotNull(metadataParser); this.metadataParser = Assertions.checkNotNull(metadataParser);
this.metadataRenderer = Assertions.checkNotNull(metadataRenderer); this.metadataRenderer = Assertions.checkNotNull(metadataRenderer);
this.metadataHandler = metadataRendererLooper == null ? null this.metadataHandler = metadataRendererLooper == null ? null
@ -90,30 +88,19 @@ public final class MetadataTrackRenderer<T> extends TrackRenderer implements Cal
} }
@Override @Override
protected int doPrepare(long positionUs) { protected boolean handlesTrack(TrackInfo trackInfo) {
boolean sourcePrepared = source.prepare(positionUs); return metadataParser.canParse(trackInfo.mimeType);
if (!sourcePrepared) {
return TrackRenderer.STATE_UNPREPARED;
}
int trackCount = source.getTrackCount();
for (int i = 0; i < trackCount; i++) {
if (metadataParser.canParse(source.getTrackInfo(i).mimeType)) {
trackIndex = i;
return TrackRenderer.STATE_PREPARED;
}
}
return TrackRenderer.STATE_IGNORE;
} }
@Override @Override
protected void onEnabled(long positionUs, boolean joining) { protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
source.enable(trackIndex, positionUs); super.onEnabled(positionUs, joining);
seekToInternal(); seekToInternal();
} }
@Override @Override
protected void seekTo(long positionUs) throws ExoPlaybackException { protected void seekTo(long positionUs) throws ExoPlaybackException {
source.seekToUs(positionUs); super.seekTo(positionUs);
seekToInternal(); seekToInternal();
} }
@ -123,23 +110,21 @@ public final class MetadataTrackRenderer<T> extends TrackRenderer implements Cal
} }
@Override @Override
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
throws ExoPlaybackException { continueBufferingSource(positionUs);
source.continueBuffering(trackIndex, positionUs);
if (!inputStreamEnded && pendingMetadata == null) { if (!inputStreamEnded && pendingMetadata == null) {
int result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false); int result = readSource(positionUs, formatHolder, sampleHolder, false);
if (result == SampleSource.SAMPLE_READ) { if (result == SampleSource.SAMPLE_READ) {
pendingMetadataTimestamp = sampleHolder.timeUs; pendingMetadataTimestamp = sampleHolder.timeUs;
try { try {
pendingMetadata = metadataParser.parse(sampleHolder.data.array(), sampleHolder.size); pendingMetadata = metadataParser.parse(sampleHolder.data.array(), sampleHolder.size);
} catch (IOException e) { } catch (IOException e) {
throw new ExoPlaybackException(e); throw new ExoPlaybackException(e);
}
sampleHolder.data.clear();
} else if (result == SampleSource.END_OF_STREAM) {
inputStreamEnded = true;
} }
sampleHolder.data.clear();
} else if (result == SampleSource.END_OF_STREAM) {
inputStreamEnded = true;
}
} }
if (pendingMetadata != null && pendingMetadataTimestamp <= positionUs) { if (pendingMetadata != null && pendingMetadataTimestamp <= positionUs) {
@ -149,23 +134,9 @@ public final class MetadataTrackRenderer<T> extends TrackRenderer implements Cal
} }
@Override @Override
protected void maybeThrowError() throws ExoPlaybackException { protected void onDisabled() throws ExoPlaybackException {
try {
source.maybeThrowError();
} catch (IOException e) {
throw new ExoPlaybackException(e);
}
}
@Override
protected void onDisabled() {
pendingMetadata = null; pendingMetadata = null;
source.disable(trackIndex); super.onDisabled();
}
@Override
protected long getDurationUs() {
return source.getTrackInfo(trackIndex).durationUs;
} }
@Override @Override

View File

@ -19,7 +19,8 @@ import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.MediaFormatHolder; import com.google.android.exoplayer.MediaFormatHolder;
import com.google.android.exoplayer.SampleHolder; import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource; import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.SampleSource.SampleSourceReader; import com.google.android.exoplayer.SampleSourceTrackRenderer;
import com.google.android.exoplayer.TrackInfo;
import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
@ -58,7 +59,7 @@ import java.util.List;
* {@link SubtitleParser#canParse(String)} will be used. * {@link SubtitleParser#canParse(String)} will be used.
*/ */
@TargetApi(16) @TargetApi(16)
public final class TextTrackRenderer extends TrackRenderer implements Callback { public final class TextTrackRenderer extends SampleSourceTrackRenderer implements Callback {
private static final int MSG_UPDATE_OVERLAY = 0; private static final int MSG_UPDATE_OVERLAY = 0;
@ -104,15 +105,11 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback {
private final Handler textRendererHandler; private final Handler textRendererHandler;
private final TextRenderer textRenderer; private final TextRenderer textRenderer;
private final SampleSourceReader source;
private final MediaFormatHolder formatHolder; private final MediaFormatHolder formatHolder;
private final SubtitleParser[] subtitleParsers; private final SubtitleParser[] subtitleParsers;
private int parserIndex; private int parserIndex;
private int trackIndex;
private boolean inputStreamEnded; private boolean inputStreamEnded;
private Subtitle subtitle; private Subtitle subtitle;
private Subtitle nextSubtitle; private Subtitle nextSubtitle;
private SubtitleParserHelper parserHelper; private SubtitleParserHelper parserHelper;
@ -132,7 +129,7 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback {
*/ */
public TextTrackRenderer(SampleSource source, TextRenderer textRenderer, public TextTrackRenderer(SampleSource source, TextRenderer textRenderer,
Looper textRendererLooper, SubtitleParser... subtitleParsers) { Looper textRendererLooper, SubtitleParser... subtitleParsers) {
this.source = source.register(); super(source);
this.textRenderer = Assertions.checkNotNull(textRenderer); this.textRenderer = Assertions.checkNotNull(textRenderer);
this.textRendererHandler = textRendererLooper == null ? null this.textRendererHandler = textRendererLooper == null ? null
: new Handler(textRendererLooper, this); : new Handler(textRendererLooper, this);
@ -153,27 +150,29 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback {
} }
@Override @Override
protected int doPrepare(long positionUs) { protected boolean handlesTrack(TrackInfo trackInfo) {
boolean sourcePrepared = source.prepare(positionUs);
if (!sourcePrepared) {
return TrackRenderer.STATE_UNPREPARED;
}
int trackCount = source.getTrackCount();
for (int i = 0; i < subtitleParsers.length; i++) { for (int i = 0; i < subtitleParsers.length; i++) {
for (int j = 0; j < trackCount; j++) { if (subtitleParsers[i].canParse(trackInfo.mimeType)) {
if (subtitleParsers[i].canParse(source.getTrackInfo(j).mimeType)) { return true;
parserIndex = i;
trackIndex = j;
return TrackRenderer.STATE_PREPARED;
}
} }
} }
return TrackRenderer.STATE_IGNORE; return false;
} }
@Override @Override
protected void onEnabled(long positionUs, boolean joining) { protected void onTrackSelected(TrackInfo trackInfo) {
source.enable(trackIndex, positionUs); for (int i = 0; i < subtitleParsers.length; i++) {
if (subtitleParsers[i].canParse(trackInfo.mimeType)) {
parserIndex = i;
return;
}
}
throw new IllegalStateException("Invalid track selected");
}
@Override
protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
super.onEnabled(positionUs, joining);
parserThread = new HandlerThread("textParser"); parserThread = new HandlerThread("textParser");
parserThread.start(); parserThread.start();
parserHelper = new SubtitleParserHelper(parserThread.getLooper(), subtitleParsers[parserIndex]); parserHelper = new SubtitleParserHelper(parserThread.getLooper(), subtitleParsers[parserIndex]);
@ -181,8 +180,8 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback {
} }
@Override @Override
protected void seekTo(long positionUs) { protected void seekTo(long positionUs) throws ExoPlaybackException {
source.seekToUs(positionUs); super.seekTo(positionUs);
seekToInternal(); seekToInternal();
} }
@ -196,8 +195,7 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback {
@Override @Override
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
source.continueBuffering(trackIndex, positionUs); continueBufferingSource(positionUs);
if (nextSubtitle == null) { if (nextSubtitle == null) {
try { try {
nextSubtitle = parserHelper.getAndClearResult(); nextSubtitle = parserHelper.getAndClearResult();
@ -241,7 +239,7 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback {
// Try and read the next subtitle from the source. // Try and read the next subtitle from the source.
SampleHolder sampleHolder = parserHelper.getSampleHolder(); SampleHolder sampleHolder = parserHelper.getSampleHolder();
sampleHolder.clearData(); sampleHolder.clearData();
int result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false); int result = readSource(positionUs, formatHolder, sampleHolder, false);
if (result == SampleSource.SAMPLE_READ) { if (result == SampleSource.SAMPLE_READ) {
parserHelper.startParseOperation(); parserHelper.startParseOperation();
} else if (result == SampleSource.END_OF_STREAM) { } else if (result == SampleSource.END_OF_STREAM) {
@ -251,33 +249,14 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback {
} }
@Override @Override
protected void onDisabled() { protected void onDisabled() throws ExoPlaybackException {
subtitle = null; subtitle = null;
nextSubtitle = null; nextSubtitle = null;
parserThread.quit(); parserThread.quit();
parserThread = null; parserThread = null;
parserHelper = null; parserHelper = null;
clearTextRenderer(); clearTextRenderer();
source.disable(trackIndex); super.onDisabled();
}
@Override
protected void onReleased() {
source.release();
}
@Override
protected void maybeThrowError() throws ExoPlaybackException {
try {
source.maybeThrowError();
} catch (IOException e) {
throw new ExoPlaybackException(e);
}
}
@Override
protected long getDurationUs() {
return source.getTrackInfo(trackIndex).durationUs;
} }
@Override @Override

View File

@ -20,7 +20,8 @@ import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.MediaFormatHolder; import com.google.android.exoplayer.MediaFormatHolder;
import com.google.android.exoplayer.SampleHolder; import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource; import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.SampleSource.SampleSourceReader; import com.google.android.exoplayer.SampleSourceTrackRenderer;
import com.google.android.exoplayer.TrackInfo;
import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.text.Cue; import com.google.android.exoplayer.text.Cue;
import com.google.android.exoplayer.text.TextRenderer; import com.google.android.exoplayer.text.TextRenderer;
@ -32,14 +33,13 @@ import android.os.Handler.Callback;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.TreeSet; import java.util.TreeSet;
/** /**
* A {@link TrackRenderer} for EIA-608 closed captions in a media stream. * A {@link TrackRenderer} for EIA-608 closed captions in a media stream.
*/ */
public final class Eia608TrackRenderer extends TrackRenderer implements Callback { public final class Eia608TrackRenderer extends SampleSourceTrackRenderer implements Callback {
private static final int MSG_INVOKE_RENDERER = 0; private static final int MSG_INVOKE_RENDERER = 0;
@ -53,7 +53,6 @@ public final class Eia608TrackRenderer extends TrackRenderer implements Callback
// The maximum duration that captions are parsed ahead of the current position. // The maximum duration that captions are parsed ahead of the current position.
private static final int MAX_SAMPLE_READAHEAD_US = 5000000; private static final int MAX_SAMPLE_READAHEAD_US = 5000000;
private final SampleSourceReader source;
private final Eia608Parser eia608Parser; private final Eia608Parser eia608Parser;
private final TextRenderer textRenderer; private final TextRenderer textRenderer;
private final Handler textRendererHandler; private final Handler textRendererHandler;
@ -62,9 +61,7 @@ public final class Eia608TrackRenderer extends TrackRenderer implements Callback
private final StringBuilder captionStringBuilder; private final StringBuilder captionStringBuilder;
private final TreeSet<ClosedCaptionList> pendingCaptionLists; private final TreeSet<ClosedCaptionList> pendingCaptionLists;
private int trackIndex;
private boolean inputStreamEnded; private boolean inputStreamEnded;
private int captionMode; private int captionMode;
private int captionRowCount; private int captionRowCount;
private String caption; private String caption;
@ -81,7 +78,7 @@ public final class Eia608TrackRenderer extends TrackRenderer implements Callback
*/ */
public Eia608TrackRenderer(SampleSource source, TextRenderer textRenderer, public Eia608TrackRenderer(SampleSource source, TextRenderer textRenderer,
Looper textRendererLooper) { Looper textRendererLooper) {
this.source = source.register(); super(source);
this.textRenderer = Assertions.checkNotNull(textRenderer); this.textRenderer = Assertions.checkNotNull(textRenderer);
textRendererHandler = textRendererLooper == null ? null : new Handler(textRendererLooper, this); textRendererHandler = textRendererLooper == null ? null : new Handler(textRendererLooper, this);
eia608Parser = new Eia608Parser(); eia608Parser = new Eia608Parser();
@ -92,30 +89,19 @@ public final class Eia608TrackRenderer extends TrackRenderer implements Callback
} }
@Override @Override
protected int doPrepare(long positionUs) { protected boolean handlesTrack(TrackInfo trackInfo) {
boolean sourcePrepared = source.prepare(positionUs); return eia608Parser.canParse(trackInfo.mimeType);
if (!sourcePrepared) {
return TrackRenderer.STATE_UNPREPARED;
}
int trackCount = source.getTrackCount();
for (int i = 0; i < trackCount; i++) {
if (eia608Parser.canParse(source.getTrackInfo(i).mimeType)) {
trackIndex = i;
return TrackRenderer.STATE_PREPARED;
}
}
return TrackRenderer.STATE_IGNORE;
} }
@Override @Override
protected void onEnabled(long positionUs, boolean joining) { protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
source.enable(trackIndex, positionUs); super.onEnabled(positionUs, joining);
seekToInternal(); seekToInternal();
} }
@Override @Override
protected void seekTo(long positionUs) throws ExoPlaybackException { protected void seekTo(long positionUs) throws ExoPlaybackException {
source.seekToUs(positionUs); super.seekTo(positionUs);
seekToInternal(); seekToInternal();
} }
@ -130,15 +116,14 @@ public final class Eia608TrackRenderer extends TrackRenderer implements Callback
@Override @Override
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
source.continueBuffering(trackIndex, positionUs); continueBufferingSource(positionUs);
if (isSamplePending()) { if (isSamplePending()) {
maybeParsePendingSample(positionUs); maybeParsePendingSample(positionUs);
} }
int result = inputStreamEnded ? SampleSource.END_OF_STREAM : SampleSource.SAMPLE_READ; int result = inputStreamEnded ? SampleSource.END_OF_STREAM : SampleSource.SAMPLE_READ;
while (!isSamplePending() && result == SampleSource.SAMPLE_READ) { while (!isSamplePending() && result == SampleSource.SAMPLE_READ) {
result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false); result = readSource(positionUs, formatHolder, sampleHolder, false);
if (result == SampleSource.SAMPLE_READ) { if (result == SampleSource.SAMPLE_READ) {
maybeParsePendingSample(positionUs); maybeParsePendingSample(positionUs);
} else if (result == SampleSource.END_OF_STREAM) { } else if (result == SampleSource.END_OF_STREAM) {
@ -161,25 +146,6 @@ public final class Eia608TrackRenderer extends TrackRenderer implements Callback
} }
} }
@Override
protected void onDisabled() {
source.disable(trackIndex);
}
@Override
protected void maybeThrowError() throws ExoPlaybackException {
try {
source.maybeThrowError();
} catch (IOException e) {
throw new ExoPlaybackException(e);
}
}
@Override
protected long getDurationUs() {
return source.getTrackInfo(trackIndex).durationUs;
}
@Override @Override
protected long getBufferedPositionUs() { protected long getBufferedPositionUs() {
return TrackRenderer.END_OF_TRACK_US; return TrackRenderer.END_OF_TRACK_US;