mirror of
https://github.com/androidx/media.git
synced 2025-05-05 06:30:24 +08:00
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:
parent
150b3cdb19
commit
13f4a3e3bd
@ -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());
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user