Commit 13f4a3e3 by Oliver Woodman

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.
parent 150b3cdb
......@@ -21,7 +21,8 @@ import com.google.android.exoplayer.MediaClock;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.MediaFormatHolder;
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.audio.AudioTrack;
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 java.io.IOException;
import java.nio.ByteBuffer;
import java.util.List;
......@@ -39,7 +39,8 @@ import java.util.List;
*
* @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.
......@@ -76,7 +77,6 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
*/
public static final int MSG_SET_VOLUME = 1;
private final SampleSourceReader source;
private final Handler eventHandler;
private final EventListener eventListener;
private final MediaFormatHolder formatHolder;
......@@ -86,7 +86,6 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
private InputBuffer inputBuffer;
private OutputBuffer outputBuffer;
private int trackIndex;
private long currentPositionUs;
private boolean allowPositionDiscontinuity;
private boolean inputStreamEnded;
......@@ -112,7 +111,7 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
*/
public LibopusAudioTrackRenderer(SampleSource source, Handler eventHandler,
EventListener eventListener) {
this.source = source.register();
super(source);
this.eventHandler = eventHandler;
this.eventListener = eventListener;
this.audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
......@@ -126,20 +125,15 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
}
@Override
protected int doPrepare(long positionUs) {
boolean sourcePrepared = source.prepare(positionUs);
if (!sourcePrepared) {
return TrackRenderer.STATE_UNPREPARED;
}
int trackCount = source.getTrackCount();
for (int i = 0; i < trackCount; i++) {
if (source.getTrackInfo(i).mimeType.equalsIgnoreCase(MimeTypes.AUDIO_OPUS)
|| source.getTrackInfo(i).mimeType.equalsIgnoreCase(MimeTypes.AUDIO_WEBM)) {
trackIndex = i;
return TrackRenderer.STATE_PREPARED;
}
}
return TrackRenderer.STATE_IGNORE;
protected boolean handlesTrack(TrackInfo trackInfo) {
return MimeTypes.AUDIO_OPUS.equalsIgnoreCase(trackInfo.mimeType)
|| MimeTypes.AUDIO_WEBM.equalsIgnoreCase(trackInfo.mimeType);
}
@Override
protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
super.onEnabled(positionUs, joining);
seekToInternal(positionUs);
}
@Override
......@@ -147,8 +141,8 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
if (outputStreamEnded) {
return;
}
sourceIsReady = source.continueBuffering(trackIndex, positionUs);
checkForDiscontinuity();
sourceIsReady = continueBufferingSource(positionUs);
checkForDiscontinuity(positionUs);
// Try and read a format if we don't have one already.
if (format == null && !readFormat(positionUs)) {
......@@ -189,7 +183,7 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
// Rendering loop.
try {
renderBuffer();
while (feedInputBuffer()) {}
while (feedInputBuffer(positionUs)) {}
} catch (AudioTrack.InitializationException e) {
notifyAudioTrackInitializationError(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) {
return false;
}
......@@ -261,8 +255,7 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
}
}
int result = source.readData(trackIndex, currentPositionUs, formatHolder,
inputBuffer.sampleHolder, false);
int result = readSource(positionUs, formatHolder, inputBuffer.sampleHolder, false);
if (result == SampleSource.NOTHING_READ) {
return false;
}
......@@ -291,11 +284,11 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
return true;
}
private void checkForDiscontinuity() {
private void checkForDiscontinuity(long positionUs) {
if (decoder == null) {
return;
}
int result = source.readData(trackIndex, currentPositionUs, formatHolder, null, true);
int result = readSource(positionUs, formatHolder, null, true);
if (result == SampleSource.DISCONTINUITY_READ) {
flushDecoder();
}
......@@ -320,11 +313,6 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
}
@Override
protected long getDurationUs() {
return source.getTrackInfo(trackIndex).durationUs;
}
@Override
public long getPositionUs() {
long newCurrentPositionUs = audioTrack.getCurrentPositionUs(isEnded());
if (newCurrentPositionUs != AudioTrack.CURRENT_POSITION_NOT_SET) {
......@@ -336,13 +324,8 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
}
@Override
protected long getBufferedPositionUs() {
return source.getBufferedPositionUs();
}
@Override
protected void seekTo(long positionUs) throws ExoPlaybackException {
source.seekToUs(positionUs);
super.seekTo(positionUs);
seekToInternal(positionUs);
}
......@@ -350,19 +333,12 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
audioTrack.reset();
currentPositionUs = positionUs;
allowPositionDiscontinuity = true;
source.seekToUs(positionUs);
inputStreamEnded = false;
outputStreamEnded = false;
sourceIsReady = false;
}
@Override
protected void onEnabled(long positionUs, boolean joining) {
source.enable(trackIndex, positionUs);
seekToInternal(positionUs);
}
@Override
protected void onStarted() {
audioTrack.play();
}
......@@ -373,38 +349,24 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
}
@Override
protected void onReleased() {
source.release();
}
@Override
protected void onDisabled() {
if (decoder != null) {
decoder.release();
decoder = null;
}
protected void onDisabled() throws ExoPlaybackException {
inputBuffer = null;
outputBuffer = null;
format = null;
audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
try {
if (decoder != null) {
decoder.release();
decoder = null;
}
audioTrack.release();
} finally {
inputBuffer = null;
outputBuffer = null;
format = null;
source.disable(trackIndex);
}
}
@Override
protected void maybeThrowError() throws ExoPlaybackException {
try {
source.maybeThrowError();
} catch (IOException e) {
throw new ExoPlaybackException(e);
super.onDisabled();
}
}
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) {
format = formatHolder.format;
audioTrack.reconfigure(format.getFrameworkMediaFormatV16());
......
......@@ -20,7 +20,8 @@ import com.google.android.exoplayer.ExoPlayer;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.MediaFormatHolder;
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.ext.vp9.VpxDecoderWrapper.InputBuffer;
import com.google.android.exoplayer.ext.vp9.VpxDecoderWrapper.OutputBuffer;
......@@ -32,12 +33,10 @@ import android.os.Handler;
import android.os.SystemClock;
import android.view.Surface;
import java.io.IOException;
/**
* 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.
......@@ -91,7 +90,6 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer {
public static final int MSG_SET_SURFACE = 1;
public static final int MSG_SET_VPX_SURFACE_VIEW = 2;
private final SampleSourceReader source;
private final boolean scaleToFit;
private final Handler eventHandler;
private final EventListener eventListener;
......@@ -110,7 +108,6 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer {
private VpxVideoSurfaceView vpxVideoSurfaceView;
private boolean outputRgb;
private int trackIndex;
private boolean inputStreamEnded;
private boolean outputStreamEnded;
private boolean sourceIsReady;
......@@ -141,7 +138,7 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer {
*/
public LibvpxVideoTrackRenderer(SampleSource source, boolean scaleToFit,
Handler eventHandler, EventListener eventListener, int maxDroppedFrameCountToNotify) {
this.source = source.register();
super(source);
this.scaleToFit = scaleToFit;
this.eventHandler = eventHandler;
this.eventListener = eventListener;
......@@ -152,20 +149,9 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer {
}
@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++) {
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;
protected boolean handlesTrack(TrackInfo trackInfo) {
return MimeTypes.VIDEO_VP9.equalsIgnoreCase(trackInfo.mimeType)
|| MimeTypes.VIDEO_WEBM.equalsIgnoreCase(trackInfo.mimeType);
}
@Override
......@@ -173,7 +159,7 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer {
if (outputStreamEnded) {
return;
}
sourceIsReady = source.continueBuffering(trackIndex, positionUs);
sourceIsReady = continueBufferingSource(positionUs);
checkForDiscontinuity(positionUs);
// 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);
if (result == SampleSource.NOTHING_READ) {
return false;
......@@ -334,7 +320,7 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer {
if (decoder == null) {
return;
}
int result = source.readData(trackIndex, positionUs, formatHolder, null, true);
int result = readSource(positionUs, formatHolder, null, true);
if (result == SampleSource.DISCONTINUITY_READ) {
flushDecoder();
}
......@@ -357,24 +343,14 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer {
}
@Override
protected long getDurationUs() {
return source.getTrackInfo(trackIndex).durationUs;
}
@Override
protected long getBufferedPositionUs() {
return source.getBufferedPositionUs();
}
@Override
protected void seekTo(long positionUs) throws ExoPlaybackException {
source.seekToUs(positionUs);
super.seekTo(positionUs);
seekToInternal();
}
@Override
protected void onEnabled(long positionUs, boolean joining) {
source.enable(trackIndex, positionUs);
protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
super.onEnabled(positionUs, joining);
seekToInternal();
}
......@@ -397,33 +373,22 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer {
}
@Override
protected void onReleased() {
source.release();
}
@Override
protected void onDisabled() {
if (decoder != null) {
decoder.release();
decoder = null;
}
protected void onDisabled() throws ExoPlaybackException {
inputBuffer = null;
outputBuffer = null;
format = null;
source.disable(trackIndex);
}
@Override
protected void maybeThrowError() throws ExoPlaybackException {
try {
source.maybeThrowError();
} catch (IOException e) {
throw new ExoPlaybackException(e);
if (decoder != null) {
decoder.release();
decoder = null;
}
} finally {
super.onDisabled();
}
}
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) {
format = formatHolder.format;
return true;
......
......@@ -157,12 +157,12 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
}
@Override
protected boolean handlesMimeType(String mimeType) {
return MimeTypes.isAudio(mimeType) && super.handlesMimeType(mimeType);
protected boolean handlesTrack(TrackInfo trackInfo) {
return MimeTypes.isAudio(trackInfo.mimeType);
}
@Override
protected void onEnabled(long positionUs, boolean joining) {
protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
super.onEnabled(positionUs, joining);
seekToInternal(positionUs);
}
......@@ -231,7 +231,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
}
@Override
protected void onDisabled() {
protected void onDisabled() throws ExoPlaybackException {
audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
try {
audioTrack.release();
......
......@@ -16,7 +16,6 @@
package com.google.android.exoplayer;
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.DrmSessionManager;
import com.google.android.exoplayer.util.Assertions;
......@@ -31,7 +30,6 @@ import android.media.MediaCrypto;
import android.os.Handler;
import android.os.SystemClock;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
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.
*/
@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.
......@@ -183,7 +181,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
private final DrmSessionManager drmSessionManager;
private final boolean playClearSamplesWithoutKeys;
private final SampleSourceReader source;
private final SampleHolder sampleHolder;
private final MediaFormatHolder formatHolder;
private final List<Long> decodeOnlyPresentationTimestamps;
......@@ -207,7 +204,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
private int codecReinitializationState;
private boolean codecHasQueuedBuffers;
private int trackIndex;
private int sourceState;
private boolean inputStreamEnded;
private boolean outputStreamEnded;
......@@ -229,8 +225,8 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
*/
public MediaCodecTrackRenderer(SampleSource source, DrmSessionManager drmSessionManager,
boolean playClearSamplesWithoutKeys, Handler eventHandler, EventListener eventListener) {
super(source);
Assertions.checkState(Util.SDK_INT >= 16);
this.source = source.register();
this.drmSessionManager = drmSessionManager;
this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys;
this.eventHandler = eventHandler;
......@@ -245,39 +241,8 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
}
@Override
protected int doPrepare(long positionUs) {
boolean sourcePrepared = source.prepare(positionUs);
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);
protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
super.onEnabled(positionUs, joining);
seekToInternal();
}
......@@ -400,7 +365,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
}
@Override
protected void onDisabled() {
protected void onDisabled() throws ExoPlaybackException {
format = null;
drmInitData = null;
try {
......@@ -412,7 +377,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
openedDrmSession = false;
}
} finally {
source.disable(trackIndex);
super.onDisabled();
}
}
}
......@@ -446,23 +411,8 @@ 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
protected void seekTo(long positionUs) throws ExoPlaybackException {
source.seekToUs(positionUs);
super.seekTo(positionUs);
seekToInternal();
}
......@@ -484,7 +434,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
@Override
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)
: SOURCE_STATE_NOT_READY;
checkForDiscontinuity(positionUs);
......@@ -506,7 +456,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
}
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) {
onInputFormatChanged(formatHolder);
}
......@@ -516,7 +466,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
if (codec == null) {
return;
}
int result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, true);
int result = readSource(positionUs, formatHolder, sampleHolder, true);
if (result == SampleSource.DISCONTINUITY_READ) {
flushCodec();
}
......@@ -597,7 +547,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
}
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) {
sourceState = SOURCE_STATE_READY_READ_MAY_FAIL;
}
......@@ -775,15 +725,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
}
@Override
protected void maybeThrowError() throws ExoPlaybackException {
try {
source.maybeThrowError();
} catch (IOException e) {
throw new ExoPlaybackException(e);
}
}
@Override
protected boolean isEnded() {
return outputStreamEnded;
}
......@@ -950,11 +891,8 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
* incorrectly on the host device. False otherwise.
*/
private static boolean codecNeedsEndOfStreamWorkaround(String name) {
return Util.SDK_INT <= 17
&& "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
return Util.SDK_INT <= 17 && "ht7s3".equals(Util.DEVICE) // Tesco HUDL
&& "OMX.rk.video_decoder.avc".equals(name);
}
}
......@@ -256,12 +256,12 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
}
@Override
protected boolean handlesMimeType(String mimeType) {
return MimeTypes.isVideo(mimeType) && super.handlesMimeType(mimeType);
protected boolean handlesTrack(TrackInfo trackInfo) {
return MimeTypes.isVideo(trackInfo.mimeType);
}
@Override
protected void onEnabled(long positionUs, boolean joining) {
protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
super.onEnabled(positionUs, joining);
renderedFirstFrame = false;
if (joining && allowedJoiningTimeUs > 0) {
......@@ -314,7 +314,7 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
}
@Override
public void onDisabled() {
protected void onDisabled() throws ExoPlaybackException {
currentWidth = -1;
currentHeight = -1;
currentPixelWidthHeightRatio = -1;
......
/*
* 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);
}
}
......@@ -110,11 +110,11 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
* @throws ExoPlaybackException If an error occurs.
*/
/* package */ final int prepare(long positionUs) throws ExoPlaybackException {
Assertions.checkState(state == TrackRenderer.STATE_UNPREPARED);
Assertions.checkState(state == STATE_UNPREPARED);
state = doPrepare(positionUs);
Assertions.checkState(state == TrackRenderer.STATE_UNPREPARED ||
state == TrackRenderer.STATE_PREPARED ||
state == TrackRenderer.STATE_IGNORE);
Assertions.checkState(state == STATE_UNPREPARED ||
state == STATE_PREPARED ||
state == STATE_IGNORE);
return state;
}
......@@ -143,8 +143,8 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
* @throws ExoPlaybackException If an error occurs.
*/
/* package */ final void enable(long positionUs, boolean joining) throws ExoPlaybackException {
Assertions.checkState(state == TrackRenderer.STATE_PREPARED);
state = TrackRenderer.STATE_ENABLED;
Assertions.checkState(state == STATE_PREPARED);
state = STATE_ENABLED;
onEnabled(positionUs, joining);
}
......@@ -170,8 +170,8 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
* @throws ExoPlaybackException If an error occurs.
*/
/* package */ final void start() throws ExoPlaybackException {
Assertions.checkState(state == TrackRenderer.STATE_ENABLED);
state = TrackRenderer.STATE_STARTED;
Assertions.checkState(state == STATE_ENABLED);
state = STATE_STARTED;
onStarted();
}
......@@ -192,8 +192,8 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
* @throws ExoPlaybackException If an error occurs.
*/
/* package */ final void stop() throws ExoPlaybackException {
Assertions.checkState(state == TrackRenderer.STATE_STARTED);
state = TrackRenderer.STATE_ENABLED;
Assertions.checkState(state == STATE_STARTED);
state = STATE_ENABLED;
onStopped();
}
......@@ -214,8 +214,8 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
* @throws ExoPlaybackException If an error occurs.
*/
/* package */ final void disable() throws ExoPlaybackException {
Assertions.checkState(state == TrackRenderer.STATE_ENABLED);
state = TrackRenderer.STATE_PREPARED;
Assertions.checkState(state == STATE_ENABLED);
state = STATE_PREPARED;
onDisabled();
}
......@@ -236,10 +236,10 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
* @throws ExoPlaybackException If an error occurs.
*/
/* package */ final void release() throws ExoPlaybackException {
Assertions.checkState(state != TrackRenderer.STATE_ENABLED
&& state != TrackRenderer.STATE_STARTED
&& state != TrackRenderer.STATE_RELEASED);
state = TrackRenderer.STATE_RELEASED;
Assertions.checkState(state != STATE_ENABLED
&& state != STATE_STARTED
&& state != STATE_RELEASED);
state = STATE_RELEASED;
onReleased();
}
......
......@@ -58,7 +58,7 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load
private static final int STATE_PREPARED = 2;
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 LoadControl loadControl;
......
......@@ -93,7 +93,7 @@ public final class ExtractorSampleSource implements SampleSource, SampleSourceRe
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 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
......@@ -326,10 +326,14 @@ public final class ExtractorSampleSource implements SampleSource, SampleSourceRe
enabledTrackCount++;
trackEnabledStates[track] = true;
pendingMediaFormat[track] = true;
pendingDiscontinuities[track] = false;
if (enabledTrackCount == 1) {
seekToUs(positionUs);
// Treat all enables in non-seekable media as being from t=0.
positionUs = !seekMap.isSeekable() ? 0 : positionUs;
downstreamPositionUs = positionUs;
lastSeekPositionUs = positionUs;
restartFrom(positionUs);
}
pendingDiscontinuities[track] = false;
}
@Override
......@@ -431,10 +435,8 @@ public final class ExtractorSampleSource implements SampleSource, SampleSourceRe
public void seekToUs(long positionUs) {
Assertions.checkState(prepared);
Assertions.checkState(enabledTrackCount > 0);
if (!seekMap.isSeekable()) {
// Treat all seeks into non-seekable media as seeks to the start.
positionUs = 0;
}
// Treat all seeks into non-seekable media as being to t=0.
positionUs = !seekMap.isSeekable() ? 0 : positionUs;
long currentPositionUs = isPendingReset() ? pendingResetPositionUs : downstreamPositionUs;
downstreamPositionUs = positionUs;
......
......@@ -52,7 +52,7 @@ public final class HlsSampleSource implements SampleSource, SampleSourceReader,
*/
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 LinkedList<HlsExtractorWrapper> extractors;
......@@ -182,15 +182,17 @@ public final class HlsSampleSource implements SampleSource, SampleSourceReader,
enabledTrackCount++;
trackEnabledStates[track] = true;
downstreamMediaFormats[track] = null;
pendingDiscontinuities[track] = false;
downstreamFormat = null;
if (!loadControlRegistered) {
loadControl.register(this, bufferSizeContribution);
loadControlRegistered = true;
}
if (enabledTrackCount == 1) {
seekToUs(positionUs);
downstreamPositionUs = positionUs;
lastSeekPositionUs = positionUs;
restartFrom(positionUs);
}
pendingDiscontinuities[track] = false;
}
@Override
......
......@@ -19,7 +19,8 @@ import com.google.android.exoplayer.ExoPlaybackException;
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.SampleSourceTrackRenderer;
import com.google.android.exoplayer.TrackInfo;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.util.Assertions;
......@@ -35,7 +36,7 @@ import java.io.IOException;
*
* @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.
......@@ -55,16 +56,13 @@ public final class MetadataTrackRenderer<T> extends TrackRenderer implements Cal
private static final int MSG_INVOKE_RENDERER = 0;
private final SampleSourceReader source;
private final MetadataParser<T> metadataParser;
private final MetadataRenderer<T> metadataRenderer;
private final Handler metadataHandler;
private final MediaFormatHolder formatHolder;
private final SampleHolder sampleHolder;
private int trackIndex;
private boolean inputStreamEnded;
private long pendingMetadataTimestamp;
private T pendingMetadata;
......@@ -80,7 +78,7 @@ public final class MetadataTrackRenderer<T> extends TrackRenderer implements Cal
*/
public MetadataTrackRenderer(SampleSource source, MetadataParser<T> metadataParser,
MetadataRenderer<T> metadataRenderer, Looper metadataRendererLooper) {
this.source = source.register();
super(source);
this.metadataParser = Assertions.checkNotNull(metadataParser);
this.metadataRenderer = Assertions.checkNotNull(metadataRenderer);
this.metadataHandler = metadataRendererLooper == null ? null
......@@ -90,30 +88,19 @@ public final class MetadataTrackRenderer<T> extends TrackRenderer implements Cal
}
@Override
protected int doPrepare(long positionUs) {
boolean sourcePrepared = source.prepare(positionUs);
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;
protected boolean handlesTrack(TrackInfo trackInfo) {
return metadataParser.canParse(trackInfo.mimeType);
}
@Override
protected void onEnabled(long positionUs, boolean joining) {
source.enable(trackIndex, positionUs);
protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
super.onEnabled(positionUs, joining);
seekToInternal();
}
@Override
protected void seekTo(long positionUs) throws ExoPlaybackException {
source.seekToUs(positionUs);
super.seekTo(positionUs);
seekToInternal();
}
......@@ -123,23 +110,21 @@ public final class MetadataTrackRenderer<T> extends TrackRenderer implements Cal
}
@Override
protected void doSomeWork(long positionUs, long elapsedRealtimeUs)
throws ExoPlaybackException {
source.continueBuffering(trackIndex, positionUs);
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
continueBufferingSource(positionUs);
if (!inputStreamEnded && pendingMetadata == null) {
int result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false);
if (result == SampleSource.SAMPLE_READ) {
pendingMetadataTimestamp = sampleHolder.timeUs;
try {
pendingMetadata = metadataParser.parse(sampleHolder.data.array(), sampleHolder.size);
} catch (IOException e) {
throw new ExoPlaybackException(e);
}
sampleHolder.data.clear();
} else if (result == SampleSource.END_OF_STREAM) {
inputStreamEnded = true;
int result = readSource(positionUs, formatHolder, sampleHolder, false);
if (result == SampleSource.SAMPLE_READ) {
pendingMetadataTimestamp = sampleHolder.timeUs;
try {
pendingMetadata = metadataParser.parse(sampleHolder.data.array(), sampleHolder.size);
} catch (IOException e) {
throw new ExoPlaybackException(e);
}
sampleHolder.data.clear();
} else if (result == SampleSource.END_OF_STREAM) {
inputStreamEnded = true;
}
}
if (pendingMetadata != null && pendingMetadataTimestamp <= positionUs) {
......@@ -149,23 +134,9 @@ public final class MetadataTrackRenderer<T> extends TrackRenderer implements Cal
}
@Override
protected void maybeThrowError() throws ExoPlaybackException {
try {
source.maybeThrowError();
} catch (IOException e) {
throw new ExoPlaybackException(e);
}
}
@Override
protected void onDisabled() {
protected void onDisabled() throws ExoPlaybackException {
pendingMetadata = null;
source.disable(trackIndex);
}
@Override
protected long getDurationUs() {
return source.getTrackInfo(trackIndex).durationUs;
super.onDisabled();
}
@Override
......
......@@ -19,7 +19,8 @@ import com.google.android.exoplayer.ExoPlaybackException;
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.SampleSourceTrackRenderer;
import com.google.android.exoplayer.TrackInfo;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.util.Assertions;
......@@ -58,7 +59,7 @@ import java.util.List;
* {@link SubtitleParser#canParse(String)} will be used.
*/
@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;
......@@ -104,15 +105,11 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback {
private final Handler textRendererHandler;
private final TextRenderer textRenderer;
private final SampleSourceReader source;
private final MediaFormatHolder formatHolder;
private final SubtitleParser[] subtitleParsers;
private int parserIndex;
private int trackIndex;
private boolean inputStreamEnded;
private Subtitle subtitle;
private Subtitle nextSubtitle;
private SubtitleParserHelper parserHelper;
......@@ -132,7 +129,7 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback {
*/
public TextTrackRenderer(SampleSource source, TextRenderer textRenderer,
Looper textRendererLooper, SubtitleParser... subtitleParsers) {
this.source = source.register();
super(source);
this.textRenderer = Assertions.checkNotNull(textRenderer);
this.textRendererHandler = textRendererLooper == null ? null
: new Handler(textRendererLooper, this);
......@@ -153,27 +150,29 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback {
}
@Override
protected int doPrepare(long positionUs) {
boolean sourcePrepared = source.prepare(positionUs);
if (!sourcePrepared) {
return TrackRenderer.STATE_UNPREPARED;
protected boolean handlesTrack(TrackInfo trackInfo) {
for (int i = 0; i < subtitleParsers.length; i++) {
if (subtitleParsers[i].canParse(trackInfo.mimeType)) {
return true;
}
}
int trackCount = source.getTrackCount();
return false;
}
@Override
protected void onTrackSelected(TrackInfo trackInfo) {
for (int i = 0; i < subtitleParsers.length; i++) {
for (int j = 0; j < trackCount; j++) {
if (subtitleParsers[i].canParse(source.getTrackInfo(j).mimeType)) {
parserIndex = i;
trackIndex = j;
return TrackRenderer.STATE_PREPARED;
}
if (subtitleParsers[i].canParse(trackInfo.mimeType)) {
parserIndex = i;
return;
}
}
return TrackRenderer.STATE_IGNORE;
throw new IllegalStateException("Invalid track selected");
}
@Override
protected void onEnabled(long positionUs, boolean joining) {
source.enable(trackIndex, positionUs);
protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
super.onEnabled(positionUs, joining);
parserThread = new HandlerThread("textParser");
parserThread.start();
parserHelper = new SubtitleParserHelper(parserThread.getLooper(), subtitleParsers[parserIndex]);
......@@ -181,8 +180,8 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback {
}
@Override
protected void seekTo(long positionUs) {
source.seekToUs(positionUs);
protected void seekTo(long positionUs) throws ExoPlaybackException {
super.seekTo(positionUs);
seekToInternal();
}
......@@ -196,8 +195,7 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback {
@Override
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
source.continueBuffering(trackIndex, positionUs);
continueBufferingSource(positionUs);
if (nextSubtitle == null) {
try {
nextSubtitle = parserHelper.getAndClearResult();
......@@ -241,7 +239,7 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback {
// Try and read the next subtitle from the source.
SampleHolder sampleHolder = parserHelper.getSampleHolder();
sampleHolder.clearData();
int result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false);
int result = readSource(positionUs, formatHolder, sampleHolder, false);
if (result == SampleSource.SAMPLE_READ) {
parserHelper.startParseOperation();
} else if (result == SampleSource.END_OF_STREAM) {
......@@ -251,33 +249,14 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback {
}
@Override
protected void onDisabled() {
protected void onDisabled() throws ExoPlaybackException {
subtitle = null;
nextSubtitle = null;
parserThread.quit();
parserThread = null;
parserHelper = null;
clearTextRenderer();
source.disable(trackIndex);
}
@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;
super.onDisabled();
}
@Override
......
......@@ -20,7 +20,8 @@ import com.google.android.exoplayer.ExoPlaybackException;
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.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;
......@@ -32,14 +33,13 @@ import android.os.Handler.Callback;
import android.os.Looper;
import android.os.Message;
import java.io.IOException;
import java.util.Collections;
import java.util.TreeSet;
/**
* 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;
......@@ -53,7 +53,6 @@ public final class Eia608TrackRenderer extends TrackRenderer implements Callback
// The maximum duration that captions are parsed ahead of the current position.
private static final int MAX_SAMPLE_READAHEAD_US = 5000000;
private final SampleSourceReader source;
private final Eia608Parser eia608Parser;
private final TextRenderer textRenderer;
private final Handler textRendererHandler;
......@@ -62,9 +61,7 @@ public final class Eia608TrackRenderer extends TrackRenderer implements Callback
private final StringBuilder captionStringBuilder;
private final TreeSet<ClosedCaptionList> pendingCaptionLists;
private int trackIndex;
private boolean inputStreamEnded;
private int captionMode;
private int captionRowCount;
private String caption;
......@@ -81,7 +78,7 @@ public final class Eia608TrackRenderer extends TrackRenderer implements Callback
*/
public Eia608TrackRenderer(SampleSource source, TextRenderer textRenderer,
Looper textRendererLooper) {
this.source = source.register();
super(source);
this.textRenderer = Assertions.checkNotNull(textRenderer);
textRendererHandler = textRendererLooper == null ? null : new Handler(textRendererLooper, this);
eia608Parser = new Eia608Parser();
......@@ -92,30 +89,19 @@ public final class Eia608TrackRenderer extends TrackRenderer implements Callback
}
@Override
protected int doPrepare(long positionUs) {
boolean sourcePrepared = source.prepare(positionUs);
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;
protected boolean handlesTrack(TrackInfo trackInfo) {
return eia608Parser.canParse(trackInfo.mimeType);
}
@Override
protected void onEnabled(long positionUs, boolean joining) {
source.enable(trackIndex, positionUs);
protected void onEnabled(long positionUs, boolean joining) throws ExoPlaybackException {
super.onEnabled(positionUs, joining);
seekToInternal();
}
@Override
protected void seekTo(long positionUs) throws ExoPlaybackException {
source.seekToUs(positionUs);
super.seekTo(positionUs);
seekToInternal();
}
......@@ -130,15 +116,14 @@ public final class Eia608TrackRenderer extends TrackRenderer implements Callback
@Override
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
source.continueBuffering(trackIndex, positionUs);
continueBufferingSource(positionUs);
if (isSamplePending()) {
maybeParsePendingSample(positionUs);
}
int result = inputStreamEnded ? SampleSource.END_OF_STREAM : 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) {
maybeParsePendingSample(positionUs);
} else if (result == SampleSource.END_OF_STREAM) {
......@@ -162,25 +147,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
protected long getBufferedPositionUs() {
return TrackRenderer.END_OF_TRACK_US;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment